ldc/ddmd/attrib.d
Kai Nacke 802923c06b Add new extern(C++, class) and extern(C++, struct) declarations.
VS has different name manglings for classes and structs. With the
new extern(C++, class) and extern(C++, struct) declarations, the
name mangling algorithm is changed to use a mangling different
from the used D entity.

A common use case is to map a value type modeled as class in C++ to
a struct in D.

This is backport of https://github.com/dlang/dmd/pull/5875.
2016-06-25 14:39:39 +02:00

1591 lines
46 KiB
D

// Compiler implementation of the D programming language
// Copyright (c) 1999-2015 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
// Distributed under the Boost Software License, Version 1.0.
// http://www.boost.org/LICENSE_1_0.txt
module ddmd.attrib;
import core.stdc.stdio;
import core.stdc.string;
import ddmd.aggregate;
import ddmd.arraytypes;
import ddmd.cond;
import ddmd.dclass;
import ddmd.declaration;
import ddmd.dinterpret;
import ddmd.dmodule;
import ddmd.dscope;
import ddmd.dstruct;
import ddmd.dsymbol;
import ddmd.dtemplate;
import ddmd.errors;
import ddmd.expression;
import ddmd.func;
import ddmd.globals;
import ddmd.hdrgen;
import ddmd.id;
import ddmd.identifier;
import ddmd.mars;
import ddmd.mtype;
import ddmd.parse;
import ddmd.root.outbuffer;
import ddmd.root.rmem;
import ddmd.tokens;
import ddmd.utf;
import ddmd.visitor;
version(IN_LLVM)
{
import gen.dpragma;
}
/***********************************************************
*/
extern (C++) class AttribDeclaration : Dsymbol
{
public:
Dsymbols* decl; // array of Dsymbol's
final extern (D) this(Dsymbols* decl)
{
this.decl = decl;
}
Dsymbols* include(Scope* sc, ScopeDsymbol sds)
{
return decl;
}
override final int apply(Dsymbol_apply_ft_t fp, void* param)
{
Dsymbols* d = include(_scope, null);
if (d)
{
for (size_t i = 0; i < d.dim; i++)
{
Dsymbol s = (*d)[i];
if (s)
{
if (s.apply(fp, param))
return 1;
}
}
}
return 0;
}
/****************************************
* Create a new scope if one or more given attributes
* are different from the sc's.
* If the returned scope != sc, the caller should pop
* the scope after it used.
*/
final static Scope* createNewScope(Scope* sc, StorageClass stc, LINK linkage, CPPMANGLE cppmangle, Prot protection, int explicitProtection, structalign_t structalign, PINLINE inlining)
{
Scope* sc2 = sc;
if (stc != sc.stc || linkage != sc.linkage || cppmangle != sc.cppmangle || !protection.isSubsetOf(sc.protection) || explicitProtection != sc.explicitProtection || structalign != sc.structalign || inlining != sc.inlining)
{
// create new one for changes
sc2 = sc.copy();
sc2.stc = stc;
sc2.linkage = linkage;
sc2.cppmangle = cppmangle;
sc2.protection = protection;
sc2.explicitProtection = explicitProtection;
sc2.structalign = structalign;
sc2.inlining = inlining;
}
return sc2;
}
/****************************************
* A hook point to supply scope for members.
* addMember, setScope, importAll, semantic, semantic2 and semantic3 will use this.
*/
Scope* newScope(Scope* sc)
{
return sc;
}
override void addMember(Scope* sc, ScopeDsymbol sds)
{
Dsymbols* d = include(sc, sds);
if (d)
{
Scope* sc2 = newScope(sc);
for (size_t i = 0; i < d.dim; i++)
{
Dsymbol s = (*d)[i];
//printf("\taddMember %s to %s\n", s->toChars(), sds->toChars());
s.addMember(sc2, sds);
}
if (sc2 != sc)
sc2.pop();
}
}
override void setScope(Scope* sc)
{
Dsymbols* d = include(sc, null);
//printf("\tAttribDeclaration::setScope '%s', d = %p\n",toChars(), d);
if (d)
{
Scope* sc2 = newScope(sc);
for (size_t i = 0; i < d.dim; i++)
{
Dsymbol s = (*d)[i];
s.setScope(sc2);
}
if (sc2 != sc)
sc2.pop();
}
}
override void importAll(Scope* sc)
{
Dsymbols* d = include(sc, null);
//printf("\tAttribDeclaration::importAll '%s', d = %p\n", toChars(), d);
if (d)
{
Scope* sc2 = newScope(sc);
for (size_t i = 0; i < d.dim; i++)
{
Dsymbol s = (*d)[i];
s.importAll(sc2);
}
if (sc2 != sc)
sc2.pop();
}
}
override void semantic(Scope* sc)
{
Dsymbols* d = include(sc, null);
//printf("\tAttribDeclaration::semantic '%s', d = %p\n",toChars(), d);
if (d)
{
Scope* sc2 = newScope(sc);
for (size_t i = 0; i < d.dim; i++)
{
Dsymbol s = (*d)[i];
s.semantic(sc2);
}
if (sc2 != sc)
sc2.pop();
}
}
override void semantic2(Scope* sc)
{
Dsymbols* d = include(sc, null);
if (d)
{
Scope* sc2 = newScope(sc);
for (size_t i = 0; i < d.dim; i++)
{
Dsymbol s = (*d)[i];
s.semantic2(sc2);
}
if (sc2 != sc)
sc2.pop();
}
}
override void semantic3(Scope* sc)
{
Dsymbols* d = include(sc, null);
if (d)
{
Scope* sc2 = newScope(sc);
for (size_t i = 0; i < d.dim; i++)
{
Dsymbol s = (*d)[i];
s.semantic3(sc2);
}
if (sc2 != sc)
sc2.pop();
}
}
override void addComment(const(char)* comment)
{
//printf("AttribDeclaration::addComment %s\n", comment);
if (comment)
{
Dsymbols* d = include(null, null);
if (d)
{
for (size_t i = 0; i < d.dim; i++)
{
Dsymbol s = (*d)[i];
//printf("AttribDeclaration::addComment %s\n", s->toChars());
s.addComment(comment);
}
}
}
}
override const(char)* kind() const
{
return "attribute";
}
override bool oneMember(Dsymbol* ps, Identifier ident)
{
Dsymbols* d = include(null, null);
return Dsymbol.oneMembers(d, ps, ident);
}
override void setFieldOffset(AggregateDeclaration ad, uint* poffset, bool isunion)
{
Dsymbols* d = include(null, null);
if (d)
{
for (size_t i = 0; i < d.dim; i++)
{
Dsymbol s = (*d)[i];
s.setFieldOffset(ad, poffset, isunion);
}
}
}
override final bool hasPointers()
{
Dsymbols* d = include(null, null);
if (d)
{
for (size_t i = 0; i < d.dim; i++)
{
Dsymbol s = (*d)[i];
if (s.hasPointers())
return true;
}
}
return false;
}
override final bool hasStaticCtorOrDtor()
{
Dsymbols* d = include(null, null);
if (d)
{
for (size_t i = 0; i < d.dim; i++)
{
Dsymbol s = (*d)[i];
if (s.hasStaticCtorOrDtor())
return true;
}
}
return false;
}
override final void checkCtorConstInit()
{
Dsymbols* d = include(null, null);
if (d)
{
for (size_t i = 0; i < d.dim; i++)
{
Dsymbol s = (*d)[i];
s.checkCtorConstInit();
}
}
}
/****************************************
*/
override final void addLocalClass(ClassDeclarations* aclasses)
{
Dsymbols* d = include(null, null);
if (d)
{
for (size_t i = 0; i < d.dim; i++)
{
Dsymbol s = (*d)[i];
s.addLocalClass(aclasses);
}
}
}
override final inout(AttribDeclaration) isAttribDeclaration() inout
{
return this;
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/***********************************************************
*/
extern (C++) class StorageClassDeclaration : AttribDeclaration
{
public:
StorageClass stc;
final extern (D) this(StorageClass stc, Dsymbols* decl)
{
super(decl);
this.stc = stc;
}
override Dsymbol syntaxCopy(Dsymbol s)
{
assert(!s);
return new StorageClassDeclaration(stc, Dsymbol.arraySyntaxCopy(decl));
}
override Scope* newScope(Scope* sc)
{
StorageClass scstc = sc.stc;
/* These sets of storage classes are mutually exclusive,
* so choose the innermost or most recent one.
*/
if (stc & (STCauto | STCscope | STCstatic | STCextern | STCmanifest))
scstc &= ~(STCauto | STCscope | STCstatic | STCextern | STCmanifest);
if (stc & (STCauto | STCscope | STCstatic | STCtls | STCmanifest | STCgshared))
scstc &= ~(STCauto | STCscope | STCstatic | STCtls | STCmanifest | STCgshared);
if (stc & (STCconst | STCimmutable | STCmanifest))
scstc &= ~(STCconst | STCimmutable | STCmanifest);
if (stc & (STCgshared | STCshared | STCtls))
scstc &= ~(STCgshared | STCshared | STCtls);
if (stc & (STCsafe | STCtrusted | STCsystem))
scstc &= ~(STCsafe | STCtrusted | STCsystem);
scstc |= stc;
//printf("scstc = x%llx\n", scstc);
return createNewScope(sc, scstc, sc.linkage, sc.cppmangle, sc.protection, sc.explicitProtection, sc.structalign, sc.inlining);
}
override final bool oneMember(Dsymbol* ps, Identifier ident)
{
bool t = Dsymbol.oneMembers(decl, ps, ident);
if (t && *ps)
{
/* This is to deal with the following case:
* struct Tick {
* template to(T) { const T to() { ... } }
* }
* For eponymous function templates, the 'const' needs to get attached to 'to'
* before the semantic analysis of 'to', so that template overloading based on the
* 'this' pointer can be successful.
*/
FuncDeclaration fd = (*ps).isFuncDeclaration();
if (fd)
{
/* Use storage_class2 instead of storage_class otherwise when we do .di generation
* we'll wind up with 'const const' rather than 'const'.
*/
/* Don't think we need to worry about mutually exclusive storage classes here
*/
fd.storage_class2 |= stc;
}
}
return t;
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/***********************************************************
*/
extern (C++) final class DeprecatedDeclaration : StorageClassDeclaration
{
public:
Expression msg;
const(char)* msgstr;
extern (D) this(Expression msg, Dsymbols* decl)
{
super(STCdeprecated, decl);
this.msg = msg;
}
override Dsymbol syntaxCopy(Dsymbol s)
{
assert(!s);
return new DeprecatedDeclaration(msg.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl));
}
/**
* Provides a new scope with `STCdeprecated` and `Scope.depdecl` set
*
* Calls `StorageClassDeclaration.newScope` (as it must be called or copied
* in any function overriding `newScope`), then set the `Scope`'s depdecl.
*
* Returns:
* Always a new scope, to use for this `DeprecatedDeclaration`'s members.
*/
override Scope* newScope(Scope* sc)
{
auto scx = super.newScope(sc);
// The enclosing scope is deprecated as well
if (scx == sc)
scx = sc.push();
scx.depdecl = this;
return scx;
}
override void setScope(Scope* sc)
{
//printf("DeprecatedDeclaration::setScope() %p\n", this);
if (decl)
Dsymbol.setScope(sc); // for forward reference
return AttribDeclaration.setScope(sc);
}
/**
* Run the DeprecatedDeclaration's semantic2 phase then its members.
*
* The message set via a `DeprecatedDeclaration` can be either of:
* - a string literal
* - an enum
* - a static immutable
* So we need to call ctfe to resolve it.
* Afterward forwards to the members' semantic2.
*/
override void semantic2(Scope* sc)
{
getMessage();
super.semantic2(sc);
}
const(char)* getMessage()
{
if (auto sc = _scope)
{
_scope = null;
sc = sc.startCTFE();
msg = msg.semantic(sc);
msg = resolveProperties(sc, msg);
sc = sc.endCTFE();
msg = msg.ctfeInterpret();
if (auto se = msg.toStringExp())
msgstr = se.toStringz();
else
msg.error("compile time constant expected, not '%s'", msg.toChars());
}
return msgstr;
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/***********************************************************
*/
extern (C++) final class LinkDeclaration : AttribDeclaration
{
public:
LINK linkage;
extern (D) this(LINK p, Dsymbols* decl)
{
super(decl);
//printf("LinkDeclaration(linkage = %d, decl = %p)\n", p, decl);
linkage = p;
}
override Dsymbol syntaxCopy(Dsymbol s)
{
assert(!s);
return new LinkDeclaration(linkage, Dsymbol.arraySyntaxCopy(decl));
}
override Scope* newScope(Scope* sc)
{
return createNewScope(sc, sc.stc, this.linkage, sc.cppmangle, sc.protection, sc.explicitProtection, sc.structalign, sc.inlining);
}
override const(char)* toChars() const
{
return "extern ()";
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/***********************************************************
*/
extern (C++) final class CPPMangleDeclaration : AttribDeclaration
{
CPPMANGLE cppmangle;
extern (D) this(CPPMANGLE p, Dsymbols* decl)
{
super(decl);
//printf("CPPMangleDeclaration(cppmangle = %d, decl = %p)\n", p, decl);
cppmangle = p;
}
override Dsymbol syntaxCopy(Dsymbol s)
{
assert(!s);
return new CPPMangleDeclaration(cppmangle, Dsymbol.arraySyntaxCopy(decl));
}
override Scope* newScope(Scope* sc)
{
return createNewScope(sc, sc.stc, LINKcpp, cppmangle, sc.protection, sc.explicitProtection, sc.structalign, sc.inlining);
}
override const(char)* toChars() const
{
return "extern ()";
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/***********************************************************
*/
extern (C++) final class ProtDeclaration : AttribDeclaration
{
public:
Prot protection;
Identifiers* pkg_identifiers;
/**
* Params:
* loc = source location of attribute token
* p = protection attribute data
* decl = declarations which are affected by this protection attribute
*/
extern (D) this(Loc loc, Prot p, Dsymbols* decl)
{
super(decl);
this.loc = loc;
this.protection = p;
//printf("decl = %p\n", decl);
}
/**
* Params:
* loc = source location of attribute token
* pkg_identifiers = list of identifiers for a qualified package name
* decl = declarations which are affected by this protection attribute
*/
extern (D) this(Loc loc, Identifiers* pkg_identifiers, Dsymbols* decl)
{
super(decl);
this.loc = loc;
this.protection.kind = PROTpackage;
this.protection.pkg = null;
this.pkg_identifiers = pkg_identifiers;
}
override Dsymbol syntaxCopy(Dsymbol s)
{
assert(!s);
if (protection.kind == PROTpackage)
return new ProtDeclaration(this.loc, pkg_identifiers, Dsymbol.arraySyntaxCopy(decl));
else
return new ProtDeclaration(this.loc, protection, Dsymbol.arraySyntaxCopy(decl));
}
override Scope* newScope(Scope* sc)
{
if (pkg_identifiers)
semantic(sc);
return createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, this.protection, 1, sc.structalign, sc.inlining);
}
override void addMember(Scope* sc, ScopeDsymbol sds)
{
if (pkg_identifiers)
{
Dsymbol tmp;
Package.resolve(pkg_identifiers, &tmp, null);
protection.pkg = tmp ? tmp.isPackage() : null;
pkg_identifiers = null;
}
if (protection.kind == PROTpackage && protection.pkg && sc._module)
{
Module m = sc._module;
Package pkg = m.parent ? m.parent.isPackage() : null;
if (!pkg || !protection.pkg.isAncestorPackageOf(pkg))
error("does not bind to one of ancestor packages of module '%s'", m.toPrettyChars(true));
}
return AttribDeclaration.addMember(sc, sds);
}
override const(char)* kind() const
{
return "protection attribute";
}
override const(char)* toPrettyChars(bool)
{
assert(protection.kind > PROTundefined);
OutBuffer buf;
buf.writeByte('\'');
protectionToBuffer(&buf, protection);
buf.writeByte('\'');
return buf.extractString();
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/***********************************************************
*/
extern (C++) final class AlignDeclaration : AttribDeclaration
{
public:
uint salign;
extern (D) this(uint sa, Dsymbols* decl)
{
super(decl);
salign = sa;
}
override Dsymbol syntaxCopy(Dsymbol s)
{
assert(!s);
return new AlignDeclaration(salign, Dsymbol.arraySyntaxCopy(decl));
}
override Scope* newScope(Scope* sc)
{
return createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.protection, sc.explicitProtection, this.salign, sc.inlining);
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/***********************************************************
*/
extern (C++) final class AnonDeclaration : AttribDeclaration
{
public:
bool isunion;
structalign_t alignment;
int sem; // 1 if successful semantic()
uint anonoffset; // offset of anonymous struct
uint anonstructsize; // size of anonymous struct
uint anonalignsize; // size of anonymous struct for alignment purposes
extern (D) this(Loc loc, bool isunion, Dsymbols* decl)
{
super(decl);
this.loc = loc;
this.isunion = isunion;
}
override Dsymbol syntaxCopy(Dsymbol s)
{
assert(!s);
return new AnonDeclaration(loc, isunion, Dsymbol.arraySyntaxCopy(decl));
}
override void setScope(Scope* sc)
{
super.setScope(sc);
alignment = sc.structalign;
}
override void semantic(Scope* sc)
{
//printf("\tAnonDeclaration::semantic %s %p\n", isunion ? "union" : "struct", this);
assert(sc.parent);
Dsymbol p = sc.parent.pastMixin();
AggregateDeclaration ad = p.isAggregateDeclaration();
if (!ad)
{
.error(loc, "%s can only be a part of an aggregate, not %s %s", kind(), p.kind(), p.toChars());
return;
}
alignment = sc.structalign;
if (decl)
{
sc = sc.push();
sc.stc &= ~(STCauto | STCscope | STCstatic | STCtls | STCgshared);
sc.inunion = isunion;
sc.flags = 0;
for (size_t i = 0; i < decl.dim; i++)
{
Dsymbol s = (*decl)[i];
s.semantic(sc);
}
sc = sc.pop();
}
}
override void setFieldOffset(AggregateDeclaration ad, uint* poffset, bool isunion)
{
//printf("\tAnonDeclaration::setFieldOffset %s %p\n", isunion ? "union" : "struct", this);
if (decl)
{
/* This works by treating an AnonDeclaration as an aggregate 'member',
* so in order to place that member we need to compute the member's
* size and alignment.
*/
size_t fieldstart = ad.fields.dim;
/* Hackishly hijack ad's structsize and alignsize fields
* for use in our fake anon aggregate member.
*/
uint savestructsize = ad.structsize;
uint savealignsize = ad.alignsize;
ad.structsize = 0;
ad.alignsize = 0;
uint offset = 0;
for (size_t i = 0; i < decl.dim; i++)
{
Dsymbol s = (*decl)[i];
s.setFieldOffset(ad, &offset, this.isunion);
if (this.isunion)
offset = 0;
}
anonstructsize = ad.structsize;
anonalignsize = ad.alignsize;
ad.structsize = savestructsize;
ad.alignsize = savealignsize;
if (fieldstart == ad.fields.dim)
{
/* Bugzilla 13613: If the fields in this->members had been already
* added in ad->fields, just update *poffset for the subsequent
* field offset calculation.
*/
*poffset = ad.structsize;
return;
}
// 0 sized structs are set to 1 byte
// TODO: is this corect hebavior?
if (anonstructsize == 0)
{
anonstructsize = 1;
anonalignsize = 1;
}
/* Given the anon 'member's size and alignment,
* go ahead and place it.
*/
anonoffset = AggregateDeclaration.placeField(
poffset,
anonstructsize, anonalignsize, alignment,
&ad.structsize, &ad.alignsize,
isunion);
// Add to the anon fields the base offset of this anonymous aggregate
//printf("anon fields, anonoffset = %d\n", anonoffset);
for (size_t i = fieldstart; i < ad.fields.dim; i++)
{
VarDeclaration v = ad.fields[i];
//printf("\t[%d] %s %d\n", i, v.toChars(), v.offset);
v.offset += anonoffset;
}
}
}
override const(char)* kind() const
{
return (isunion ? "anonymous union" : "anonymous struct");
}
override final inout(AnonDeclaration) isAnonDeclaration() inout
{
return this;
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/***********************************************************
*/
extern (C++) final class PragmaDeclaration : AttribDeclaration
{
public:
Expressions* args; // array of Expression's
extern (D) this(Loc loc, Identifier ident, Expressions* args, Dsymbols* decl)
{
super(decl);
this.loc = loc;
this.ident = ident;
this.args = args;
}
override Dsymbol syntaxCopy(Dsymbol s)
{
//printf("PragmaDeclaration::syntaxCopy(%s)\n", toChars());
assert(!s);
return new PragmaDeclaration(loc, ident, Expression.arraySyntaxCopy(args), Dsymbol.arraySyntaxCopy(decl));
}
override void semantic(Scope* sc)
{
// Should be merged with PragmaStatement
//printf("\tPragmaDeclaration::semantic '%s'\n",toChars());
version(IN_LLVM)
{
LDCPragma llvm_internal = LDCPragma.LLVMnone;
const(char)* arg1str = null;
}
if (ident == Id.msg)
{
if (args)
{
for (size_t i = 0; i < args.dim; i++)
{
Expression e = (*args)[i];
sc = sc.startCTFE();
e = e.semantic(sc);
e = resolveProperties(sc, e);
sc = sc.endCTFE();
// pragma(msg) is allowed to contain types as well as expressions
e = ctfeInterpretForPragmaMsg(e);
if (e.op == TOKerror)
{
errorSupplemental(loc, "while evaluating pragma(msg, %s)", (*args)[i].toChars());
return;
}
StringExp se = e.toStringExp();
if (se)
{
se = se.toUTF8(sc);
fprintf(stderr, "%.*s", cast(int)se.len, se.string);
}
else
fprintf(stderr, "%s", e.toChars());
}
fprintf(stderr, "\n");
}
goto Lnodecl;
}
else if (ident == Id.lib)
{
if (!args || args.dim != 1)
error("string expected for library name");
else
{
Expression e = (*args)[0];
sc = sc.startCTFE();
e = e.semantic(sc);
e = resolveProperties(sc, e);
sc = sc.endCTFE();
e = e.ctfeInterpret();
(*args)[0] = e;
if (e.op == TOKerror)
goto Lnodecl;
StringExp se = e.toStringExp();
if (!se)
error("string expected for library name, not '%s'", e.toChars());
else
{
char* name = cast(char*)mem.xmalloc(se.len + 1);
memcpy(name, se.string, se.len);
name[se.len] = 0;
if (global.params.verbose)
fprintf(global.stdmsg, "library %s\n", name);
if (global.params.moduleDeps && !global.params.moduleDepsFile)
{
OutBuffer* ob = global.params.moduleDeps;
Module imod = sc.instantiatingModule();
ob.writestring("depsLib ");
ob.writestring(imod.toPrettyChars());
ob.writestring(" (");
escapePath(ob, imod.srcfile.toChars());
ob.writestring(") : ");
ob.writestring(name);
ob.writenl();
}
mem.xfree(name);
}
}
goto Lnodecl;
}
else if (ident == Id.startaddress)
{
if (!args || args.dim != 1)
error("function name expected for start address");
else
{
/* Bugzilla 11980:
* resolveProperties and ctfeInterpret call are not necessary.
*/
Expression e = (*args)[0];
sc = sc.startCTFE();
e = e.semantic(sc);
sc = sc.endCTFE();
(*args)[0] = e;
Dsymbol sa = getDsymbol(e);
if (!sa || !sa.isFuncDeclaration())
error("function name expected for start address, not '%s'", e.toChars());
}
goto Lnodecl;
}
else if (ident == Id.Pinline)
{
goto Ldecl;
}
else if (ident == Id.mangle)
{
if (!args)
args = new Expressions();
if (args.dim != 1)
{
error("string expected for mangled name");
args.setDim(1);
(*args)[0] = new ErrorExp(); // error recovery
goto Ldecl;
}
Expression e = (*args)[0];
e = e.semantic(sc);
e = e.ctfeInterpret();
(*args)[0] = e;
if (e.op == TOKerror)
goto Ldecl;
StringExp se = e.toStringExp();
if (!se)
{
error("string expected for mangled name, not '%s'", e.toChars());
goto Ldecl;
}
if (!se.len)
{
error("zero-length string not allowed for mangled name");
goto Ldecl;
}
if (se.sz != 1)
{
error("mangled name characters can only be of type char");
goto Ldecl;
}
version (all)
{
/* Note: D language specification should not have any assumption about backend
* implementation. Ideally pragma(mangle) can accept a string of any content.
*
* Therefore, this validation is compiler implementation specific.
*/
for (size_t i = 0; i < se.len;)
{
char* p = se.string;
dchar c = p[i];
if (c < 0x80)
{
if (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c >= '0' && c <= '9' || c != 0 && strchr("$%().:?@[]_", c))
{
++i;
continue;
}
else
{
error("char 0x%02x not allowed in mangled name", c);
break;
}
}
if (const msg = utf_decodeChar(se.string, se.len, i, c))
{
error("%s", msg);
break;
}
if (!isUniAlpha(c))
{
error("char 0x%04x not allowed in mangled name", c);
break;
}
}
}
}
// IN_LLVM
else if ((llvm_internal = DtoGetPragma(sc, this, arg1str)) != LDCPragma.LLVMnone)
{
// nothing to do anymore
}
else if (global.params.ignoreUnsupportedPragmas)
{
if (global.params.verbose)
{
/* Print unrecognized pragmas
*/
fprintf(global.stdmsg, "pragma %s", ident.toChars());
if (args)
{
for (size_t i = 0; i < args.dim; i++)
{
Expression e = (*args)[i];
version(IN_LLVM)
{
// ignore errors in ignored pragmas.
global.gag++;
uint errors_save = global.errors;
}
sc = sc.startCTFE();
e = e.semantic(sc);
e = resolveProperties(sc, e);
sc = sc.endCTFE();
e = e.ctfeInterpret();
if (i == 0)
fprintf(global.stdmsg, " (");
else
fprintf(global.stdmsg, ",");
fprintf(global.stdmsg, "%s", e.toChars());
version(IN_LLVM)
{
// restore error state.
global.gag--;
global.errors = errors_save;
}
}
if (args.dim)
fprintf(global.stdmsg, ")");
}
fprintf(global.stdmsg, "\n");
}
static if (!IN_LLVM)
goto Lnodecl;
}
else
error("unrecognized pragma(%s)", ident.toChars());
Ldecl:
if (decl)
{
Scope* sc2 = newScope(sc);
for (size_t i = 0; i < decl.dim; i++)
{
Dsymbol s = (*decl)[i];
s.semantic(sc2);
if (ident == Id.mangle)
{
assert(args && args.dim == 1);
if (StringExp se = (*args)[0].toStringExp())
{
char* name = cast(char*)mem.xmalloc(se.len + 1);
memcpy(name, se.string, se.len);
name[se.len] = 0;
uint cnt = setMangleOverride(s, name);
if (cnt > 1)
error("can only apply to a single declaration");
}
}
// IN_LLVM: add else clause
else
{
DtoCheckPragma(this, s, llvm_internal, arg1str);
}
}
if (sc2 != sc)
sc2.pop();
}
return;
Lnodecl:
if (decl)
{
error("pragma is missing closing ';'");
goto Ldecl;
// do them anyway, to avoid segfaults.
}
}
override Scope* newScope(Scope* sc)
{
if (ident == Id.Pinline)
{
PINLINE inlining = PINLINEdefault;
if (!args || args.dim == 0)
inlining = PINLINEdefault;
else if (args.dim != 1)
{
error("one boolean expression expected for pragma(inline), not %d", args.dim);
args.setDim(1);
(*args)[0] = new ErrorExp();
}
else
{
Expression e = (*args)[0];
if (e.op != TOKint64 || !e.type.equals(Type.tbool))
{
if (e.op != TOKerror)
{
error("pragma(inline, true or false) expected, not %s", e.toChars());
(*args)[0] = new ErrorExp();
}
}
else if (e.isBool(true))
inlining = PINLINEalways;
else if (e.isBool(false))
inlining = PINLINEnever;
}
return createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.protection, sc.explicitProtection, sc.structalign, inlining);
}
else if (IN_LLVM && ident == Id.LDC_profile_instr) {
bool emitInstr = true;
if (!args || args.dim != 1 || !DtoCheckProfileInstrPragma((*args)[0], emitInstr)) {
error("pragma(LDC_profile_instr, true or false) expected");
(*args)[0] = new ErrorExp();
} else {
// Only create a new scope if the emitInstrumentation flag is changed
if (sc.emitInstrumentation != emitInstr) {
auto newscope = sc.copy();
newscope.emitInstrumentation = emitInstr;
return newscope;
}
}
}
return sc;
}
override const(char)* kind() const
{
return "pragma";
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/***********************************************************
*/
extern (C++) class ConditionalDeclaration : AttribDeclaration
{
public:
Condition condition;
Dsymbols* elsedecl; // array of Dsymbol's for else block
final extern (D) this(Condition condition, Dsymbols* decl, Dsymbols* elsedecl)
{
super(decl);
//printf("ConditionalDeclaration::ConditionalDeclaration()\n");
this.condition = condition;
this.elsedecl = elsedecl;
}
override Dsymbol syntaxCopy(Dsymbol s)
{
assert(!s);
return new ConditionalDeclaration(condition.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl), Dsymbol.arraySyntaxCopy(elsedecl));
}
override final bool oneMember(Dsymbol* ps, Identifier ident)
{
//printf("ConditionalDeclaration::oneMember(), inc = %d\n", condition->inc);
if (condition.inc)
{
Dsymbols* d = condition.include(null, null) ? decl : elsedecl;
return Dsymbol.oneMembers(d, ps, ident);
}
else
{
bool res = (Dsymbol.oneMembers(decl, ps, ident) && *ps is null && Dsymbol.oneMembers(elsedecl, ps, ident) && *ps is null);
*ps = null;
return res;
}
}
// Decide if 'then' or 'else' code should be included
override Dsymbols* include(Scope* sc, ScopeDsymbol sds)
{
//printf("ConditionalDeclaration::include(sc = %p) scope = %p\n", sc, scope);
assert(condition);
return condition.include(_scope ? _scope : sc, sds) ? decl : elsedecl;
}
override final void addComment(const(char)* comment)
{
/* Because addComment is called by the parser, if we called
* include() it would define a version before it was used.
* But it's no problem to drill down to both decl and elsedecl,
* so that's the workaround.
*/
if (comment)
{
Dsymbols* d = decl;
for (int j = 0; j < 2; j++)
{
if (d)
{
for (size_t i = 0; i < d.dim; i++)
{
Dsymbol s = (*d)[i];
//printf("ConditionalDeclaration::addComment %s\n", s->toChars());
s.addComment(comment);
}
}
d = elsedecl;
}
}
}
override void setScope(Scope* sc)
{
Dsymbols* d = include(sc, null);
//printf("\tConditionalDeclaration::setScope '%s', d = %p\n",toChars(), d);
if (d)
{
for (size_t i = 0; i < d.dim; i++)
{
Dsymbol s = (*d)[i];
s.setScope(sc);
}
}
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/***********************************************************
*/
extern (C++) final class StaticIfDeclaration : ConditionalDeclaration
{
public:
ScopeDsymbol scopesym;
int addisdone;
extern (D) this(Condition condition, Dsymbols* decl, Dsymbols* elsedecl)
{
super(condition, decl, elsedecl);
//printf("StaticIfDeclaration::StaticIfDeclaration()\n");
}
override Dsymbol syntaxCopy(Dsymbol s)
{
assert(!s);
return new StaticIfDeclaration(condition.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl), Dsymbol.arraySyntaxCopy(elsedecl));
}
/****************************************
* Different from other AttribDeclaration subclasses, include() call requires
* the completion of addMember and setScope phases.
*/
override Dsymbols* include(Scope* sc, ScopeDsymbol sds)
{
//printf("StaticIfDeclaration::include(sc = %p) scope = %p\n", sc, scope);
if (condition.inc == 0)
{
assert(scopesym); // addMember is already done
assert(_scope); // setScope is already done
Dsymbols* d = ConditionalDeclaration.include(_scope, scopesym);
if (d && !addisdone)
{
// Add members lazily.
for (size_t i = 0; i < d.dim; i++)
{
Dsymbol s = (*d)[i];
s.addMember(_scope, scopesym);
}
// Set the member scopes lazily.
for (size_t i = 0; i < d.dim; i++)
{
Dsymbol s = (*d)[i];
s.setScope(_scope);
}
addisdone = 1;
}
return d;
}
else
{
return ConditionalDeclaration.include(sc, scopesym);
}
}
override void addMember(Scope* sc, ScopeDsymbol sds)
{
//printf("StaticIfDeclaration::addMember() '%s'\n", toChars());
/* This is deferred until the condition evaluated later (by the include() call),
* so that expressions in the condition can refer to declarations
* in the same scope, such as:
*
* template Foo(int i)
* {
* const int j = i + 1;
* static if (j == 3)
* const int k;
* }
*/
this.scopesym = sds;
}
override void semantic(Scope* sc)
{
AttribDeclaration.semantic(sc);
}
override void importAll(Scope* sc)
{
// do not evaluate condition before semantic pass
}
override void setScope(Scope* sc)
{
// do not evaluate condition before semantic pass
// But do set the scope, in case we need it for forward referencing
Dsymbol.setScope(sc);
}
override const(char)* kind() const
{
return "static if";
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/***********************************************************
* Mixin declarations, like:
* mixin("int x");
*/
extern (C++) final class CompileDeclaration : AttribDeclaration
{
public:
Expression exp;
ScopeDsymbol scopesym;
int compiled;
extern (D) this(Loc loc, Expression exp)
{
super(null);
//printf("CompileDeclaration(loc = %d)\n", loc.linnum);
this.loc = loc;
this.exp = exp;
}
override Dsymbol syntaxCopy(Dsymbol s)
{
//printf("CompileDeclaration::syntaxCopy('%s')\n", toChars());
return new CompileDeclaration(loc, exp.syntaxCopy());
}
override void addMember(Scope* sc, ScopeDsymbol sds)
{
//printf("CompileDeclaration::addMember(sc = %p, sds = %p, memnum = %d)\n", sc, sds, memnum);
this.scopesym = sds;
}
override void setScope(Scope* sc)
{
Dsymbol.setScope(sc);
}
void compileIt(Scope* sc)
{
//printf("CompileDeclaration::compileIt(loc = %d) %s\n", loc.linnum, exp->toChars());
sc = sc.startCTFE();
exp = exp.semantic(sc);
exp = resolveProperties(sc, exp);
sc = sc.endCTFE();
if (exp.op != TOKerror)
{
Expression e = exp.ctfeInterpret();
StringExp se = e.toStringExp();
if (!se)
exp.error("argument to mixin must be a string, not (%s) of type %s", exp.toChars(), exp.type.toChars());
else
{
se = se.toUTF8(sc);
uint errors = global.errors;
auto cstr = se.toStringz();
scope Parser p = new Parser(loc, sc._module, cstr, se.len, 0);
p.nextToken();
decl = p.parseDeclDefs(0);
if (p.token.value != TOKeof)
exp.error("incomplete mixin declaration (%s)", se.toChars());
if (p.errors)
{
assert(global.errors != errors);
decl = null;
}
}
}
}
override void semantic(Scope* sc)
{
//printf("CompileDeclaration::semantic()\n");
if (!compiled)
{
compileIt(sc);
AttribDeclaration.addMember(sc, scopesym);
compiled = 1;
if (_scope && decl)
{
for (size_t i = 0; i < decl.dim; i++)
{
Dsymbol s = (*decl)[i];
s.setScope(_scope);
}
}
}
AttribDeclaration.semantic(sc);
}
override const(char)* kind() const
{
return "mixin";
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/***********************************************************
* User defined attributes look like:
* @(args, ...)
*/
extern (C++) final class UserAttributeDeclaration : AttribDeclaration
{
public:
Expressions* atts;
extern (D) this(Expressions* atts, Dsymbols* decl)
{
super(decl);
//printf("UserAttributeDeclaration()\n");
this.atts = atts;
}
override Dsymbol syntaxCopy(Dsymbol s)
{
//printf("UserAttributeDeclaration::syntaxCopy('%s')\n", toChars());
assert(!s);
return new UserAttributeDeclaration(Expression.arraySyntaxCopy(this.atts), Dsymbol.arraySyntaxCopy(decl));
}
override Scope* newScope(Scope* sc)
{
Scope* sc2 = sc;
if (atts && atts.dim)
{
// create new one for changes
sc2 = sc.copy();
sc2.userAttribDecl = this;
}
return sc2;
}
override void semantic(Scope* sc)
{
//printf("UserAttributeDeclaration::semantic() %p\n", this);
if (decl && !_scope)
Dsymbol.setScope(sc); // for function local symbols
return AttribDeclaration.semantic(sc);
}
override void semantic2(Scope* sc)
{
if (decl && atts && atts.dim)
{
if (atts && atts.dim && _scope)
{
_scope = null;
arrayExpressionSemantic(atts, sc, true); // run semantic
}
}
AttribDeclaration.semantic2(sc);
}
override void setScope(Scope* sc)
{
//printf("UserAttributeDeclaration::setScope() %p\n", this);
if (decl)
Dsymbol.setScope(sc); // for forward reference of UDAs
return AttribDeclaration.setScope(sc);
}
static Expressions* concat(Expressions* udas1, Expressions* udas2)
{
Expressions* udas;
if (!udas1 || udas1.dim == 0)
udas = udas2;
else if (!udas2 || udas2.dim == 0)
udas = udas1;
else
{
/* Create a new tuple that combines them
* (do not append to left operand, as this is a copy-on-write operation)
*/
udas = new Expressions();
udas.push(new TupleExp(Loc(), udas1));
udas.push(new TupleExp(Loc(), udas2));
}
return udas;
}
Expressions* getAttributes()
{
if (auto sc = _scope)
{
_scope = null;
arrayExpressionSemantic(atts, sc);
}
auto exps = new Expressions();
if (userAttribDecl)
exps.push(new TupleExp(Loc(), userAttribDecl.getAttributes()));
if (atts && atts.dim)
exps.push(new TupleExp(Loc(), atts));
return exps;
}
override const(char)* kind() const
{
return "UserAttribute";
}
override void accept(Visitor v)
{
v.visit(this);
}
}
extern (C++) static uint setMangleOverride(Dsymbol s, char* sym)
{
AttribDeclaration ad = s.isAttribDeclaration();
if (ad)
{
Dsymbols* decls = ad.include(null, null);
uint nestedCount = 0;
if (decls && decls.dim)
for (size_t i = 0; i < decls.dim; ++i)
nestedCount += setMangleOverride((*decls)[i], sym);
return nestedCount;
}
else if (s.isFuncDeclaration() || s.isVarDeclaration())
{
s.isDeclaration().mangleOverride = sym;
return 1;
}
else
return 0;
}