add auto-generated UFCS correctness checks
This commit is contained in:
parent
efd8743c9e
commit
911ce077a5
|
@ -75,14 +75,14 @@ jobs:
|
||||||
- name: Linux Tests
|
- name: Linux Tests
|
||||||
if: contains(matrix.os, 'ubuntu')
|
if: contains(matrix.os, 'ubuntu')
|
||||||
run: |
|
run: |
|
||||||
./run_tests.sh
|
./run_tests.sh --extra
|
||||||
working-directory: tests
|
working-directory: tests
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Windows and MacOS Tests
|
- name: Windows and MacOS Tests
|
||||||
if: contains(matrix.os, 'windows') || contains(matrix.os, 'macos')
|
if: contains(matrix.os, 'windows') || contains(matrix.os, 'macos')
|
||||||
run: |
|
run: |
|
||||||
./run_tests.sh
|
./run_tests.sh --extra
|
||||||
working-directory: tests
|
working-directory: tests
|
||||||
shell: bash
|
shell: bash
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|
|
@ -34,7 +34,7 @@ echo "STAT:rough build time=${build_time}s"
|
||||||
echo "STAT:"
|
echo "STAT:"
|
||||||
|
|
||||||
cd tests
|
cd tests
|
||||||
./run_tests.sh --time-server
|
./run_tests.sh --time-server --extra
|
||||||
|
|
||||||
echo "STAT:DCD run_tests.sh $(grep -F 'Elapsed (wall clock) time' stderr.txt)"
|
echo "STAT:DCD run_tests.sh $(grep -F 'Elapsed (wall clock) time' stderr.txt)"
|
||||||
echo "STAT:DCD run_tests.sh $(grep -F 'Maximum resident set size (kbytes)' stderr.txt)"
|
echo "STAT:DCD run_tests.sh $(grep -F 'Maximum resident set size (kbytes)' stderr.txt)"
|
||||||
|
@ -49,7 +49,7 @@ rm -rf .dub bin/dcd-server
|
||||||
dub build --build=profile-gc --config=server --compiler=dmd 2>&1 || echo "DCD BUILD FAILED"
|
dub build --build=profile-gc --config=server --compiler=dmd 2>&1 || echo "DCD BUILD FAILED"
|
||||||
|
|
||||||
cd tests
|
cd tests
|
||||||
./run_tests.sh
|
./run_tests.sh --extra
|
||||||
|
|
||||||
echo "STAT:top 5 GC sources in server:"
|
echo "STAT:top 5 GC sources in server:"
|
||||||
if [ ! -f "profilegc.log" ]; then
|
if [ ! -f "profilegc.log" ]; then
|
||||||
|
|
2
makefile
2
makefile
|
@ -137,7 +137,7 @@ ldcserver: githash
|
||||||
${LDC} $(LDC_SERVER_FLAGS) ${SERVER_SRC} -oq -of=bin/dcd-server
|
${LDC} $(LDC_SERVER_FLAGS) ${SERVER_SRC} -oq -of=bin/dcd-server
|
||||||
|
|
||||||
test: debugserver dmdclient
|
test: debugserver dmdclient
|
||||||
cd tests && ./run_tests.sh
|
cd tests && ./run_tests.sh --extra
|
||||||
|
|
||||||
release:
|
release:
|
||||||
./release.sh
|
./release.sh
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
proc_test.d
|
|
@ -0,0 +1,266 @@
|
||||||
|
// This generates functions with all specified test types as first argument +
|
||||||
|
// variables for each specified test type.
|
||||||
|
// Then it calls all functions with every type to see which ones are accepted by
|
||||||
|
// the compiler, to automatically stay up-to-date.
|
||||||
|
|
||||||
|
import std;
|
||||||
|
import fs = std.file;
|
||||||
|
|
||||||
|
string[] testTypes = [
|
||||||
|
"bool",
|
||||||
|
"byte",
|
||||||
|
"ubyte",
|
||||||
|
"short",
|
||||||
|
"ushort",
|
||||||
|
"int",
|
||||||
|
"uint",
|
||||||
|
"long",
|
||||||
|
"ulong",
|
||||||
|
"char",
|
||||||
|
"wchar",
|
||||||
|
"dchar",
|
||||||
|
"float",
|
||||||
|
"double",
|
||||||
|
"real",
|
||||||
|
"BasicStruct",
|
||||||
|
"AliasThisInt",
|
||||||
|
];
|
||||||
|
// index here must map onto varTypePermutations index
|
||||||
|
string[][] funcTypePermutations = [
|
||||||
|
// TODO: check for const/inout/immutable/shared in UFCS checks
|
||||||
|
[
|
||||||
|
"%s",
|
||||||
|
// "const(%s)",
|
||||||
|
"ref %s",
|
||||||
|
// "ref const(%s)"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"%s*",
|
||||||
|
// "const(%s)*",
|
||||||
|
// "const(%s*)",
|
||||||
|
"ref %s*",
|
||||||
|
// "ref const(%s)*",
|
||||||
|
// "ref const(%s*)"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"%s[]",
|
||||||
|
// "const(%s)[]",
|
||||||
|
// "const(%s[])",
|
||||||
|
"ref %s[]",
|
||||||
|
// "ref const(%s)[]",
|
||||||
|
// "ref const(%s[])"
|
||||||
|
]
|
||||||
|
];
|
||||||
|
string[][] varTypePermutations = [
|
||||||
|
[
|
||||||
|
"%s",
|
||||||
|
// "const(%s)"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"%s*",
|
||||||
|
// "const(%s)*",
|
||||||
|
// "const(%s*)"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"%s[]",
|
||||||
|
// "const(%s)[]",
|
||||||
|
// "const(%s[])"
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
string preamble = `
|
||||||
|
struct BasicStruct { int member1; string member2; }
|
||||||
|
struct AliasThisInt { int member1; string member2; alias member1 this; }
|
||||||
|
|
||||||
|
`;
|
||||||
|
|
||||||
|
int main(string[] args)
|
||||||
|
{
|
||||||
|
string functionsCode;
|
||||||
|
string varsCode;
|
||||||
|
string callCode;
|
||||||
|
|
||||||
|
string[] allFunctions;
|
||||||
|
string[string] funcLookup;
|
||||||
|
string[string] varLookup;
|
||||||
|
|
||||||
|
foreach (ti, type; testTypes)
|
||||||
|
{
|
||||||
|
foreach (pi, perms; funcTypePermutations)
|
||||||
|
{
|
||||||
|
foreach (i, perm; perms)
|
||||||
|
{
|
||||||
|
string resolved = format(perm, type);
|
||||||
|
string id = getID(ti, pi, i);
|
||||||
|
allFunctions ~= ("func_" ~ id);
|
||||||
|
functionsCode ~= "void func_" ~ id ~ "(" ~ resolved ~ " arg) {}\n";
|
||||||
|
functionsCode ~= resolved ~ " make_" ~ id ~ "() { static " ~ resolved
|
||||||
|
.chompPrefix("ref ") ~ " x; return x; }\n";
|
||||||
|
funcLookup["func_" ~ id] = resolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (pi, perms; varTypePermutations)
|
||||||
|
{
|
||||||
|
foreach (i, perm; perms)
|
||||||
|
{
|
||||||
|
string resolved = format(perm, type);
|
||||||
|
string id = getID(ti, pi, i);
|
||||||
|
varsCode ~= resolved ~ " var_" ~ id ~ " = make_" ~ id ~ "();\n";
|
||||||
|
varLookup["var_" ~ id] = resolved;
|
||||||
|
foreach (cti, subType; testTypes)
|
||||||
|
foreach (ci, subPerms; funcTypePermutations)
|
||||||
|
foreach (fi, subPerm; subPerms)
|
||||||
|
{
|
||||||
|
callCode ~= "var_" ~ id ~ ".func_" ~ getID(cti, ci, fi) ~ "();\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allFunctions.sort!"a<b";
|
||||||
|
|
||||||
|
string code = preamble
|
||||||
|
~ functionsCode
|
||||||
|
~ "\nvoid main() {\n"
|
||||||
|
~ varsCode
|
||||||
|
~ callCode
|
||||||
|
~ "}\n";
|
||||||
|
string[] lines = code.splitLines;
|
||||||
|
|
||||||
|
fs.write("proc_test.d", code);
|
||||||
|
|
||||||
|
auto output = executeShell("$DC -verrors=0 -c proc_test.d").output;
|
||||||
|
|
||||||
|
size_t numErrors = 0;
|
||||||
|
|
||||||
|
string[][string] variableIncompatibilities;
|
||||||
|
|
||||||
|
foreach (err; output.lineSplitter)
|
||||||
|
{
|
||||||
|
if (!err.startsWith("proc_test.d("))
|
||||||
|
continue;
|
||||||
|
err = err["proc_test.d(".length .. $];
|
||||||
|
auto lineNo = err.parse!int;
|
||||||
|
if (!err.startsWith("): Error: "))
|
||||||
|
continue;
|
||||||
|
err = err["): Error: ".length .. $];
|
||||||
|
string line = lines[lineNo - 1];
|
||||||
|
enforce(line.endsWith("();"), "Unexpected error in line " ~ lineNo.to!string);
|
||||||
|
line = line[0 .. $ - 3];
|
||||||
|
string varName = line.findSplit(".")[0];
|
||||||
|
string funcName = line.findSplit(".")[2];
|
||||||
|
// writeln("variable type ", varLookup[varName], " can't call ", funcLookup[funcName]);
|
||||||
|
variableIncompatibilities[varName] ~= funcName;
|
||||||
|
numErrors++;
|
||||||
|
}
|
||||||
|
|
||||||
|
enforce(numErrors > 1_000, "compiler didn't error as expected, need to adjust tests!");
|
||||||
|
|
||||||
|
writeln("Total incompatible type combinations: ", numErrors);
|
||||||
|
|
||||||
|
string[][string] wrongDCDCompletions;
|
||||||
|
|
||||||
|
foreach (varName; varLookup.byKey)
|
||||||
|
{
|
||||||
|
string input = code[0 .. $ - 2]
|
||||||
|
~ "\n"
|
||||||
|
~ varName ~ ".func_";
|
||||||
|
|
||||||
|
string[] dcdClient = ["../../../bin/dcd-client"];
|
||||||
|
if (args[1].length)
|
||||||
|
dcdClient ~= args[1];
|
||||||
|
|
||||||
|
auto proc = pipeProcess(dcdClient ~ ["-c" ~ to!string(input.length)]);
|
||||||
|
proc.stdin.rawWrite(input);
|
||||||
|
proc.stdin.rawWrite("\n}\n");
|
||||||
|
proc.stdin.close();
|
||||||
|
|
||||||
|
string[] dcdResult;
|
||||||
|
|
||||||
|
size_t i = 0;
|
||||||
|
foreach (line; proc.stdout.byLineCopy)
|
||||||
|
{
|
||||||
|
if (i++ == 0)
|
||||||
|
{
|
||||||
|
enforce(line == "identifiers");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto parts = line.split("\t");
|
||||||
|
if (parts[1] != "F")
|
||||||
|
continue;
|
||||||
|
dcdResult ~= parts[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
enforce(i > 0, "every variable must auto-complete something! Missing completion for var " ~ varName
|
||||||
|
~ " of type " ~ varLookup[varName] ~ generateEmptyResponseReproductionCode(
|
||||||
|
varLookup[varName]));
|
||||||
|
enforce(dcdResult.length > 0, "Wrongly no UFCS completion for var " ~ varName
|
||||||
|
~ " of type " ~ varLookup[varName] ~ generateEmptyResponseReproductionCode(
|
||||||
|
varLookup[varName]));
|
||||||
|
|
||||||
|
dcdResult.sort!"a<b";
|
||||||
|
|
||||||
|
string[] minusExpect = variableIncompatibilities[varName];
|
||||||
|
minusExpect.sort!"a<b";
|
||||||
|
variableIncompatibilities[varName] = minusExpect = minusExpect.uniq.array;
|
||||||
|
|
||||||
|
auto neededFunctions = setDifference(allFunctions, minusExpect);
|
||||||
|
|
||||||
|
auto unneccessaryFunctions = setDifference(dcdResult, neededFunctions);
|
||||||
|
auto missingFunctions = setDifference(neededFunctions, setIntersection(
|
||||||
|
dcdResult, neededFunctions));
|
||||||
|
|
||||||
|
string[] diff =
|
||||||
|
unneccessaryFunctions.map!(ln => '+' ~ ln)
|
||||||
|
.chain(missingFunctions.map!(ln => '-' ~ ln))
|
||||||
|
.array;
|
||||||
|
|
||||||
|
// writeln(varLookup[varName], " -> ", dcdResult);
|
||||||
|
if (diff.length)
|
||||||
|
wrongDCDCompletions[varName] = diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (varName, wrongTypes; wrongDCDCompletions)
|
||||||
|
{
|
||||||
|
writeln("Incorrect results for ", varLookup[varName], ":");
|
||||||
|
wrongTypes.sort!"a<b";
|
||||||
|
char prevChar = ' ';
|
||||||
|
foreach (wrongType; wrongTypes)
|
||||||
|
{
|
||||||
|
if (prevChar != wrongType[0])
|
||||||
|
{
|
||||||
|
prevChar = wrongType[0];
|
||||||
|
if (prevChar == '+')
|
||||||
|
writeln("\tDCD errornously matched these argument types:");
|
||||||
|
else if (prevChar == '-')
|
||||||
|
writeln("\tDCD errornously did not match these argument types:");
|
||||||
|
}
|
||||||
|
|
||||||
|
wrongType = wrongType[1 .. $];
|
||||||
|
writeln("\t\t", funcLookup[wrongType]);
|
||||||
|
}
|
||||||
|
writeln();
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrongDCDCompletions.length ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
string getID(size_t ti, size_t pi, size_t i)
|
||||||
|
{
|
||||||
|
return format!"%s_%s_%s"(ti, pi, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
string generateEmptyResponseReproductionCode(string type)
|
||||||
|
{
|
||||||
|
string prefix =
|
||||||
|
"void ufcsFunc(" ~ type ~ " v) {}\n\n"
|
||||||
|
~ "void main() {\n"
|
||||||
|
~ " " ~ type ~ " myVar;\n"
|
||||||
|
~ " myVar.ufcs";
|
||||||
|
return "\n\nReproduction code:\n```d\n"
|
||||||
|
~ prefix ~ ";\n"
|
||||||
|
~ "}\n"
|
||||||
|
~ "```\n\n"
|
||||||
|
~ "call `dcd-client -c" ~ prefix.length.to!string ~ "`";
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ -z "${DC:-}" ]; then
|
||||||
|
DC=dmd
|
||||||
|
fi
|
||||||
|
|
||||||
|
DC="$DC" "$DC" -run generate_tests.d "$1"
|
|
@ -7,6 +7,7 @@ IMPORTS=$(pwd)/imports
|
||||||
export IMPORTS
|
export IMPORTS
|
||||||
SOCKETMODES="unix tcp"
|
SOCKETMODES="unix tcp"
|
||||||
TIME_SERVER=0
|
TIME_SERVER=0
|
||||||
|
EXTRA_TESTCASES=
|
||||||
|
|
||||||
# `--arguments` must come before test dirs!
|
# `--arguments` must come before test dirs!
|
||||||
while (( "$#" )); do
|
while (( "$#" )); do
|
||||||
|
@ -22,6 +23,9 @@ while (( "$#" )); do
|
||||||
# socket mode can still be overriden with `--tcp-only`
|
# socket mode can still be overriden with `--tcp-only`
|
||||||
TIME_SERVER=1
|
TIME_SERVER=1
|
||||||
SOCKETMODES="unix"
|
SOCKETMODES="unix"
|
||||||
|
elif [[ "$1" == "--extra" ]]; then
|
||||||
|
# also include tests in the "extra" directory that long to complete
|
||||||
|
EXTRA_TESTCASES="extra/*/"
|
||||||
elif [[ "$1" =~ ^-- ]]; then
|
elif [[ "$1" =~ ^-- ]]; then
|
||||||
echo "Unrecognized test argument: $1"
|
echo "Unrecognized test argument: $1"
|
||||||
exit 1
|
exit 1
|
||||||
|
@ -108,7 +112,7 @@ for socket in $SOCKETMODES; do # supported: unix tcp
|
||||||
done
|
done
|
||||||
|
|
||||||
# Run tests
|
# Run tests
|
||||||
for testCase in $TESTCASES; do
|
for testCase in $TESTCASES $EXTRA_TESTCASES; do
|
||||||
cd $testCase
|
cd $testCase
|
||||||
|
|
||||||
./run.sh "$tcp"
|
./run.sh "$tcp"
|
||||||
|
|
Loading…
Reference in New Issue