From 4791e9e7fae2bfa226fc1d6e17ab9022fced82ea Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Thu, 26 Jan 2023 01:28:50 -0800 Subject: [PATCH] fix Issue 23598 - Circular reference bug with static if and eponymous templates (#14838) --- compiler/src/dmd/attrib.d | 7 +- compiler/src/dmd/attrib.h | 3 +- compiler/src/dmd/dsymbol.d | 1 + compiler/src/dmd/dsymbol.h | 2 + compiler/src/dmd/dsymbolsem.d | 13 ++-- compiler/src/dmd/dtemplate.d | 40 ++++++++++- compiler/src/dmd/frontend.h | 5 +- compiler/test/compilable/test23598.d | 87 +++++++++++++++++++++++ compiler/test/fail_compilation/ice12727.d | 2 +- compiler/test/fail_compilation/ice13816.d | 6 +- 10 files changed, 154 insertions(+), 12 deletions(-) create mode 100644 compiler/test/compilable/test23598.d diff --git a/compiler/src/dmd/attrib.d b/compiler/src/dmd/attrib.d index 712099ed33..c7bd343011 100644 --- a/compiler/src/dmd/attrib.d +++ b/compiler/src/dmd/attrib.d @@ -207,7 +207,7 @@ extern (C++) abstract class AttribDeclaration : Dsymbol objc.addSymbols(this, classes, categories); } - override final inout(AttribDeclaration) isAttribDeclaration() inout pure @safe + override inout(AttribDeclaration) isAttribDeclaration() inout pure @safe { return this; } @@ -1080,6 +1080,11 @@ extern (C++) final class StaticIfDeclaration : ConditionalDeclaration return "static if"; } + override inout(StaticIfDeclaration) isStaticIfDeclaration() inout pure @safe + { + return this; + } + override void accept(Visitor v) { v.visit(this); diff --git a/compiler/src/dmd/attrib.h b/compiler/src/dmd/attrib.h index 96c46e8f71..dd8da6f470 100644 --- a/compiler/src/dmd/attrib.h +++ b/compiler/src/dmd/attrib.h @@ -37,7 +37,7 @@ public: bool hasStaticCtorOrDtor() override final; void checkCtorConstInit() override final; void addLocalClass(ClassDeclarations *) override final; - AttribDeclaration *isAttribDeclaration() override final { return this; } + AttribDeclaration *isAttribDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -184,6 +184,7 @@ public: void addMember(Scope *sc, ScopeDsymbol *sds) override; void setScope(Scope *sc) override; void importAll(Scope *sc) override; + StaticIfDeclaration *isStaticIfDeclaration() override { return this; } const char *kind() const override; void accept(Visitor *v) override { v->visit(this); } }; diff --git a/compiler/src/dmd/dsymbol.d b/compiler/src/dmd/dsymbol.d index a17c830b6e..a9ca5b71d8 100644 --- a/compiler/src/dmd/dsymbol.d +++ b/compiler/src/dmd/dsymbol.d @@ -1413,6 +1413,7 @@ extern (C++) class Dsymbol : ASTNode inout(OverloadSet) isOverloadSet() inout { return null; } inout(CompileDeclaration) isCompileDeclaration() inout { return null; } inout(StaticAssert) isStaticAssert() inout { return null; } + inout(StaticIfDeclaration) isStaticIfDeclaration() inout { return null; } } /*********************************************************** diff --git a/compiler/src/dmd/dsymbol.h b/compiler/src/dmd/dsymbol.h index 88110e1f5d..ca9634eaad 100644 --- a/compiler/src/dmd/dsymbol.h +++ b/compiler/src/dmd/dsymbol.h @@ -72,6 +72,7 @@ class ExpressionDsymbol; class AliasAssign; class OverloadSet; class StaticAssert; +class StaticIfDeclaration; struct AA; #ifdef IN_GCC typedef union tree_node Symbol; @@ -323,6 +324,7 @@ public: virtual OverloadSet *isOverloadSet() { return NULL; } virtual CompileDeclaration *isCompileDeclaration() { return NULL; } virtual StaticAssert *isStaticAssert() { return NULL; } + virtual StaticIfDeclaration *isStaticIfDeclaration() { return NULL; } void accept(Visitor *v) override { v->visit(this); } }; diff --git a/compiler/src/dmd/dsymbolsem.d b/compiler/src/dmd/dsymbolsem.d index bc4016e824..cc0c5fe9f8 100644 --- a/compiler/src/dmd/dsymbolsem.d +++ b/compiler/src/dmd/dsymbolsem.d @@ -4539,7 +4539,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor override void visit(StructDeclaration sd) { - //printf("StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok); + enum log = false; + if (log) printf("+StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok); //static int count; if (++count == 20) assert(0); @@ -4609,6 +4610,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (!sd.members) // if opaque declaration { + if (log) printf("\topaque declaration %s\n", sd.toChars()); sd.semanticRun = PASS.semanticdone; return; } @@ -4660,7 +4662,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor sc2.pop(); - //printf("\tdeferring %s\n", toChars()); + if (log) printf("\tdeferring %s\n", sd.toChars()); return deferDsymbolSemantic(sd, scx); } @@ -4690,7 +4692,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor sd.inv = buildInv(sd, sc2); sd.semanticRun = PASS.semanticdone; - //printf("-StructDeclaration::semantic(this=%p, '%s')\n", sd, sd.toChars()); + if (log) printf("-StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok); sc2.pop(); @@ -4757,6 +4759,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor // Make an error in 2.110 if (sd.storage_class & STC.scope_) deprecation(sd.loc, "`scope` as a type constraint is deprecated. Use `scope` at the usage site."); + //printf("-StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok); } void interfaceSemantic(ClassDeclaration cd) @@ -6147,7 +6150,7 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, Expressions* Dsymbol s; if (Dsymbol.oneMembers(tempinst.members, &s, tempdecl.ident) && s) { - //printf("tempdecl.ident = %s, s = '%s'\n", tempdecl.ident.toChars(), s.kind(), s.toPrettyChars()); + //printf("tempdecl.ident = %s, s = `%s %s`\n", tempdecl.ident.toChars(), s.kind(), s.toPrettyChars()); //printf("setting aliasdecl\n"); tempinst.aliasdecl = s; } @@ -6194,7 +6197,7 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, Expressions* { if (!tempinst.aliasdecl || tempinst.aliasdecl != s) { - //printf("tempdecl.ident = %s, s = '%s'\n", tempdecl.ident.toChars(), s.kind(), s.toPrettyChars()); + //printf("tempdecl.ident = %s, s = `%s %s`\n", tempdecl.ident.toChars(), s.kind(), s.toPrettyChars()); //printf("setting aliasdecl 2\n"); tempinst.aliasdecl = s; } diff --git a/compiler/src/dmd/dtemplate.d b/compiler/src/dmd/dtemplate.d index 17e128b637..e6cabd5457 100644 --- a/compiler/src/dmd/dtemplate.d +++ b/compiler/src/dmd/dtemplate.d @@ -45,6 +45,7 @@ import dmd.aliasthis; import dmd.arraytypes; import dmd.astenums; import dmd.ast_node; +import dmd.attrib; import dmd.dcast; import dmd.dclass; import dmd.declaration; @@ -1226,7 +1227,7 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol paramscope.pop(); static if (LOGM) { - printf("-TemplateDeclaration.matchWithInstance(this = %p, ti = %p) = %d\n", this, ti, m); + printf("-TemplateDeclaration.matchWithInstance(this = %s, ti = %s) = %d\n", toChars(), ti.toChars(), m); } return m; } @@ -7520,6 +7521,43 @@ extern (C++) class TemplateInstance : ScopeDsymbol members.foreachDsymbol( (s) { s.importAll(sc2); } ); + if (!aliasdecl) + { + /* static if's are crucial to evaluating aliasdecl correctly. But + * evaluating the if/else bodies may require aliasdecl. + * So, evaluate the condition for static if's, but not their if/else bodies. + * Then try to set aliasdecl. + * Later do the if/else bodies. + * https://issues.dlang.org/show_bug.cgi?id=23598 + * It might be better to do this by attaching a lambda to the StaticIfDeclaration + * to do the oneMembers call after the sid.include(sc2) is run as part of dsymbolSemantic(). + */ + bool done; + void staticIfDg(Dsymbol s) + { + if (done || aliasdecl) + return; + //printf("\t staticIfDg on '%s %s' in '%s'\n", s.kind(), s.toChars(), this.toChars()); + if (!s.isStaticIfDeclaration()) + { + //s.dsymbolSemantic(sc2); + done = true; + return; + } + auto sid = s.isStaticIfDeclaration(); + sid.include(sc2); + if (members.length) + { + Dsymbol sa; + if (Dsymbol.oneMembers(members, &sa, tempdecl.ident) && sa) + aliasdecl = sa; + } + done = true; + } + + members.foreachDsymbol(&staticIfDg); + } + void symbolDg(Dsymbol s) { //printf("\t semantic on '%s' %p kind %s in '%s'\n", s.toChars(), s, s.kind(), this.toChars()); diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index 4f4894cd2e..2a3339c513 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -102,6 +102,7 @@ class VisibilityDeclaration; class OverloadSet; class CompileDeclaration; class StaticAssert; +class StaticIfDeclaration; class DsymbolTable; struct MangleOverride; class AliasThis; @@ -571,6 +572,7 @@ public: virtual OverloadSet* isOverloadSet(); virtual CompileDeclaration* isCompileDeclaration(); virtual StaticAssert* isStaticAssert(); + virtual StaticIfDeclaration* isStaticIfDeclaration(); }; typedef uint64_t size_t; @@ -5387,7 +5389,7 @@ public: void checkCtorConstInit() final override; void addLocalClass(Array* aclasses) final override; void addObjcSymbols(Array* classes, Array* categories) final override; - AttribDeclaration* isAttribDeclaration() final override; + AttribDeclaration* isAttribDeclaration() override; void accept(Visitor* v) override; }; @@ -5524,6 +5526,7 @@ public: void setScope(Scope* sc) override; void importAll(Scope* sc) override; const char* kind() const override; + StaticIfDeclaration* isStaticIfDeclaration() override; void accept(Visitor* v) override; }; diff --git a/compiler/test/compilable/test23598.d b/compiler/test/compilable/test23598.d new file mode 100644 index 0000000000..cdcb5a801d --- /dev/null +++ b/compiler/test/compilable/test23598.d @@ -0,0 +1,87 @@ +// https://issues.dlang.org/show_bug.cgi?id=23598 + +alias AliasSeq(a...) = a; + +static if (1) +{ + +template sort(alias f, a...) +{ + static if (a.length > 0) + { + alias x = f!(a[0]); + alias sort = a; + } + else + alias sort = a; +} + +alias SortedItems = sort!(isDependencyOf, String); + +enum isDependencyOf(Item) = Item.DirectDependencies.length == 0; + +struct String +{ + alias DirectDependencies = AliasSeq!(); + + enum l = SortedItems.length; // (3) +} + +} + +/*****************************************************/ + +static if (1) +{ +enum x = 1; +enum y = 2; + +template f(T) +{ + alias b = int; + static if (x) + { + alias c = x; + } + else + { + alias c = y; + } + + static if (is(typeof(c))) + { + } + else + { + static assert(0); + } +} + +void g() +{ + int x = f!int.c; +} +} + +/*****************************************************/ + +template forward(args...) +{ + template fwd(alias arg) + { + alias fwd = arg; + } + + alias Result = AliasSeq!(); + static foreach (arg; args) + Result = AliasSeq!(Result, fwd!arg); + static if (Result.length == 1) + alias forward = Result[0]; + else + alias forward = Result; +} + +void func(int i, int j) +{ + func(forward!(i, j)); +} diff --git a/compiler/test/fail_compilation/ice12727.d b/compiler/test/fail_compilation/ice12727.d index bf6af7b404..13eb8e09ed 100644 --- a/compiler/test/fail_compilation/ice12727.d +++ b/compiler/test/fail_compilation/ice12727.d @@ -1,13 +1,13 @@ /* TEST_OUTPUT: ---- +fail_compilation/ice12727.d(16): Error: template instance `IndexTuple!(1, 0)` recursive template expansion fail_compilation/ice12727.d(16): Error: alias `ice12727.IndexTuple!(1, 0).IndexTuple` recursive alias declaration fail_compilation/ice12727.d(23): Error: template instance `ice12727.IndexTuple!(1, 0)` error instantiating fail_compilation/ice12727.d(27): instantiated from here: `Matrix!(float, 3)` fail_compilation/ice12727.d(28): instantiated from here: `Vector!(float, 3)` ---- */ - template IndexTuple(int e, int s = 0, T...) { static if (s == e) diff --git a/compiler/test/fail_compilation/ice13816.d b/compiler/test/fail_compilation/ice13816.d index e683e339e6..aefe273da0 100644 --- a/compiler/test/fail_compilation/ice13816.d +++ b/compiler/test/fail_compilation/ice13816.d @@ -1,11 +1,13 @@ /* TEST_OUTPUT: --- -fail_compilation/ice13816.d(15): Error: alias `ice13816.ItemProperty!().ItemProperty` recursive alias declaration -fail_compilation/ice13816.d(20): Error: template instance `ice13816.ItemProperty!()` error instantiating +fail_compilation/ice13816.d(17): Error: template instance `TypeTuple!(ItemProperty!())` recursive template expansion +fail_compilation/ice13816.d(17): Error: alias `ice13816.ItemProperty!().ItemProperty` recursive alias declaration +fail_compilation/ice13816.d(22): Error: template instance `ice13816.ItemProperty!()` error instantiating --- */ + alias TypeTuple(T...) = T; template ItemProperty()