From ce363bccaebaceebd937a0f83c10533f2ad5811e Mon Sep 17 00:00:00 2001 From: ryuukk Date: Tue, 14 Feb 2023 18:41:15 +0100 Subject: [PATCH 1/7] resolve templates --- dsymbol/src/dsymbol/conversion/first.d | 228 ++++++++++++++++++ dsymbol/src/dsymbol/conversion/second.d | 208 ++++++++++++++++ dsymbol/src/dsymbol/type_lookup.d | 15 ++ tests/tc027/expected1.txt | 15 +- tests/tc027/run.sh | 2 +- tests/tc_templates_resolve/complex.d | 94 ++++++++ tests/tc_templates_resolve/expected_1_1.txt | 9 + tests/tc_templates_resolve/expected_1_2.txt | 9 + tests/tc_templates_resolve/expected_2_1.txt | 9 + tests/tc_templates_resolve/expected_2_2.txt | 9 + .../expected_complex_1.txt | 9 + .../expected_complex_2.txt | 10 + .../expected_complex_3.txt | 10 + .../expected_complex_4.txt | 10 + .../expected_complex_5.txt | 10 + .../expected_complex_6.txt | 10 + .../expected_complex_7.txt | 10 + .../expected_complex_8.txt | 8 + tests/tc_templates_resolve/file1.d | 31 +++ tests/tc_templates_resolve/file2.d | 31 +++ tests/tc_templates_resolve/run.sh | 49 ++++ 21 files changed, 777 insertions(+), 9 deletions(-) create mode 100644 tests/tc_templates_resolve/complex.d create mode 100644 tests/tc_templates_resolve/expected_1_1.txt create mode 100644 tests/tc_templates_resolve/expected_1_2.txt create mode 100644 tests/tc_templates_resolve/expected_2_1.txt create mode 100644 tests/tc_templates_resolve/expected_2_2.txt create mode 100644 tests/tc_templates_resolve/expected_complex_1.txt create mode 100644 tests/tc_templates_resolve/expected_complex_2.txt create mode 100644 tests/tc_templates_resolve/expected_complex_3.txt create mode 100644 tests/tc_templates_resolve/expected_complex_4.txt create mode 100644 tests/tc_templates_resolve/expected_complex_5.txt create mode 100644 tests/tc_templates_resolve/expected_complex_6.txt create mode 100644 tests/tc_templates_resolve/expected_complex_7.txt create mode 100644 tests/tc_templates_resolve/expected_complex_8.txt create mode 100644 tests/tc_templates_resolve/file1.d create mode 100644 tests/tc_templates_resolve/file2.d create mode 100644 tests/tc_templates_resolve/run.sh diff --git a/dsymbol/src/dsymbol/conversion/first.d b/dsymbol/src/dsymbol/conversion/first.d index e488566..1d4e180 100644 --- a/dsymbol/src/dsymbol/conversion/first.d +++ b/dsymbol/src/dsymbol/conversion/first.d @@ -231,10 +231,168 @@ final class FirstPass : ASTVisitor currentSymbol.addChild(symbol, true); symbol.acSymbol.protection = protection.current; } + + + void processIdentifierOrTemplate(SemanticSymbol* symbol, TypeLookup* lookup, VariableContext* ctx, VariableContext.TypeInstance* current, IdentifierOrTemplateInstance ioti) + { + if (ioti.identifier != tok!"") + current.chain ~= ioti.identifier.text; + else if (ioti.templateInstance) + processTemplateInstance(symbol, lookup, ctx, current, ioti.templateInstance); + } + + void processTypeIdentifierPart(SemanticSymbol* symbol, TypeLookup* lookup, VariableContext* ctx, VariableContext.TypeInstance* current, TypeIdentifierPart tip) + { + if (tip.identifierOrTemplateInstance) + processIdentifierOrTemplate(symbol, lookup, ctx, current, tip.identifierOrTemplateInstance); + + if (tip.typeIdentifierPart) + processTypeIdentifierPart(symbol, lookup, ctx, current, tip.typeIdentifierPart); + } + + void processTemplateArguments(SemanticSymbol* symbol, TypeLookup* lookup, VariableContext* ctx, VariableContext.TypeInstance* current, TemplateArguments targs) + { + if (targs.templateArgumentList) + { + foreach(i, targ; targs.templateArgumentList.items) + { + if (targ.type is null) continue; + if (targ.type.type2 is null) continue; + + auto part = targ.type.type2.typeIdentifierPart; + if (part is null) continue; + + auto newArg = GCAllocator.instance.make!(VariableContext.TypeInstance)(); + newArg.parent = current; + current.args ~= newArg; + + if (part.identifierOrTemplateInstance) + { + processIdentifierOrTemplate(symbol, lookup, ctx, newArg, part.identifierOrTemplateInstance); + } + if (part.typeIdentifierPart) + { + if (part.typeIdentifierPart.identifierOrTemplateInstance) + processIdentifierOrTemplate(symbol, lookup, ctx, newArg, part.typeIdentifierPart.identifierOrTemplateInstance); + } + } + } + else if (targs.templateSingleArgument) + { + auto singleArg = targs.templateSingleArgument; + auto arg = GCAllocator.instance.make!(VariableContext.TypeInstance)(); + arg.parent = current; + arg.name = singleArg.token.text; + arg.chain ~= arg.name; + current.args ~= arg; + } + } + + void processTemplateInstance(SemanticSymbol* symbol, TypeLookup* lookup, VariableContext* ctx, VariableContext.TypeInstance* current, TemplateInstance ti) + { + if (ti.identifier != tok!"") + current.chain ~= ti.identifier.text; + + if (ti.templateArguments) + processTemplateArguments(symbol, lookup, ctx, current, ti.templateArguments); + } + + void buildChain(SemanticSymbol* symbol, TypeLookup* lookup, VariableContext* ctx, TypeIdentifierPart tip) + { + if (tip.identifierOrTemplateInstance) + buildChainTemplateOrIdentifier(symbol, lookup, ctx, tip.identifierOrTemplateInstance); + if (tip.typeIdentifierPart) + buildChain(symbol, lookup, ctx, tip.typeIdentifierPart); + } + + void buildChainTemplateOrIdentifier(SemanticSymbol* symbol, TypeLookup* lookup, VariableContext* ctx, IdentifierOrTemplateInstance iot) + { + auto crumb = iot.identifier; + if (crumb != tok!"") + lookup.breadcrumbs.insert(istring(crumb.text)); + + if (iot.templateInstance) + { + if (iot.templateInstance.identifier != tok!"") + lookup.breadcrumbs.insert(istring(iot.templateInstance.identifier.text)); + } + } + + void traverseUnaryExpression( SemanticSymbol* symbol, TypeLookup* lookup, VariableContext* ctx, UnaryExpression ue) + { + if (PrimaryExpression pe = ue.primaryExpression) + { + if (pe.identifierOrTemplateInstance) + buildChainTemplateOrIdentifier(symbol, lookup, ctx, pe.identifierOrTemplateInstance); + + if (pe.basicType != tok!"") + lookup.breadcrumbs.insert(internString(str(pe.basicType.type))); + switch (pe.primary.type) + { + case tok!"identifier": + lookup.breadcrumbs.insert(internString(pe.primary.text)); + break; + case tok!"doubleLiteral": + lookup.breadcrumbs.insert(DOUBLE_LITERAL_SYMBOL_NAME); + break; + case tok!"floatLiteral": + lookup.breadcrumbs.insert(FLOAT_LITERAL_SYMBOL_NAME); + break; + case tok!"idoubleLiteral": + lookup.breadcrumbs.insert(IDOUBLE_LITERAL_SYMBOL_NAME); + break; + case tok!"ifloatLiteral": + lookup.breadcrumbs.insert(IFLOAT_LITERAL_SYMBOL_NAME); + break; + case tok!"intLiteral": + lookup.breadcrumbs.insert(INT_LITERAL_SYMBOL_NAME); + break; + case tok!"longLiteral": + lookup.breadcrumbs.insert(LONG_LITERAL_SYMBOL_NAME); + break; + case tok!"realLiteral": + lookup.breadcrumbs.insert(REAL_LITERAL_SYMBOL_NAME); + break; + case tok!"irealLiteral": + lookup.breadcrumbs.insert(IREAL_LITERAL_SYMBOL_NAME); + break; + case tok!"uintLiteral": + lookup.breadcrumbs.insert(UINT_LITERAL_SYMBOL_NAME); + break; + case tok!"ulongLiteral": + lookup.breadcrumbs.insert(ULONG_LITERAL_SYMBOL_NAME); + break; + case tok!"characterLiteral": + lookup.breadcrumbs.insert(CHAR_LITERAL_SYMBOL_NAME); + break; + case tok!"dstringLiteral": + lookup.breadcrumbs.insert(DSTRING_LITERAL_SYMBOL_NAME); + break; + case tok!"stringLiteral": + lookup.breadcrumbs.insert(STRING_LITERAL_SYMBOL_NAME); + break; + case tok!"wstringLiteral": + lookup.breadcrumbs.insert(WSTRING_LITERAL_SYMBOL_NAME); + break; + case tok!"false": + case tok!"true": + lookup.breadcrumbs.insert(BOOL_VALUE_SYMBOL_NAME); + break; + default: + break; + } + } + + if (IdentifierOrTemplateInstance iot = ue.identifierOrTemplateInstance) + buildChainTemplateOrIdentifier(symbol, lookup, ctx, iot); + + if(ue.unaryExpression) traverseUnaryExpression(symbol, lookup, ctx, ue.unaryExpression); + } override void visit(const VariableDeclaration dec) { assert (currentSymbol); + foreach (declarator; dec.declarators) { SemanticSymbol* symbol = allocateSemanticSymbol( @@ -255,6 +413,16 @@ final class FirstPass : ASTVisitor // TODO: remove this cast. See the note on structFieldTypes structFieldTypes.insert(cast() dec.type); } + + auto lookup = symbol.typeLookups.front; + + if (dec.type && dec.type.type2 && dec.type.type2.typeIdentifierPart) + { + TypeIdentifierPart typeIdentifierPart = cast(TypeIdentifierPart) dec.type.type2.typeIdentifierPart; + + lookup.ctx.root = GCAllocator.instance.make!(VariableContext.TypeInstance)(); + processTypeIdentifierPart(symbol, lookup, &lookup.ctx, lookup.ctx.root, typeIdentifierPart); + } } if (dec.autoDeclaration !is null) { @@ -277,6 +445,66 @@ final class FirstPass : ASTVisitor // TODO: remove this cast. See the note on structFieldTypes structFieldTypes.insert(null); } + + auto lookup = symbol.typeLookups.front; + + auto initializer = part.initializer.nonVoidInitializer; + if (initializer && initializer.assignExpression) + { + UnaryExpression unary = cast(UnaryExpression) initializer.assignExpression; + + if (unary && (unary.newExpression || unary.indexExpression)) + continue; + + lookup.breadcrumbs.clear(); + if (unary) + { + if (CastExpression castExpression = unary.castExpression) + { + if (castExpression.type && castExpression.type.type2) + { + Type2 t2 = castExpression.type.type2; + if (t2 && t2.typeIdentifierPart) + buildChain(symbol, lookup, &lookup.ctx, t2.typeIdentifierPart); + } + continue; + } + else if (FunctionCallExpression fc = unary.functionCallExpression) + unary = fc.unaryExpression; + // build chain + traverseUnaryExpression(symbol, lookup, &lookup.ctx, unary); + // needs to be reversed because it got added in order (right->left) + auto crumbs = &lookup.breadcrumbs; + istring[] result; + foreach(c; *crumbs) + result ~= c; + + crumbs.clear(); + foreach_reverse(c; result) + lookup.breadcrumbs.insert(c); + + // check template + if (IdentifierOrTemplateInstance iot = unary.identifierOrTemplateInstance) + { + if (iot.templateInstance) + { + lookup.ctx.root = GCAllocator.instance.make!(VariableContext.TypeInstance)(); + processTemplateInstance(symbol, lookup, &lookup.ctx, lookup.ctx.root, iot.templateInstance); + } + } + else if (PrimaryExpression pe = unary.primaryExpression) + { + if (pe.identifierOrTemplateInstance) + { + if (pe.identifierOrTemplateInstance.templateInstance) + { + lookup.ctx.root = GCAllocator.instance.make!(VariableContext.TypeInstance)(); + processTemplateInstance(symbol, lookup, &lookup.ctx, lookup.ctx.root, pe.identifierOrTemplateInstance.templateInstance); + } + } + } + } + } } } } diff --git a/dsymbol/src/dsymbol/conversion/second.d b/dsymbol/src/dsymbol/conversion/second.d index 433cd66..cb059d6 100644 --- a/dsymbol/src/dsymbol/conversion/second.d +++ b/dsymbol/src/dsymbol/conversion/second.d @@ -55,6 +55,22 @@ void secondPass(SemanticSymbol* currentSymbol, Scope* moduleScope, ref ModuleCac resolveType(currentSymbol.acSymbol, currentSymbol.typeLookups, moduleScope, cache); } + + if (currentSymbol.acSymbol.type && currentSymbol.typeLookups.length > 0) + { + TypeLookup* lookup = currentSymbol.typeLookups.front; + if (lookup.ctx.root) + { + auto type = currentSymbol.acSymbol.type; + if (type.kind == structName || type.kind == className || type.kind == functionName) + if (lookup.ctx.root.args.length > 0) + { + DSymbol*[string] mapping; + int depth; + resolveTemplate(currentSymbol.acSymbol, type, lookup, lookup.ctx.root, moduleScope, cache, depth, mapping); + } + } + } break; case importSymbol: if (currentSymbol.acSymbol.type is null) @@ -98,6 +114,198 @@ void secondPass(SemanticSymbol* currentSymbol, Scope* moduleScope, ref ModuleCac break; } } +/** + * Extract the return type from the callTip of a function symbol + */ +string extractReturnType(string callTip) +{ + import std.string: indexOf; + + auto spaceIndex = callTip.indexOf(" "); + if (spaceIndex <= 0) return ""; + + auto retPart = callTip[0 .. spaceIndex]; + auto returnTypeConst = retPart.length > 6 ? retPart[0 .. 6] == "const(" : false; + auto returnTypeInout = retPart.length > 6 ? retPart[0 .. 6] == "inout(" : false; + if (returnTypeConst || returnTypeInout) + { + retPart = retPart[retPart.indexOf("(") + 1 .. $]; + retPart = retPart[0 .. retPart.indexOf(")")]; + } + auto returnTypePtr = retPart[$-1] == '*'; + auto returnTypeArr = retPart[$-1] == ']'; + if (returnTypePtr) + { + retPart = retPart[0 .. $-1]; + } + return retPart; +} + +/** + * Copy a Type symbol with templates arguments + * Returns: Copy of Type symbol + */ +DSymbol* createTypeWithTemplateArgs(DSymbol* type, TypeLookup* lookup, VariableContext.TypeInstance* ti, ref ModuleCache cache, Scope* moduleScope, ref int depth, DSymbol*[string] m) +{ + assert(type); + DSymbol* newType = GCAllocator.instance.make!DSymbol("dummy", CompletionKind.dummy, null); + newType.name = type.name; + newType.kind = type.kind; + newType.qualifier = type.qualifier; + newType.protection = type.protection; + newType.symbolFile = type.symbolFile; + newType.doc = type.doc; + newType.callTip = type.callTip; + newType.type = type.type; + DSymbol*[string] mapping; + + int count = 0; + if (ti.args.length > 0) + { + foreach(part; type.opSlice()) + { + if (part.kind == CompletionKind.typeTmpParam) + { + scope(exit) count++; + if (count >= ti.args.length) + { + // warning("too many T for args available, investigate"); + continue; + } + auto key = part.name; + + DSymbol* first; + foreach(i, crumb; ti.args[count].chain) + { + auto argName = crumb; + if (i == 0) + { + + if (m) + if (key in m) + { + argName = m[key].name; + } + + auto result = moduleScope.getSymbolsAtGlobalScope(istring(argName)); + if (result.length == 0) + { + break; + } + first = result[0]; + } + else + first = first.getFirstPartNamed(istring(argName)); + } + + if (first is null) + continue; + + auto ca = ti.args[count]; + if (ca.chain.length > 0) + mapping[key] = createTypeWithTemplateArgs(first, lookup, ca, cache, moduleScope, depth, null); + } + } + } + + + // HACK: to support functions with template arguments that return a generic type + // first.d in processParameters only store the function's return type in the callTip + // maybe it's time to properly handle it by creating a proper symbol, so we can have + // proper support for functions that return complex types such as templates + if (type.kind == CompletionKind.functionName) + { + auto callTip = type.callTip; + if (callTip && callTip.length > 1) + { + auto retType = extractReturnType(callTip); + if (retType in mapping) + newType.type = mapping[retType]; + } + } + + assert(newType); + string[] T_names; + foreach(part; type.opSlice()) + { + if (part.kind == CompletionKind.typeTmpParam) + { + T_names ~= part.name; + } + else if (part.type && part.type.kind == CompletionKind.typeTmpParam) + { + DSymbol* newPart = GCAllocator.instance.make!DSymbol(part.name, part.kind, null); + newPart.qualifier = part.qualifier; + newPart.protection = part.protection; + newPart.symbolFile = part.symbolFile; + newPart.doc = part.doc; + newPart.callTip = part.callTip; + + if (part.type.name in mapping) + { + newPart.ownType = true; + newPart.type = mapping[part.type.name]; + } + else if (m && part.type.name in m) + { + newPart.ownType = true; + newPart.type = m[part.type.name]; + } + + newType.addChild(newPart, true); + } + else + { + // BUG: doing it recursively messes with the mapping + // i need to debug this and figure out perhaps a better way to do this stuff + // maybe move the VariableContext to the symbol directly + // i'll need to experiemnt with it + + //DSymbol* part_T; + //if (depth < 50) + //if (part.type && part.kind == CompletionKind.variableName) + //foreach(partPart; part.type.opSlice()) + //{ + // if (partPart.kind == CompletionKind.typeTmpParam) + // { + // part_T = part; + // foreach(arg; ti.args) + // { + // warning(" > ", arg.chain); + // foreach(aa; arg.args) + // warning(" > ", aa.chain); + // } + // warning("go agane ".blue, part.name, " ", part.type.name, " with arg: ", ti.chain," Ts: ", T_names); + // resolveTemplate(part, part.type, lookup, ti, moduleScope, cache, depth, mapping); + // break; + // } + // //else if (partPart.type && partPart.type.kind == CompletionKind.typeTmpParam) + // //{ + // // warning("here!".red," ", partPart.name," ", partPart.type.name); + // //} + //} + newType.addChild(part, false); + } + } + return newType; +} + +/** + * Resolve template arguments + */ +void resolveTemplate(DSymbol* variableSym, DSymbol* type, TypeLookup* lookup, VariableContext.TypeInstance* current, Scope* moduleScope, ref ModuleCache cache, ref int depth, DSymbol*[string] mapping = null) +{ + depth += 1; + + if (variableSym is null || type is null) return; + + if (current.chain.length == 0) return; // TODO: should not be empty, happens for simple stuff Inner inner; + + DSymbol* newType = createTypeWithTemplateArgs(type, lookup, current, cache, moduleScope, depth, mapping); + + variableSym.type = newType; + variableSym.ownType = true; +} void resolveImport(DSymbol* acSymbol, ref TypeLookups typeLookups, ref ModuleCache cache) diff --git a/dsymbol/src/dsymbol/type_lookup.d b/dsymbol/src/dsymbol/type_lookup.d index 2260e57..f7f79de 100644 --- a/dsymbol/src/dsymbol/type_lookup.d +++ b/dsymbol/src/dsymbol/type_lookup.d @@ -37,4 +37,19 @@ struct TypeLookup UnrolledList!istring breadcrumbs; /// The kind of type lookup TypeLookupKind kind; + /// To store information about template instances + VariableContext ctx; } + +struct VariableContext +{ + struct TypeInstance + { + string[] chain; + TypeInstance*[] args; + string name; + TypeInstance* parent; + } + TypeInstance* root; + int num; +} \ No newline at end of file diff --git a/tests/tc027/expected1.txt b/tests/tc027/expected1.txt index 3ce878f..0e9796e 100644 --- a/tests/tc027/expected1.txt +++ b/tests/tc027/expected1.txt @@ -1,9 +1,8 @@ identifiers -C h -alignof k -i v -init k -mangleof k -sizeof k -stringof k -tupleof k +alignof k +i v int i stdin 21 +init k +mangleof k +sizeof k +stringof k +tupleof k diff --git a/tests/tc027/run.sh b/tests/tc027/run.sh index 2f0f9e4..876777b 100755 --- a/tests/tc027/run.sh +++ b/tests/tc027/run.sh @@ -1,5 +1,5 @@ set -e set -u -../../bin/dcd-client $1 file.d -c66 > actual1.txt +../../bin/dcd-client $1 file.d --extended -c66 > actual1.txt diff actual1.txt expected1.txt --strip-trailing-cr diff --git a/tests/tc_templates_resolve/complex.d b/tests/tc_templates_resolve/complex.d new file mode 100644 index 0000000..9f1cb03 --- /dev/null +++ b/tests/tc_templates_resolve/complex.d @@ -0,0 +1,94 @@ +struct Data +{ + int inside_data; + Inner inner; +} + +struct Inner +{ + int inside_inner; +} + +struct AganeOne(T) +{ + int inside_aganeone; + T yo; +} + +struct AganeTwo(T, U) +{ + int inside_aganetwo; + T yo_T; + U yo_U; +} + +struct Other(T) +{ + int inside_other; + T what; + AganeOne!(T) agane_T; + AganeOne!(Inner) agane_inner; +} + +struct One(T){ T inside_one; } + +struct Outter { + struct Two(T, U){ int inside_two; T agane_one; U agane_two; One!(T) one_agane_one; T get_T(T)(){return T.init;} U get_U(){return U.init;} } +} + +struct A{ int inside_a;} +struct B{ int inside_b;} +struct C{ int inside_c;} + +struct What +{ + int inside_what; + const(V) get_it(T, U, V)() { return T.init; } +} + +void main() +{ + auto from_auto = Outter.Two!( + AganeOne!(Other!(Data)), + AganeTwo!(A, B) + )(); + + Outter.Two!( + AganeOne!(Other!(Data)), + AganeTwo!(A, Other!(B)) + ) from_normal; + + auto u = from_auto.get_U(); + auto uuu = from_normal.agane_two; + + auto v = from_normal.get_U(); + + What what; + auto it = what.get_it!(A, B, C)(); + + { + from_auto.agane_one. + } + { + from_auto.agane_two. + } + { + from_normal.agane_two. + } + { + from_normal.agane_two. + } + { + u. + } + { + uuu. + } + { + uuu. + } + { + it. + } + +} \ No newline at end of file diff --git a/tests/tc_templates_resolve/expected_1_1.txt b/tests/tc_templates_resolve/expected_1_1.txt new file mode 100644 index 0000000..f6e1453 --- /dev/null +++ b/tests/tc_templates_resolve/expected_1_1.txt @@ -0,0 +1,9 @@ +identifiers +alignof k +init k +mangleof k +one_t v One one_t stdin 103 +sizeof k +stringof k +tupleof k +value_t v A value_t stdin 0 diff --git a/tests/tc_templates_resolve/expected_1_2.txt b/tests/tc_templates_resolve/expected_1_2.txt new file mode 100644 index 0000000..bc6dc47 --- /dev/null +++ b/tests/tc_templates_resolve/expected_1_2.txt @@ -0,0 +1,9 @@ +identifiers +alignof k +init k +mangleof k +sizeof k +stringof k +tupleof k +value_t v A value_t stdin 0 +value_u v B value_u stdin 0 diff --git a/tests/tc_templates_resolve/expected_2_1.txt b/tests/tc_templates_resolve/expected_2_1.txt new file mode 100644 index 0000000..f6e1453 --- /dev/null +++ b/tests/tc_templates_resolve/expected_2_1.txt @@ -0,0 +1,9 @@ +identifiers +alignof k +init k +mangleof k +one_t v One one_t stdin 103 +sizeof k +stringof k +tupleof k +value_t v A value_t stdin 0 diff --git a/tests/tc_templates_resolve/expected_2_2.txt b/tests/tc_templates_resolve/expected_2_2.txt new file mode 100644 index 0000000..bc6dc47 --- /dev/null +++ b/tests/tc_templates_resolve/expected_2_2.txt @@ -0,0 +1,9 @@ +identifiers +alignof k +init k +mangleof k +sizeof k +stringof k +tupleof k +value_t v A value_t stdin 0 +value_u v B value_u stdin 0 diff --git a/tests/tc_templates_resolve/expected_complex_1.txt b/tests/tc_templates_resolve/expected_complex_1.txt new file mode 100644 index 0000000..fc11a50 --- /dev/null +++ b/tests/tc_templates_resolve/expected_complex_1.txt @@ -0,0 +1,9 @@ +identifiers +alignof k +init k +inside_aganeone v int inside_aganeone stdin 124 +mangleof k +sizeof k +stringof k +tupleof k +yo v Other yo stdin 0 diff --git a/tests/tc_templates_resolve/expected_complex_2.txt b/tests/tc_templates_resolve/expected_complex_2.txt new file mode 100644 index 0000000..62a1edf --- /dev/null +++ b/tests/tc_templates_resolve/expected_complex_2.txt @@ -0,0 +1,10 @@ +identifiers +alignof k +init k +inside_aganetwo v int inside_aganetwo stdin 186 +mangleof k +sizeof k +stringof k +tupleof k +yo_T v A yo_T stdin 0 +yo_U v B yo_U stdin 0 diff --git a/tests/tc_templates_resolve/expected_complex_3.txt b/tests/tc_templates_resolve/expected_complex_3.txt new file mode 100644 index 0000000..fb085b8 --- /dev/null +++ b/tests/tc_templates_resolve/expected_complex_3.txt @@ -0,0 +1,10 @@ +identifiers +alignof k +init k +inside_aganetwo v int inside_aganetwo stdin 186 +mangleof k +sizeof k +stringof k +tupleof k +yo_T v A yo_T stdin 0 +yo_U v Other yo_U stdin 0 diff --git a/tests/tc_templates_resolve/expected_complex_4.txt b/tests/tc_templates_resolve/expected_complex_4.txt new file mode 100644 index 0000000..fb085b8 --- /dev/null +++ b/tests/tc_templates_resolve/expected_complex_4.txt @@ -0,0 +1,10 @@ +identifiers +alignof k +init k +inside_aganetwo v int inside_aganetwo stdin 186 +mangleof k +sizeof k +stringof k +tupleof k +yo_T v A yo_T stdin 0 +yo_U v Other yo_U stdin 0 diff --git a/tests/tc_templates_resolve/expected_complex_5.txt b/tests/tc_templates_resolve/expected_complex_5.txt new file mode 100644 index 0000000..62a1edf --- /dev/null +++ b/tests/tc_templates_resolve/expected_complex_5.txt @@ -0,0 +1,10 @@ +identifiers +alignof k +init k +inside_aganetwo v int inside_aganetwo stdin 186 +mangleof k +sizeof k +stringof k +tupleof k +yo_T v A yo_T stdin 0 +yo_U v B yo_U stdin 0 diff --git a/tests/tc_templates_resolve/expected_complex_6.txt b/tests/tc_templates_resolve/expected_complex_6.txt new file mode 100644 index 0000000..fb085b8 --- /dev/null +++ b/tests/tc_templates_resolve/expected_complex_6.txt @@ -0,0 +1,10 @@ +identifiers +alignof k +init k +inside_aganetwo v int inside_aganetwo stdin 186 +mangleof k +sizeof k +stringof k +tupleof k +yo_T v A yo_T stdin 0 +yo_U v Other yo_U stdin 0 diff --git a/tests/tc_templates_resolve/expected_complex_7.txt b/tests/tc_templates_resolve/expected_complex_7.txt new file mode 100644 index 0000000..fb085b8 --- /dev/null +++ b/tests/tc_templates_resolve/expected_complex_7.txt @@ -0,0 +1,10 @@ +identifiers +alignof k +init k +inside_aganetwo v int inside_aganetwo stdin 186 +mangleof k +sizeof k +stringof k +tupleof k +yo_T v A yo_T stdin 0 +yo_U v Other yo_U stdin 0 diff --git a/tests/tc_templates_resolve/expected_complex_8.txt b/tests/tc_templates_resolve/expected_complex_8.txt new file mode 100644 index 0000000..a3a129c --- /dev/null +++ b/tests/tc_templates_resolve/expected_complex_8.txt @@ -0,0 +1,8 @@ +identifiers +alignof k +init k +inside_c v int inside_c stdin 605 +mangleof k +sizeof k +stringof k +tupleof k diff --git a/tests/tc_templates_resolve/file1.d b/tests/tc_templates_resolve/file1.d new file mode 100644 index 0000000..16fa8cf --- /dev/null +++ b/tests/tc_templates_resolve/file1.d @@ -0,0 +1,31 @@ +struct A +{ + int inside_a; +} +struct B +{ + int inside_b; +} +struct One(T) +{ + T value_t; + One!T one_t; +} + +struct Two(T, U) +{ + T value_t; + U value_u; +} + +void main() +{ + auto from_auto_one = One!A(); + auto from_auto_two = Two!(A, B)(); + { + from_auto_one. + } + { + from_auto_two. + } +} diff --git a/tests/tc_templates_resolve/file2.d b/tests/tc_templates_resolve/file2.d new file mode 100644 index 0000000..25e561b --- /dev/null +++ b/tests/tc_templates_resolve/file2.d @@ -0,0 +1,31 @@ +struct A +{ + int inside_a; +} +struct B +{ + int inside_b; +} +struct One(T) +{ + T value_t; + One!T one_t; +} + +struct Two(T, U) +{ + T value_t; + U value_u; +} + +void main() +{ + One!A from_normal_one; + Two!(A, B) from_normal_two; + { + from_normal_one. + } + { + from_normal_two. + } +} diff --git a/tests/tc_templates_resolve/run.sh b/tests/tc_templates_resolve/run.sh new file mode 100644 index 0000000..8bd9670 --- /dev/null +++ b/tests/tc_templates_resolve/run.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +set -e +set -u + +MODE=$1 + + +# ../../bin/dcd-client $1 file1.d --extended -c 270 + +function check () { + echo "$1 $2" + ../../bin/dcd-client $MODE $1.d --extended -c $2 > $3.txt + diff $3.txt $4.txt --strip-trailing-cr +} + + +#echo "test1" +../../bin/dcd-client $1 file1.d --extended -c 280 > actual_1_1.txt +diff actual_1_1.txt expected_1_1.txt --strip-trailing-cr + + +#echo "test2" +../../bin/dcd-client $1 file1.d --extended -c 315 > actual_1_2.txt +diff actual_1_2.txt expected_1_2.txt --strip-trailing-cr + + + +#echo "test3" +../../bin/dcd-client $1 file2.d --extended -c 268 > actual_2_1.txt +diff actual_2_1.txt expected_2_1.txt --strip-trailing-cr + + +#echo "test4" +../../bin/dcd-client $1 file2.d --extended -c 305 > actual_2_2.txt +diff actual_2_2.txt expected_2_2.txt --strip-trailing-cr + + + +#echo "test c omplex" +check complex 1121 actual_complex_1 expected_complex_1 +check complex 1162 actual_complex_2 expected_complex_2 +check complex 1205 actual_complex_3 expected_complex_3 +check complex 1248 actual_complex_4 expected_complex_4 +check complex 1271 actual_complex_5 expected_complex_5 +check complex 1296 actual_complex_6 expected_complex_6 +check complex 1321 actual_complex_7 expected_complex_7 +check complex 1345 actual_complex_8 expected_complex_8 + From aa2dadc768a8667cdd165baace1d7ffccf55164c Mon Sep 17 00:00:00 2001 From: ryuukk Date: Tue, 14 Feb 2023 18:46:07 +0100 Subject: [PATCH 2/7] fix test script permission --- tests/tc_templates_resolve/run.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 tests/tc_templates_resolve/run.sh diff --git a/tests/tc_templates_resolve/run.sh b/tests/tc_templates_resolve/run.sh old mode 100644 new mode 100755 From f8a3c5b111129e445aab6742d8d5264d6de2a383 Mon Sep 17 00:00:00 2001 From: ryuukk Date: Tue, 14 Feb 2023 21:51:54 +0100 Subject: [PATCH 3/7] make it work with builtin symbols --- dsymbol/src/dsymbol/conversion/first.d | 11 ++++++++++- dsymbol/src/dsymbol/conversion/second.d | 15 +++++++++++++-- tests/tc_templates_resolve/expected_3_1.txt | 9 +++++++++ tests/tc_templates_resolve/file3.d | 11 +++++++++++ tests/tc_templates_resolve/run.sh | 12 +++++++----- 5 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 tests/tc_templates_resolve/expected_3_1.txt create mode 100644 tests/tc_templates_resolve/file3.d diff --git a/dsymbol/src/dsymbol/conversion/first.d b/dsymbol/src/dsymbol/conversion/first.d index 1d4e180..c5eb32f 100644 --- a/dsymbol/src/dsymbol/conversion/first.d +++ b/dsymbol/src/dsymbol/conversion/first.d @@ -260,7 +260,16 @@ final class FirstPass : ASTVisitor if (targ.type.type2 is null) continue; auto part = targ.type.type2.typeIdentifierPart; - if (part is null) continue; + if (part is null) + { + if (targ.type.type2.builtinType == tok!"") continue; + auto builtInName = getBuiltinTypeName(targ.type.type2.builtinType); + auto newArg = GCAllocator.instance.make!(VariableContext.TypeInstance)(); + newArg.parent = current; + newArg.chain ~= builtInName; + current.args ~= newArg; + continue; + } auto newArg = GCAllocator.instance.make!(VariableContext.TypeInstance)(); newArg.parent = current; diff --git a/dsymbol/src/dsymbol/conversion/second.d b/dsymbol/src/dsymbol/conversion/second.d index cb059d6..8a9c521 100644 --- a/dsymbol/src/dsymbol/conversion/second.d +++ b/dsymbol/src/dsymbol/conversion/second.d @@ -175,18 +175,29 @@ DSymbol* createTypeWithTemplateArgs(DSymbol* type, TypeLookup* lookup, VariableC auto key = part.name; DSymbol* first; + bool isBuiltin; foreach(i, crumb; ti.args[count].chain) { auto argName = crumb; if (i == 0) { - if (m) if (key in m) { argName = m[key].name; } + // check if that's a built in type + // if it is, then use it and skip the type creation step + foreach(builtin; builtinSymbols) + { + if (builtin.name == crumb) + { + first = builtin; + isBuiltin = true; + break; + } + } auto result = moduleScope.getSymbolsAtGlobalScope(istring(argName)); if (result.length == 0) { @@ -203,7 +214,7 @@ DSymbol* createTypeWithTemplateArgs(DSymbol* type, TypeLookup* lookup, VariableC auto ca = ti.args[count]; if (ca.chain.length > 0) - mapping[key] = createTypeWithTemplateArgs(first, lookup, ca, cache, moduleScope, depth, null); + mapping[key] = isBuiltin ? first : createTypeWithTemplateArgs(first, lookup, ca, cache, moduleScope, depth, null); } } } diff --git a/tests/tc_templates_resolve/expected_3_1.txt b/tests/tc_templates_resolve/expected_3_1.txt new file mode 100644 index 0000000..6a19b1a --- /dev/null +++ b/tests/tc_templates_resolve/expected_3_1.txt @@ -0,0 +1,9 @@ +identifiers +alignof k +init k +mangleof k +sizeof k +stringof k +tupleof k +value_key v int value_key stdin 0 +value_value v int value_value stdin 0 diff --git a/tests/tc_templates_resolve/file3.d b/tests/tc_templates_resolve/file3.d new file mode 100644 index 0000000..4f2a26a --- /dev/null +++ b/tests/tc_templates_resolve/file3.d @@ -0,0 +1,11 @@ +struct HashMap(Key, Value) +{ + Key value_key; + Value value_value; +} + +void main() +{ + auto hmap = HashMap!(int, int)(); + hmap. +} \ No newline at end of file diff --git a/tests/tc_templates_resolve/run.sh b/tests/tc_templates_resolve/run.sh index 8bd9670..8810ffa 100755 --- a/tests/tc_templates_resolve/run.sh +++ b/tests/tc_templates_resolve/run.sh @@ -5,9 +5,6 @@ set -u MODE=$1 - -# ../../bin/dcd-client $1 file1.d --extended -c 270 - function check () { echo "$1 $2" ../../bin/dcd-client $MODE $1.d --extended -c $2 > $3.txt @@ -36,8 +33,13 @@ diff actual_2_1.txt expected_2_1.txt --strip-trailing-cr diff actual_2_2.txt expected_2_2.txt --strip-trailing-cr +#echo "test5" +../../bin/dcd-client $1 file3.d --extended -c 144 > actual_3_1.txt +diff actual_3_1.txt expected_3_1.txt --strip-trailing-cr -#echo "test c omplex" + + +#echo "test complex" check complex 1121 actual_complex_1 expected_complex_1 check complex 1162 actual_complex_2 expected_complex_2 check complex 1205 actual_complex_3 expected_complex_3 @@ -46,4 +48,4 @@ check complex 1271 actual_complex_5 expected_complex_5 check complex 1296 actual_complex_6 expected_complex_6 check complex 1321 actual_complex_7 expected_complex_7 check complex 1345 actual_complex_8 expected_complex_8 - +check complex 1413 actual_complex_9 expected_complex_9 From 1f4cd702ba954a8d803c3c9b67b9e204e4526ce3 Mon Sep 17 00:00:00 2001 From: ryuukk Date: Tue, 14 Feb 2023 22:01:55 +0100 Subject: [PATCH 4/7] fix test script --- tests/tc_templates_resolve/run.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/tc_templates_resolve/run.sh b/tests/tc_templates_resolve/run.sh index 8810ffa..b795233 100755 --- a/tests/tc_templates_resolve/run.sh +++ b/tests/tc_templates_resolve/run.sh @@ -48,4 +48,3 @@ check complex 1271 actual_complex_5 expected_complex_5 check complex 1296 actual_complex_6 expected_complex_6 check complex 1321 actual_complex_7 expected_complex_7 check complex 1345 actual_complex_8 expected_complex_8 -check complex 1413 actual_complex_9 expected_complex_9 From 81da9f4c4bc8abf6bb2494a5ec36101a7459d692 Mon Sep 17 00:00:00 2001 From: ryuukk Date: Tue, 14 Feb 2023 22:18:01 +0100 Subject: [PATCH 5/7] fix test --- tests/tc_templates_resolve/run.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/tc_templates_resolve/run.sh b/tests/tc_templates_resolve/run.sh index b795233..709d9c2 100755 --- a/tests/tc_templates_resolve/run.sh +++ b/tests/tc_templates_resolve/run.sh @@ -34,11 +34,10 @@ diff actual_2_2.txt expected_2_2.txt --strip-trailing-cr #echo "test5" -../../bin/dcd-client $1 file3.d --extended -c 144 > actual_3_1.txt +../../bin/dcd-client $1 file3.d --extended -c 135 > actual_3_1.txt diff actual_3_1.txt expected_3_1.txt --strip-trailing-cr - #echo "test complex" check complex 1121 actual_complex_1 expected_complex_1 check complex 1162 actual_complex_2 expected_complex_2 From e7a83be721356da02f6de23dd2d4f46401f27560 Mon Sep 17 00:00:00 2001 From: ryuukk Date: Fri, 17 Feb 2023 02:45:40 +0100 Subject: [PATCH 6/7] add more tests --- dsymbol/src/dsymbol/conversion/second.d | 16 ++++++- tests/tc_templates_resolve/expected_3_2.txt | 9 ++++ tests/tc_templates_resolve/expected_3_3.txt | 9 ++++ tests/tc_templates_resolve/expected_3_4.txt | 9 ++++ tests/tc_templates_resolve/expected_3_5.txt | 8 ++++ tests/tc_templates_resolve/file3.d | 46 +++++++++++++++++++-- tests/tc_templates_resolve/run.sh | 32 +++++++++----- 7 files changed, 113 insertions(+), 16 deletions(-) create mode 100644 tests/tc_templates_resolve/expected_3_2.txt create mode 100644 tests/tc_templates_resolve/expected_3_3.txt create mode 100644 tests/tc_templates_resolve/expected_3_4.txt create mode 100644 tests/tc_templates_resolve/expected_3_5.txt diff --git a/dsymbol/src/dsymbol/conversion/second.d b/dsymbol/src/dsymbol/conversion/second.d index 8a9c521..d3cd9d0 100644 --- a/dsymbol/src/dsymbol/conversion/second.d +++ b/dsymbol/src/dsymbol/conversion/second.d @@ -95,8 +95,22 @@ void secondPass(SemanticSymbol* currentSymbol, Scope* moduleScope, ref ModuleCac break; } + // let's be methodic about the way we traverse symbols + // so that childs have access to resolved symbols + // functions should be last, because inside, there might be symbols that references + // code from the parent not yet resolved (templates) foreach (child; currentSymbol.children) - secondPass(child, moduleScope, cache); + if (child.acSymbol.kind != CompletionKind.variableName && child.acSymbol.kind != CompletionKind.functionName) + secondPass(child, moduleScope, cache); + + foreach (child; currentSymbol.children) + if (child.acSymbol.kind == CompletionKind.variableName) + secondPass(child, moduleScope, cache); + + foreach (child; currentSymbol.children) + if (child.acSymbol.kind == CompletionKind.functionName) + secondPass(child, moduleScope, cache); + // Alias this and mixin templates are resolved after child nodes are // resolved so that the correct symbol information will be available. diff --git a/tests/tc_templates_resolve/expected_3_2.txt b/tests/tc_templates_resolve/expected_3_2.txt new file mode 100644 index 0000000..6a19b1a --- /dev/null +++ b/tests/tc_templates_resolve/expected_3_2.txt @@ -0,0 +1,9 @@ +identifiers +alignof k +init k +mangleof k +sizeof k +stringof k +tupleof k +value_key v int value_key stdin 0 +value_value v int value_value stdin 0 diff --git a/tests/tc_templates_resolve/expected_3_3.txt b/tests/tc_templates_resolve/expected_3_3.txt new file mode 100644 index 0000000..6a19b1a --- /dev/null +++ b/tests/tc_templates_resolve/expected_3_3.txt @@ -0,0 +1,9 @@ +identifiers +alignof k +init k +mangleof k +sizeof k +stringof k +tupleof k +value_key v int value_key stdin 0 +value_value v int value_value stdin 0 diff --git a/tests/tc_templates_resolve/expected_3_4.txt b/tests/tc_templates_resolve/expected_3_4.txt new file mode 100644 index 0000000..6a19b1a --- /dev/null +++ b/tests/tc_templates_resolve/expected_3_4.txt @@ -0,0 +1,9 @@ +identifiers +alignof k +init k +mangleof k +sizeof k +stringof k +tupleof k +value_key v int value_key stdin 0 +value_value v int value_value stdin 0 diff --git a/tests/tc_templates_resolve/expected_3_5.txt b/tests/tc_templates_resolve/expected_3_5.txt new file mode 100644 index 0000000..bfc6075 --- /dev/null +++ b/tests/tc_templates_resolve/expected_3_5.txt @@ -0,0 +1,8 @@ +identifiers +alignof k +init k +mangleof k +max k +min k +sizeof k +stringof k diff --git a/tests/tc_templates_resolve/file3.d b/tests/tc_templates_resolve/file3.d index 4f2a26a..946c176 100644 --- a/tests/tc_templates_resolve/file3.d +++ b/tests/tc_templates_resolve/file3.d @@ -1,4 +1,4 @@ -struct HashMap(Key, Value) +struct TopHashMap(Key, Value) { Key value_key; Value value_value; @@ -6,6 +6,44 @@ struct HashMap(Key, Value) void main() { - auto hmap = HashMap!(int, int)(); - hmap. -} \ No newline at end of file + auto top = TopHashMap!(int, int)(); + auto bottom = BottomHashMap!(int, int)(); + { + top. + } + { + auto copy = top; + copy. + } + { + bottom. + } + { + auto copy = bottom; + copy. + } + { + auto wf = WithFunction!(int, int)(); + auto gkey = wf.get_key(); + gkey. + } +} + +struct BottomHashMap(Key, Value) +{ + Key value_key; + Value value_value; +} + +struct WithFunction(Key, Value) +{ + Key get_key() + { + return Key.init; + } + + Value get_value() + { + return Value.init; + } +} diff --git a/tests/tc_templates_resolve/run.sh b/tests/tc_templates_resolve/run.sh index 709d9c2..15b4c03 100755 --- a/tests/tc_templates_resolve/run.sh +++ b/tests/tc_templates_resolve/run.sh @@ -13,29 +13,39 @@ function check () { #echo "test1" -../../bin/dcd-client $1 file1.d --extended -c 280 > actual_1_1.txt -diff actual_1_1.txt expected_1_1.txt --strip-trailing-cr +check file1 280 actual_1_1 expected_1_1 #echo "test2" -../../bin/dcd-client $1 file1.d --extended -c 315 > actual_1_2.txt -diff actual_1_2.txt expected_1_2.txt --strip-trailing-cr - +check file1 315 actual_1_2 expected_1_2 #echo "test3" -../../bin/dcd-client $1 file2.d --extended -c 268 > actual_2_1.txt -diff actual_2_1.txt expected_2_1.txt --strip-trailing-cr +check file2 268 actual_2_1 expected_2_1 #echo "test4" -../../bin/dcd-client $1 file2.d --extended -c 305 > actual_2_2.txt -diff actual_2_2.txt expected_2_2.txt --strip-trailing-cr +check file2 305 actual_2_2 expected_2_2 #echo "test5" -../../bin/dcd-client $1 file3.d --extended -c 135 > actual_3_1.txt -diff actual_3_1.txt expected_3_1.txt --strip-trailing-cr +check file3 195 actual_3_1 expected_3_1 + + +#echo "test6" +check file3 246 actual_3_2 expected_3_2 + + +#echo "test7" +check file3 274 actual_3_3 expected_3_3 + + +#echo "test8" +check file3 328 actual_3_4 expected_3_4 + + +#echo "test9" +check file3 433 actual_3_5 expected_3_5 #echo "test complex" From dd27ab0799a2d845c11d1e072789f00d30631e13 Mon Sep 17 00:00:00 2001 From: ryuukk Date: Fri, 17 Feb 2023 02:49:47 +0100 Subject: [PATCH 7/7] never own builtin symbols --- dsymbol/src/dsymbol/conversion/second.d | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/dsymbol/src/dsymbol/conversion/second.d b/dsymbol/src/dsymbol/conversion/second.d index d3cd9d0..a20f655 100644 --- a/dsymbol/src/dsymbol/conversion/second.d +++ b/dsymbol/src/dsymbol/conversion/second.d @@ -245,7 +245,11 @@ DSymbol* createTypeWithTemplateArgs(DSymbol* type, TypeLookup* lookup, VariableC { auto retType = extractReturnType(callTip); if (retType in mapping) - newType.type = mapping[retType]; + { + auto result = mapping[retType]; + newType.ownType = result.kind == CompletionKind.keyword ? false : true; + newType.type = result; + } } } @@ -268,13 +272,15 @@ DSymbol* createTypeWithTemplateArgs(DSymbol* type, TypeLookup* lookup, VariableC if (part.type.name in mapping) { - newPart.ownType = true; - newPart.type = mapping[part.type.name]; + auto result = mapping[part.type.name]; + newPart.ownType = result.kind == CompletionKind.keyword ? false : true; + newPart.type = result; } else if (m && part.type.name in m) { - newPart.ownType = true; - newPart.type = m[part.type.name]; + auto result = m[part.type.name]; + newPart.ownType = result.kind == CompletionKind.keyword ? false : true; + newPart.type = result; } newType.addChild(newPart, true);