Skip to content

Commit

Permalink
Merge pull request #8787 from TurkeyMan/namespace_expr
Browse files Browse the repository at this point in the history
Fix issue 19278 - extern(C++, "name") doesn't accept expressions
merged-on-behalf-of: Mathias LANG <pro.mathias.lang@gmail.com>
  • Loading branch information
dlang-bot committed Nov 5, 2018
2 parents 3d4fe18 + db36d97 commit 1965a43
Show file tree
Hide file tree
Showing 11 changed files with 197 additions and 84 deletions.
8 changes: 7 additions & 1 deletion src/dmd/astbase.d
Expand Up @@ -1164,11 +1164,17 @@ struct ASTBase
*/
bool mangleOnly;

extern (D) this(const ref Loc loc, Identifier ident, Dsymbols* members, bool mangleOnly)
/**
* Namespace identifier resolved during semantic.
*/
Expression identExp;

extern (D) this(const ref Loc loc, Identifier ident, Expression identExp, Dsymbols* members, bool mangleOnly)
{
super(ident);
this.loc = loc;
this.members = members;
this.identExp = identExp;
this.mangleOnly = mangleOnly;
}

Expand Down
64 changes: 64 additions & 0 deletions src/dmd/dsymbolsem.d
Expand Up @@ -2831,6 +2831,68 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
if (!sc)
return;

bool repopulateMembers = false;
if (ns.identExp)
{
// resolve the namespace identifier
sc = sc.startCTFE();
Expression resolved = ns.identExp.expressionSemantic(sc);
resolved = resolveProperties(sc, resolved);
sc = sc.endCTFE();
resolved = resolved.ctfeInterpret();
StringExp name = resolved.toStringExp();
TupleExp tup = name ? null : resolved.toTupleExp();
if (!tup && !name)
{
error(ns.loc, "expected string expression for namespace name, got `%s`", ns.identExp.toChars());
return;
}
ns.identExp = resolved; // we don't need to keep the old AST around
if (name)
{
const(char)[] ident = name.toStringz();
if (ident.length == 0 || !Identifier.isValidIdentifier(ident))
{
error(ns.loc, "expected valid identifer for C++ namespace but got `%.*s`", cast(int)ident.length, ident.ptr);
return;
}
ns.ident = Identifier.idPool(ident);
}
else
{
// create namespace stack from the tuple
Nspace parentns = ns;
foreach (i, exp; *tup.exps)
{
name = exp.toStringExp();
if (!name)
{
error(ns.loc, "expected string expression for namespace name, got `%s`", exp.toChars());
return;
}
const(char)[] ident = name.toStringz();
if (ident.length == 0 || !Identifier.isValidIdentifier(ident))
{
error(ns.loc, "expected valid identifer for C++ namespace but got `%.*s`", cast(int)ident.length, ident.ptr);
return;
}
if (i == 0)
{
ns.ident = Identifier.idPool(ident);
}
else
{
// insert the new namespace
Nspace childns = new Nspace(ns.loc, Identifier.idPool(ident), null, parentns.members, ns.mangleOnly);
parentns.members = new Dsymbols;
parentns.members.push(childns);
parentns = childns;
repopulateMembers = true;
}
}
}
}

ns.semanticRun = PASS.semantic;
ns.parent = sc.parent;
if (ns.members)
Expand All @@ -2841,6 +2903,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
sc.parent = ns;
foreach (s; *ns.members)
{
if (repopulateMembers)
s.setScope(sc);
s.importAll(sc);
}
foreach (s; *ns.members)
Expand Down
10 changes: 10 additions & 0 deletions src/dmd/expression.d
Expand Up @@ -839,6 +839,11 @@ extern (C++) abstract class Expression : RootObject
return null;
}

TupleExp toTupleExp()
{
return null;
}

/***************************************
* Return !=0 if expression is an lvalue.
*/
Expand Down Expand Up @@ -2537,6 +2542,11 @@ extern (C++) final class TupleExp : Expression
}
}

override TupleExp toTupleExp()
{
return this;
}

override Expression syntaxCopy()
{
return new TupleExp(loc, e0 ? e0.syntaxCopy() : null, arraySyntaxCopy(exps));
Expand Down
2 changes: 2 additions & 0 deletions src/dmd/expression.h
Expand Up @@ -89,6 +89,7 @@ class Expression : public RootObject
virtual real_t toImaginary();
virtual complex_t toComplex();
virtual StringExp *toStringExp();
virtual TupleExp *toTupleExp();
virtual bool isLvalue();
virtual Expression *toLvalue(Scope *sc, Expression *e);
virtual Expression *modifiableLvalue(Scope *sc, Expression *e);
Expand Down Expand Up @@ -282,6 +283,7 @@ class TupleExp : public Expression
*/
Expressions *exps;

TupleExp *toTupleExp();
Expression *syntaxCopy();
bool equals(RootObject *o);

Expand Down
11 changes: 9 additions & 2 deletions src/dmd/nspace.d
Expand Up @@ -17,6 +17,7 @@ import dmd.arraytypes;
import dmd.dscope;
import dmd.dsymbol;
import dmd.dsymbolsem;
import dmd.expression;
import dmd.globals;
import dmd.identifier;
import dmd.visitor;
Expand All @@ -35,18 +36,24 @@ extern (C++) final class Nspace : ScopeDsymbol
*/
bool mangleOnly;

extern (D) this(const ref Loc loc, Identifier ident, Dsymbols* members, bool mangleOnly)
/**
* Namespace identifier resolved during semantic.
*/
Expression identExp;

extern (D) this(const ref Loc loc, Identifier ident, Expression identExp, Dsymbols* members, bool mangleOnly)
{
super(ident);
//printf("Nspace::Nspace(ident = %s)\n", ident.toChars());
this.loc = loc;
this.members = members;
this.identExp = identExp;
this.mangleOnly = mangleOnly;
}

override Dsymbol syntaxCopy(Dsymbol s)
{
auto ns = new Nspace(loc, ident, null, mangleOnly);
auto ns = new Nspace(loc, ident, identExp, null, mangleOnly);
return ScopeDsymbol.syntaxCopy(ns);
}

Expand Down
1 change: 1 addition & 0 deletions src/dmd/nspace.h
Expand Up @@ -20,6 +20,7 @@ class Nspace : public ScopeDsymbol
{
public:
bool mangleOnly;
Expression *identExp;
Dsymbol *syntaxCopy(Dsymbol *s);
void addMember(Scope *sc, ScopeDsymbol *sds);
void setScope(Scope *sc);
Expand Down
96 changes: 44 additions & 52 deletions src/dmd/parse.d
Expand Up @@ -867,16 +867,17 @@ final class Parser(AST) : Lexer

const linkLoc = token.loc;
AST.Identifiers* idents = null;
AST.Expressions* identExps = null;
CPPMANGLE cppmangle;
bool cppMangleOnly = false;
const link = parseLinkage(&idents, cppmangle, cppMangleOnly);
const link = parseLinkage(&idents, &identExps, cppmangle, cppMangleOnly);
if (pAttrs.link != LINK.default_)
{
if (pAttrs.link != link)
{
error("conflicting linkage `extern (%s)` and `extern (%s)`", AST.linkageToChars(pAttrs.link), AST.linkageToChars(link));
}
else if (idents)
else if (idents || identExps)
{
// Allow:
// extern(C++, foo) extern(C++, bar) void foo();
Expand All @@ -901,7 +902,23 @@ final class Parser(AST) : Lexer
a = new AST.Dsymbols();
a.push(s);
}
s = new AST.Nspace(linkLoc, id, a, cppMangleOnly);
s = new AST.Nspace(linkLoc, id, null, a, cppMangleOnly);
}
pAttrs.link = LINK.default_;
}
else if (identExps)
{
assert(link == LINK.cpp);
assert(identExps.dim);
for (size_t i = identExps.dim; i;)
{
AST.Expression exp = (*identExps)[--i];
if (s)
{
a = new AST.Dsymbols();
a.push(s);
}
s = new AST.Nspace(linkLoc, null, exp, a, cppMangleOnly);
}
pAttrs.link = LINK.default_;
}
Expand Down Expand Up @@ -2117,11 +2134,13 @@ final class Parser(AST) : Lexer
* extern (linkage)
* extern (C++, namespaces)
* extern (C++, "namespace", "namespaces", ...)
* extern (C++, (StringExp))
* The parser is on the 'extern' token.
*/
LINK parseLinkage(AST.Identifiers** pidents, out CPPMANGLE cppmangle, out bool cppMangleOnly)
LINK parseLinkage(AST.Identifiers** pidents, AST.Expressions** pIdentExps, out CPPMANGLE cppmangle, out bool cppMangleOnly)
{
AST.Identifiers* idents = null;
AST.Expressions* identExps = null;
cppmangle = CPPMANGLE.def;
LINK link = LINK.default_;
nextToken();
Expand Down Expand Up @@ -2152,64 +2171,35 @@ final class Parser(AST) : Lexer
cppmangle = token.value == TOK.class_ ? CPPMANGLE.asClass : CPPMANGLE.asStruct;
nextToken();
}
else if (token.value == TOK.string_) // extern(C++, "namespace", "namespaces")
else if (token.value == TOK.identifier) // named scope namespace
{
cppMangleOnly = true;
idents = new AST.Identifiers();

while (1)
{
AST.StringExp stringExp = cast(AST.StringExp)parsePrimaryExp();
const(char)[] name = stringExp.toStringz();
if (name.length == 0)
{
error("invalid zero length C++ namespace");
idents = null;
break;
}
else if (!Identifier.isValidIdentifier(name))
{
error("expected valid identifer for C++ namespace but got `%s`", name.ptr);
idents = null;
break;
}
idents.push(Identifier.idPool(name));
if (token.value == TOK.comma)
Identifier idn = token.ident;
idents.push(idn);
nextToken();
if (token.value == TOK.dot)
{
nextToken();
if (token.value != TOK.string_)
{
error("string expected following `,` for C++ namespace, not `%s`", token.toChars());
idents = null;
break;
}
if (token.value == TOK.identifier)
continue;
error("identifier expected for C++ namespace");
idents = null; // error occurred, invalidate list of elements.
}
else
break;
break;
}
}
else
else // non-scoped StringExp namespace
{
idents = new AST.Identifiers();
cppMangleOnly = true;
identExps = new AST.Expressions();
while (1)
{
if (token.value == TOK.identifier)
{
Identifier idn = token.ident;
idents.push(idn);
nextToken();
if (token.value == TOK.dot)
{
nextToken();
continue;
}
}
else
{
error("identifier expected for C++ namespace");
idents = null; // error occurred, invalidate list of elements.
}
break;
identExps.push(parseCondExp());
if (token.value != TOK.comma)
break;
nextToken();
}
}
}
Expand Down Expand Up @@ -2248,6 +2238,7 @@ final class Parser(AST) : Lexer
}
check(TOK.rightParentheses);
*pidents = idents;
*pIdentExps = identExps;
return link;
}

Expand Down Expand Up @@ -4249,10 +4240,11 @@ final class Parser(AST) : Lexer
error("redundant linkage declaration");
sawLinkage = true;
AST.Identifiers* idents = null;
AST.Expressions* identExps = null;
CPPMANGLE cppmangle;
bool cppMangleOnly = false;
link = parseLinkage(&idents, cppmangle, cppMangleOnly);
if (idents)
link = parseLinkage(&idents, &identExps, cppmangle, cppMangleOnly);
if (idents || identExps)
{
error("C++ name spaces not allowed here");
}
Expand Down
38 changes: 38 additions & 0 deletions test/compilable/cppmangle.d
Expand Up @@ -935,3 +935,41 @@ version (Posix) extern (C++)
static assert(func16479_17_1!int.mangleof == `_Z14func16479_17_1IiEPN7fakestd3__111vector16479IT_NS1_14allocator16479IS3_EEEEv`);
static assert(func16479_17_2!int.mangleof == `_Z14func16479_17_2IiEPN7fakestd3__111vector16479IT_NS1_14allocator16479IS3_EEEEv`);
}

/**************************************/
// https://issues.dlang.org/show_bug.cgi?id=19278
// extern(C++, "name") doesn't accept expressions

extern(C++, "hello" ~ "world")
{
void test19278();
}
enum NS = "lookup";
extern(C++, (NS))
{
void test19278_2();
}
alias AliasSeq(Args...) = Args;
alias Tup = AliasSeq!("hello", "world");
extern(C++, (Tup))
{
void test19278_3();
}
extern(C++, (AliasSeq!(Tup, "yay")))
{
void test19278_4();
}
version(Win64)
{
static assert(test19278.mangleof == "?test19278@helloworld@@YAXXZ");
static assert(test19278_2.mangleof == "?test19278_2@lookup@@YAXXZ");
static assert(test19278_3.mangleof == "?test19278_3@world@hello@@YAXXZ");
static assert(test19278_4.mangleof == "?test19278_4@yay@world@hello@@YAXXZ");
}
else version(Posix)
{
static assert(test19278.mangleof == "_ZN10helloworld9test19278Ev");
static assert(test19278_2.mangleof == "_ZN6lookup11test19278_2Ev");
static assert(test19278_3.mangleof == "_ZN5hello5world11test19278_3Ev");
static assert(test19278_4.mangleof == "_ZN5hello5world3yay11test19278_4Ev");
}

0 comments on commit 1965a43

Please sign in to comment.