Skip to content

Commit 5ef7583

Browse files
committed
The Great Desugaring
A major source of headaches and probably the source ldc-developers#1 of mapping failures for sophisticated C++ libraries has been the restrictions of D template instantiation scope. Those restrictions are totally necessary and work great in D, and Clang adding "sugar" to its types made it possible to comply with them... most of the time. One particular wall was hit while trying to map the MSVC standard library. For type_traits's conjunction<_Is_character<char>, _Is_character<char>>, the base class in Clang's AST is: TemplateSpecializationType 0xdfaed30 '_Conjunction<struct std::_Is_character<char>, struct std::_Is_character<char> >' sugar _Conjunction | -TemplateArgument type 'struct std::_Is_character<char>':'struct std::_Is_character<char>' | SubstTemplateTypeParmType 0xdda9f20 'struct std::_Is_character<char>' sugar | -TemplateTypeParmType 0xa7ce0f0 '_Traits' dependent contains_unexpanded_pack depth 0 index 0 pack | `-TemplateTypeParm 0xa7ce0c0 '_Traits' `-RecordType 0xa9079d0 'struct std::_Is_character<char>' `-ClassTemplateSpecialization 0xa907930 '_Is_character' | -TemplateArgument type 'struct std::_Is_character<char>' : 'struct std::_Is_character<char>' | SubstTemplateTypeParmType 0xdda9f20 'struct std::_Is_character<char>' sugar (...same as above....) `-RecordType 0xdfaed10 'struct std::_Conjunction<struct std::_Is_character<char>, struct std::_Is_character<char> >' `-ClassTemplateSpecialization 0xdfaec78 '_Conjunction' This is one of those rare cases where Clang has lost pack information and no simple/good heuristic to guess when two or more arguments come from the same pack appears to be possible. Hence either Clang needs to be modified to preserve pack info, or a complicated check/heuristic is needed, or we've got to stop trying to stick to DMD's way. The third option was chosen because there's actually no good reason to comply with those restrictions. Reflection doesn't get improved, and while instantiation from C++ modules, every referenced symbol is from C++ modules. So from now on: - Every type gets desugared before being mapped in non-dependent contexts. - C++ modules now have access to the "C++ global namespace" during name lookups, through a special type of global import. This allows to discard the C++ symbol collector and substitution complicated hack when mapping template arguments deduced by Sema.
1 parent ff80b5d commit 5ef7583

File tree

9 files changed

+64
-227
lines changed

9 files changed

+64
-227
lines changed

dmd2/cpp/cppdeclaration.cpp

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -233,14 +233,6 @@ void DtorDeclaration::semantic(Scope *sc)
233233
::DtorDeclaration::semantic(sc);
234234
}
235235

236-
// Cheat and use the C++ "global scope", we can do it safely in specific cases
237-
Scope *globalScope(::Module *m)
238-
{
239-
auto sc = Scope::createGlobal(m);
240-
sc = sc->push(cpp::Module::rootPackage);
241-
return sc;
242-
}
243-
244236
void DeclReferencer::Traverse(Loc loc, Scope *sc, clang::Stmt *S)
245237
{
246238
this->loc = loc;
@@ -490,12 +482,11 @@ void FuncDeclaration::semantic3reference(::FuncDeclaration *fd, Scope *sc)
490482
const clang::FunctionDecl *Def;
491483
if (!FD->isInvalidDecl() && FD->hasBody(Def))
492484
{
493-
auto globalSc = globalScope(sc->instantiatingModule());
494-
declReferencer.Traverse(fd->loc, globalSc, Def->getBody());
485+
declReferencer.Traverse(fd->loc, sc, Def->getBody());
495486

496487
if (auto Ctor = dyn_cast<clang::CXXConstructorDecl>(FD))
497488
for (auto& Init: Ctor->inits())
498-
declReferencer.Traverse(fd->loc, globalSc, Init->getInit());
489+
declReferencer.Traverse(fd->loc, sc, Init->getInit());
499490
}
500491

501492
fd->semanticRun = PASSsemantic3done;

dmd2/cpp/cppdeclaration.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,6 @@ class DeclReferencer : public clang::RecursiveASTVisitor<DeclReferencer>
208208
DeclReferencer() : expmap(mapper)
209209
{
210210
mapper.addImplicitDecls = false;
211-
mapper.cppPrefix = false;
212211
}
213212

214213
void Traverse(Loc loc, Scope *sc, clang::Stmt *S);
@@ -221,7 +220,6 @@ class DeclReferencer : public clang::RecursiveASTVisitor<DeclReferencer>
221220
};
222221

223222
extern DeclReferencer declReferencer;
224-
Scope *globalScope(::Module *m);
225223

226224
const clang::Decl *getCanonicalDecl(const clang::Decl *D); // the only difference with D->getCanonicalDecl() is that if the canonical decl is an out-of-ilne friend' decl and the actual decl is declared, this returns the latter instead of the former
227225
bool isPolymorphic(const clang::RecordDecl *D);

dmd2/cpp/cppexpression.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -478,9 +478,8 @@ Expression* ExprMapper::fromExpression(const clang::Expr *E, bool interpret) //
478478
}
479479
else if (auto SNTTP = dyn_cast<clang::SubstNonTypeTemplateParmExpr>(E))
480480
{
481-
if (/*!interpret || */SNTTP->isValueDependent()) // NOTE/FIXME(?): this doesn't work with packs, which get added n times if they have n elements, and Clang doesn't store pack info in the SNTTP
482-
e = fromExpressionNonTypeTemplateParm(loc,
483-
SNTTP->getParameter());
481+
if (SNTTP->isValueDependent())
482+
e = fromExpressionNonTypeTemplateParm(loc, SNTTP->getParameter());
484483
else
485484
e = fromExpression(SNTTP->getReplacement());
486485
}

dmd2/cpp/cppimport.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ namespace cpp
1616
Import::Import(Loc loc, Identifiers *packages, Identifier *id, Identifier *aliasId, int isstatic)
1717
: ::Import(loc, packages, id, aliasId, isstatic)
1818
{
19-
// add "_cpp" as leftmost package to avoid name clashes
19+
// add "§cpp" as leftmost package to avoid name clashes
2020
if (!this->packages)
2121
this->packages = new Identifiers;
22-
this->packages->shift(Identifier::idPool("_cpp")); // any better idea ?
22+
this->packages->shift(Identifier::idPool(u8"§cpp"));
2323

2424
if (!aliasId)
2525
setSymIdent();
@@ -63,4 +63,19 @@ void Modmap::semantic(Scope* sc)
6363
{
6464
}
6565

66+
GlobalImport::GlobalImport(Loc loc)
67+
: ::Import(loc, nullptr, Identifier::idPool(u8"§cpp"), nullptr, 0)
68+
{
69+
this->pkg = cpp::Module::rootPackage;
70+
}
71+
72+
void GlobalImport::load(Scope *sc)
73+
{
74+
if (loaded)
75+
return;
76+
77+
sc->scopesym->importScope(pkg, Prot(PROTprivate));
78+
loaded = true;
79+
}
80+
6681
}

dmd2/cpp/cppimport.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ class Import : public ::Import
2222

2323
Import(Loc loc, Identifiers *packages, Identifier *id, Identifier *aliasId, int isstatic);
2424

25-
::Module *loadModule(Loc loc, Identifiers *packages, Identifier *id);
26-
void load(Scope *sc) override; // HACK
25+
::Module *loadModule(Loc loc, Identifiers *packages, Identifier *id) override;
26+
void load(Scope *sc) override;
2727
};
2828

2929
struct Modmap : public ::Modmap
@@ -33,8 +33,20 @@ struct Modmap : public ::Modmap
3333

3434
Modmap(Loc loc, StringExp *arg);
3535

36-
void importAll(Scope *sc);
37-
void semantic(Scope *sc);
36+
void importAll(Scope *sc) override;
37+
void semantic(Scope *sc) override;
38+
};
39+
40+
// Special import for lookups from C++ modules
41+
class GlobalImport : public ::Import
42+
{
43+
public:
44+
CALYPSO_LANGPLUGIN
45+
46+
bool loaded = false;
47+
48+
GlobalImport(Loc loc);
49+
void load(Scope *sc) override;
3850
};
3951

4052
}

dmd2/cpp/cppmodule.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ Modules Module::amodules;
5454

5555
void Module::init()
5656
{
57-
rootPackage = new Package(Identifier::idPool("_cpp"));
57+
rootPackage = new Package(Identifier::idPool(u8"§cpp"));
5858
rootPackage->symtab = new DsymbolTable;
5959

6060
modules->insert(rootPackage);
@@ -121,6 +121,8 @@ void Module::addPreambule()
121121
::Import *im = new ::Import(Loc(), packages, Identifier::idPool("core"), nullptr, true);
122122
members->shift(im);
123123
}
124+
// the C++ special import for global namespace lookups
125+
members->shift(new GlobalImport(Loc()));
124126
{ // object
125127
::Import *im = new ::Import(Loc(), nullptr, Id::object, nullptr, true);
126128
members->shift(im);

dmd2/cpp/cpptemplate.cpp

Lines changed: 1 addition & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -54,100 +54,6 @@ TemplateDeclaration::TemplateDeclaration(const TemplateDeclaration &o)
5454

5555
IMPLEMENT_syntaxCopy(TemplateDeclaration, TempOrSpec)
5656

57-
// HACK-ish unfortunately.. but arg deduction isn't trivial. Can't think of a simpler way.
58-
// TODO: remove this, use cppdeclaration.cpp Referencer approach instead w/ the global scope
59-
struct CppSymCollector
60-
{
61-
Dsymbols *substsyms;
62-
CppSymCollector(Dsymbols *substsyms)
63-
: substsyms(substsyms) {}
64-
65-
inline void addIfCPP(Dsymbol *s)
66-
{
67-
if (isCPP(s))
68-
substsyms->push(s);
69-
70-
if (auto ti = s->parent->isTemplateInstance())
71-
if (isCPP(ti))
72-
collect(ti->tiargs);
73-
}
74-
75-
void collect(Dsymbol *s)
76-
{
77-
addIfCPP(s);
78-
}
79-
80-
void collect(Type *t)
81-
{
82-
switch (t->ty)
83-
{
84-
case Tstruct:
85-
addIfCPP(static_cast<TypeStruct*>(t)->sym);
86-
break;
87-
case Tclass:
88-
addIfCPP(static_cast<TypeClass*>(t)->sym);
89-
break;
90-
case Tenum:
91-
addIfCPP(static_cast<TypeEnum*>(t)->sym);
92-
break;
93-
case Tarray:
94-
case Tsarray:
95-
case Tpointer:
96-
case Treference:
97-
collect(static_cast<TypeNext*>(t)->next);
98-
break;
99-
case Tfunction:
100-
{
101-
auto tf = static_cast<TypeFunction*>(t);
102-
collect(tf->next);
103-
for (auto p: *tf->parameters)
104-
collect(p->type);
105-
break;
106-
}
107-
case Tident:
108-
case Tinstance:
109-
default:
110-
// ::warning(Loc(), "Collecting C++ symbols unhandled for type %s:\"%s\"",
111-
// t->kind(), t->toChars());
112-
break;
113-
}
114-
}
115-
116-
void collect(Expression *e)
117-
{
118-
// TODO DotIdExp ...
119-
}
120-
121-
void collect(Tuple *tup)
122-
{
123-
collect(&tup->objects);
124-
}
125-
126-
void collect(Objects *tiargs)
127-
{
128-
for (auto o: *tiargs)
129-
{
130-
Type *ta = isType(o);
131-
Expression *ea = isExpression(o);
132-
Dsymbol *sa = isDsymbol(o);
133-
Tuple *tupa = isTuple(o);
134-
135-
if (ta) collect(ta);
136-
else if (ea) collect(ea);
137-
else if (sa) collect(sa);
138-
else { assert(tupa); collect(tupa); }
139-
}
140-
}
141-
};
142-
143-
static Dsymbols *collectSymbols(Objects *tiargs)
144-
{
145-
auto substsyms = new Dsymbols;
146-
CppSymCollector(substsyms).collect(tiargs);
147-
148-
return substsyms;
149-
}
150-
15157
static void fillTemplateArgumentListInfo(Loc loc, Scope *sc, clang::TemplateArgumentListInfo& Args,
15258
Objects *tiargs, const clang::RedeclarableTemplateDecl *Temp,
15359
TypeMapper& tymap, ExprMapper& expmap)
@@ -416,8 +322,6 @@ MATCH TemplateDeclaration::matchWithInstance(Scope *sc, ::TemplateInstance *ti,
416322

417323
TypeMapper tymap;
418324
tymap.addImplicitDecls = false;
419-
tymap.substsyms = collectSymbols(dedtypes);
420-
tymap.desugar = true;
421325

422326
auto InstArgs = (isForeignInstance(ti) ? getTemplateInstantiationArgs(Inst)
423327
: getTemplateArgs(Inst))->asArray();
@@ -610,7 +514,6 @@ TemplateInstUnion TemplateDeclaration::getClangInst(Scope* sc, ::TemplateInstanc
610514
TypeMapper tymap;
611515
ExprMapper expmap(tymap);
612516
tymap.addImplicitDecls = false;
613-
tymap.cppPrefix = false;
614517

615518
auto Temp = const_cast<clang::RedeclarableTemplateDecl*>
616519
(getDefinition(getPrimaryTemplate(), false));
@@ -772,7 +675,6 @@ bool TemplateInstance::semanticTiargs(Scope* sc)
772675
TypeMapper tymap;
773676
ExprMapper expmap(tymap);
774677
tymap.addImplicitDecls = false;
775-
tymap.cppPrefix = false;
776678

777679
auto Args = getTemplateArgs(Inst)->asArray();
778680
auto Arg = Args.begin();
@@ -781,8 +683,6 @@ bool TemplateInstance::semanticTiargs(Scope* sc)
781683
SpecValue spec(tymap);
782684
getIdentifierOrNull(Temp, &spec);
783685

784-
auto sc2 = globalScope(sc->instantiatingModule());
785-
786686
for (size_t i = spec ? 1 : 0; i < tiargs->dim; Arg++, Param++)
787687
{
788688
auto NTTPD = dyn_cast<clang::NonTypeTemplateParmDecl>(*Param);
@@ -795,7 +695,7 @@ bool TemplateInstance::semanticTiargs(Scope* sc)
795695
for (auto arg: *a) {
796696
if (auto e = isExpression(arg)) {
797697
assert(isExpression((*tiargs)[i]));
798-
(*tiargs)[i] = e->semantic(sc2);
698+
(*tiargs)[i] = e->semantic(sc);
799699
}
800700
i++;
801701
}
@@ -861,7 +761,6 @@ void TemplateInstance::correctTiargs()
861761

862762
TypeMapper tymap;
863763
tymap.addImplicitDecls = false;
864-
tymap.substsyms = collectSymbols(tiargs);
865764

866765
primTiargs = tiargs;
867766
tiargs = TypeMapper::FromType(tymap, loc).fromTemplateArguments(Args.begin(), Args.end(),

0 commit comments

Comments
 (0)