mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-05 01:20:51 +03:00
2826 lines
90 KiB
D
2826 lines
90 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.declaration;
|
|
|
|
import core.stdc.stdio;
|
|
import ddmd.aggregate;
|
|
import ddmd.arraytypes;
|
|
import ddmd.attrib;
|
|
import ddmd.cppmangle;
|
|
import ddmd.ctfeexpr;
|
|
import ddmd.dcast;
|
|
import ddmd.dclass;
|
|
import ddmd.declaration;
|
|
import ddmd.delegatize;
|
|
import ddmd.dinterpret;
|
|
import ddmd.dmangle;
|
|
import ddmd.doc;
|
|
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.init;
|
|
import ddmd.inline;
|
|
import ddmd.intrange;
|
|
import ddmd.mtype;
|
|
import ddmd.opover;
|
|
import ddmd.root.outbuffer;
|
|
import ddmd.root.rootobject;
|
|
import ddmd.statement;
|
|
import ddmd.target;
|
|
import ddmd.tokens;
|
|
import ddmd.visitor;
|
|
|
|
/************************************
|
|
* Check to see the aggregate type is nested and its context pointer is
|
|
* accessible from the current scope.
|
|
* Returns true if error occurs.
|
|
*/
|
|
extern (C++) bool checkFrameAccess(Loc loc, Scope* sc, AggregateDeclaration ad, size_t iStart = 0)
|
|
{
|
|
Dsymbol sparent = ad.toParent2();
|
|
Dsymbol s = sc.func;
|
|
if (ad.isNested() && s)
|
|
{
|
|
//printf("ad = %p %s [%s], parent:%p\n", ad, ad.toChars(), ad.loc.toChars(), ad.parent);
|
|
//printf("sparent = %p %s [%s], parent: %s\n", sparent, sparent.toChars(), sparent.loc.toChars(), sparent.parent,toChars());
|
|
if (checkNestedRef(s, sparent))
|
|
{
|
|
error(loc, "cannot access frame pointer of %s", ad.toPrettyChars());
|
|
return true;
|
|
}
|
|
}
|
|
bool result = false;
|
|
for (size_t i = iStart; i < ad.fields.dim; i++)
|
|
{
|
|
VarDeclaration vd = ad.fields[i];
|
|
Type tb = vd.type.baseElemOf();
|
|
if (tb.ty == Tstruct)
|
|
{
|
|
result |= checkFrameAccess(loc, sc, (cast(TypeStruct)tb).sym);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/******************************************
|
|
*/
|
|
extern (C++) void ObjectNotFound(Identifier id)
|
|
{
|
|
Type.error(Loc(), "%s not found. object.d may be incorrectly installed or corrupt.", id.toChars());
|
|
fatal();
|
|
}
|
|
|
|
enum STCundefined = 0L;
|
|
enum STCstatic = (1L << 0);
|
|
enum STCextern = (1L << 1);
|
|
enum STCconst = (1L << 2);
|
|
enum STCfinal = (1L << 3);
|
|
enum STCabstract = (1L << 4);
|
|
enum STCparameter = (1L << 5);
|
|
enum STCfield = (1L << 6);
|
|
enum STCoverride = (1L << 7);
|
|
enum STCauto = (1L << 8);
|
|
enum STCsynchronized = (1L << 9);
|
|
enum STCdeprecated = (1L << 10);
|
|
enum STCin = (1L << 11); // in parameter
|
|
enum STCout = (1L << 12); // out parameter
|
|
enum STClazy = (1L << 13); // lazy parameter
|
|
enum STCforeach = (1L << 14); // variable for foreach loop
|
|
// (1L << 15)
|
|
enum STCvariadic = (1L << 16); // variadic function argument
|
|
enum STCctorinit = (1L << 17); // can only be set inside constructor
|
|
enum STCtemplateparameter = (1L << 18); // template parameter
|
|
enum STCscope = (1L << 19);
|
|
enum STCimmutable = (1L << 20);
|
|
enum STCref = (1L << 21);
|
|
enum STCinit = (1L << 22); // has explicit initializer
|
|
enum STCmanifest = (1L << 23); // manifest constant
|
|
enum STCnodtor = (1L << 24); // don't run destructor
|
|
enum STCnothrow = (1L << 25); // never throws exceptions
|
|
enum STCpure = (1L << 26); // pure function
|
|
enum STCtls = (1L << 27); // thread local
|
|
enum STCalias = (1L << 28); // alias parameter
|
|
enum STCshared = (1L << 29); // accessible from multiple threads
|
|
enum STCgshared = (1L << 30); // accessible from multiple threads, but not typed as "shared"
|
|
enum STCwild = (1L << 31); // for "wild" type constructor
|
|
enum STCproperty = (1L << 32);
|
|
enum STCsafe = (1L << 33);
|
|
enum STCtrusted = (1L << 34);
|
|
enum STCsystem = (1L << 35);
|
|
enum STCctfe = (1L << 36); // can be used in CTFE, even if it is static
|
|
enum STCdisable = (1L << 37); // for functions that are not callable
|
|
enum STCresult = (1L << 38); // for result variables passed to out contracts
|
|
enum STCnodefaultctor = (1L << 39); // must be set inside constructor
|
|
enum STCtemp = (1L << 40); // temporary variable
|
|
enum STCrvalue = (1L << 41); // force rvalue for variables
|
|
enum STCnogc = (1L << 42); // @nogc
|
|
enum STCvolatile = (1L << 43); // destined for volatile in the back end
|
|
enum STCreturn = (1L << 44); // 'return ref' for function parameters
|
|
enum STCautoref = (1L << 45); // Mark for the already deduced 'auto ref' parameter
|
|
enum STCinference = (1L << 46); // do attribute inference
|
|
|
|
enum STC_TYPECTOR = (STCconst | STCimmutable | STCshared | STCwild);
|
|
enum STC_FUNCATTR = (STCref | STCnothrow | STCnogc | STCpure | STCproperty | STCsafe | STCtrusted | STCsystem);
|
|
|
|
extern (C++) __gshared const(StorageClass) STCStorageClass =
|
|
(STCauto | STCscope | STCstatic | STCextern | STCconst | STCfinal | STCabstract | STCsynchronized | STCdeprecated | STCoverride | STClazy | STCalias | STCout | STCin | STCmanifest | STCimmutable | STCshared | STCwild | STCnothrow | STCnogc | STCpure | STCref | STCtls | STCgshared | STCproperty | STCsafe | STCtrusted | STCsystem | STCdisable);
|
|
|
|
struct Match
|
|
{
|
|
int count; // number of matches found
|
|
MATCH last; // match level of lastf
|
|
FuncDeclaration lastf; // last matching function we found
|
|
FuncDeclaration nextf; // current matching function
|
|
FuncDeclaration anyf; // pick a func, any func, to use for error recovery
|
|
}
|
|
|
|
enum Semantic : int
|
|
{
|
|
SemanticStart, // semantic has not been run
|
|
SemanticIn, // semantic() is in progress
|
|
SemanticDone, // semantic() has been run
|
|
Semantic2Done, // semantic2() has been run
|
|
}
|
|
|
|
alias SemanticStart = Semantic.SemanticStart;
|
|
alias SemanticIn = Semantic.SemanticIn;
|
|
alias SemanticDone = Semantic.SemanticDone;
|
|
alias Semantic2Done = Semantic.Semantic2Done;
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) class Declaration : Dsymbol
|
|
{
|
|
public:
|
|
Type type;
|
|
Type originalType; // before semantic analysis
|
|
StorageClass storage_class;
|
|
Prot protection;
|
|
LINK linkage;
|
|
int inuse; // used to detect cycles
|
|
|
|
// overridden symbol with pragma(mangle, "...")
|
|
const(char)* mangleOverride;
|
|
|
|
Semantic sem;
|
|
|
|
final extern (D) this(Identifier id)
|
|
{
|
|
super(id);
|
|
storage_class = STCundefined;
|
|
protection = Prot(PROTundefined);
|
|
linkage = LINKdefault;
|
|
sem = SemanticStart;
|
|
}
|
|
|
|
override void semantic(Scope* sc)
|
|
{
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "declaration";
|
|
}
|
|
|
|
override final uint size(Loc loc)
|
|
{
|
|
assert(type);
|
|
return cast(uint)type.size();
|
|
}
|
|
|
|
/*************************************
|
|
* Check to see if declaration can be modified in this context (sc).
|
|
* Issue error if not.
|
|
*/
|
|
final int checkModify(Loc loc, Scope* sc, Type t, Expression e1, int flag)
|
|
{
|
|
VarDeclaration v = isVarDeclaration();
|
|
if (v && v.canassign)
|
|
return 2;
|
|
if (isParameter() || isResult())
|
|
{
|
|
for (Scope* scx = sc; scx; scx = scx.enclosing)
|
|
{
|
|
if (scx.func == parent && (scx.flags & SCOPEcontract))
|
|
{
|
|
const(char)* s = isParameter() && parent.ident != Id.ensure ? "parameter" : "result";
|
|
if (!flag)
|
|
error(loc, "cannot modify %s '%s' in contract", s, toChars());
|
|
return 2; // do not report type related errors
|
|
}
|
|
}
|
|
}
|
|
if (v && (isCtorinit() || isField()))
|
|
{
|
|
// It's only modifiable if inside the right constructor
|
|
if ((storage_class & (STCforeach | STCref)) == (STCforeach | STCref))
|
|
return 2;
|
|
return modifyFieldVar(loc, sc, v, e1) ? 2 : 1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
override final Dsymbol search(Loc loc, Identifier ident, int flags = SearchLocalsOnly)
|
|
{
|
|
Dsymbol s = Dsymbol.search(loc, ident, flags);
|
|
if (!s && type)
|
|
{
|
|
s = type.toDsymbol(_scope);
|
|
if (s)
|
|
s = s.search(loc, ident, flags);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
final bool isStatic()
|
|
{
|
|
return (storage_class & STCstatic) != 0;
|
|
}
|
|
|
|
bool isDelete()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool isDataseg()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool isThreadlocal()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool isCodeseg()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
final bool isCtorinit()
|
|
{
|
|
return (storage_class & STCctorinit) != 0;
|
|
}
|
|
|
|
final bool isFinal()
|
|
{
|
|
return (storage_class & STCfinal) != 0;
|
|
}
|
|
|
|
final bool isAbstract()
|
|
{
|
|
return (storage_class & STCabstract) != 0;
|
|
}
|
|
|
|
final bool isConst()
|
|
{
|
|
return (storage_class & STCconst) != 0;
|
|
}
|
|
|
|
final bool isImmutable()
|
|
{
|
|
return (storage_class & STCimmutable) != 0;
|
|
}
|
|
|
|
final bool isWild()
|
|
{
|
|
return (storage_class & STCwild) != 0;
|
|
}
|
|
|
|
final bool isAuto()
|
|
{
|
|
return (storage_class & STCauto) != 0;
|
|
}
|
|
|
|
final bool isScope()
|
|
{
|
|
return (storage_class & STCscope) != 0;
|
|
}
|
|
|
|
final bool isSynchronized()
|
|
{
|
|
return (storage_class & STCsynchronized) != 0;
|
|
}
|
|
|
|
final bool isParameter()
|
|
{
|
|
return (storage_class & STCparameter) != 0;
|
|
}
|
|
|
|
override final bool isDeprecated()
|
|
{
|
|
return (storage_class & STCdeprecated) != 0;
|
|
}
|
|
|
|
final bool isOverride()
|
|
{
|
|
return (storage_class & STCoverride) != 0;
|
|
}
|
|
|
|
final bool isResult()
|
|
{
|
|
return (storage_class & STCresult) != 0;
|
|
}
|
|
|
|
final bool isField()
|
|
{
|
|
return (storage_class & STCfield) != 0;
|
|
}
|
|
|
|
final bool isIn()
|
|
{
|
|
return (storage_class & STCin) != 0;
|
|
}
|
|
|
|
final bool isOut()
|
|
{
|
|
return (storage_class & STCout) != 0;
|
|
}
|
|
|
|
final bool isRef()
|
|
{
|
|
return (storage_class & STCref) != 0;
|
|
}
|
|
|
|
override final Prot prot()
|
|
{
|
|
return protection;
|
|
}
|
|
|
|
override final inout(Declaration) isDeclaration() inout
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TupleDeclaration : Declaration
|
|
{
|
|
public:
|
|
Objects* objects;
|
|
bool isexp; // true: expression tuple
|
|
TypeTuple tupletype; // !=null if this is a type tuple
|
|
|
|
extern (D) this(Loc loc, Identifier id, Objects* objects)
|
|
{
|
|
super(id);
|
|
this.loc = loc;
|
|
this.objects = objects;
|
|
}
|
|
|
|
override Dsymbol syntaxCopy(Dsymbol s)
|
|
{
|
|
assert(0);
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "tuple";
|
|
}
|
|
|
|
override Type getType()
|
|
{
|
|
/* If this tuple represents a type, return that type
|
|
*/
|
|
//printf("TupleDeclaration::getType() %s\n", toChars());
|
|
if (isexp)
|
|
return null;
|
|
if (!tupletype)
|
|
{
|
|
/* It's only a type tuple if all the Object's are types
|
|
*/
|
|
for (size_t i = 0; i < objects.dim; i++)
|
|
{
|
|
RootObject o = (*objects)[i];
|
|
if (o.dyncast() != DYNCAST_TYPE)
|
|
{
|
|
//printf("\tnot[%d], %p, %d\n", i, o, o->dyncast());
|
|
return null;
|
|
}
|
|
}
|
|
/* We know it's a type tuple, so build the TypeTuple
|
|
*/
|
|
Types* types = cast(Types*)objects;
|
|
auto args = new Parameters();
|
|
args.setDim(objects.dim);
|
|
OutBuffer buf;
|
|
int hasdeco = 1;
|
|
for (size_t i = 0; i < types.dim; i++)
|
|
{
|
|
Type t = (*types)[i];
|
|
//printf("type = %s\n", t->toChars());
|
|
version (none)
|
|
{
|
|
buf.printf("_%s_%d", ident.toChars(), i);
|
|
char* name = cast(char*)buf.extractData();
|
|
auto id = new Identifier(name, TOKidentifier);
|
|
auto arg = new Parameter(STCin, t, id, null);
|
|
}
|
|
else
|
|
{
|
|
auto arg = new Parameter(0, t, null, null);
|
|
}
|
|
(*args)[i] = arg;
|
|
if (!t.deco)
|
|
hasdeco = 0;
|
|
}
|
|
tupletype = new TypeTuple(args);
|
|
if (hasdeco)
|
|
return tupletype.semantic(Loc(), null);
|
|
}
|
|
return tupletype;
|
|
}
|
|
|
|
override Dsymbol toAlias2()
|
|
{
|
|
//printf("TupleDeclaration::toAlias2() '%s' objects = %s\n", toChars(), objects->toChars());
|
|
for (size_t i = 0; i < objects.dim; i++)
|
|
{
|
|
RootObject o = (*objects)[i];
|
|
if (Dsymbol s = isDsymbol(o))
|
|
{
|
|
s = s.toAlias2();
|
|
(*objects)[i] = s;
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
override bool needThis()
|
|
{
|
|
//printf("TupleDeclaration::needThis(%s)\n", toChars());
|
|
for (size_t i = 0; i < objects.dim; i++)
|
|
{
|
|
RootObject o = (*objects)[i];
|
|
if (o.dyncast() == DYNCAST_EXPRESSION)
|
|
{
|
|
Expression e = cast(Expression)o;
|
|
if (e.op == TOKdsymbol)
|
|
{
|
|
DsymbolExp ve = cast(DsymbolExp)e;
|
|
Declaration d = ve.s.isDeclaration();
|
|
if (d && d.needThis())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
override inout(TupleDeclaration) isTupleDeclaration() inout
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class AliasDeclaration : Declaration
|
|
{
|
|
public:
|
|
Dsymbol aliassym;
|
|
Dsymbol overnext; // next in overload list
|
|
Dsymbol _import; // !=null if unresolved internal alias for selective import
|
|
|
|
extern (D) this(Loc loc, Identifier id, Type type)
|
|
{
|
|
super(id);
|
|
//printf("AliasDeclaration(id = '%s', type = %p)\n", id->toChars(), type);
|
|
//printf("type = '%s'\n", type->toChars());
|
|
this.loc = loc;
|
|
this.type = type;
|
|
assert(type);
|
|
}
|
|
|
|
extern (D) this(Loc loc, Identifier id, Dsymbol s)
|
|
{
|
|
super(id);
|
|
//printf("AliasDeclaration(id = '%s', s = %p)\n", id->toChars(), s);
|
|
assert(s != this);
|
|
this.loc = loc;
|
|
this.aliassym = s;
|
|
assert(s);
|
|
}
|
|
|
|
override Dsymbol syntaxCopy(Dsymbol s)
|
|
{
|
|
//printf("AliasDeclaration::syntaxCopy()\n");
|
|
assert(!s);
|
|
AliasDeclaration sa = type ? new AliasDeclaration(loc, ident, type.syntaxCopy()) : new AliasDeclaration(loc, ident, aliassym.syntaxCopy(null));
|
|
sa.storage_class = storage_class;
|
|
return sa;
|
|
}
|
|
|
|
override void semantic(Scope* sc)
|
|
{
|
|
//printf("AliasDeclaration::semantic() %s\n", toChars());
|
|
if (aliassym)
|
|
{
|
|
auto fd = aliassym.isFuncLiteralDeclaration();
|
|
auto td = aliassym.isTemplateDeclaration();
|
|
if (fd || td && td.literal)
|
|
{
|
|
if (fd && fd.semanticRun >= PASSsemanticdone)
|
|
return;
|
|
|
|
Expression e = new FuncExp(loc, aliassym);
|
|
e = e.semantic(sc);
|
|
if (e.op == TOKfunction)
|
|
{
|
|
FuncExp fe = cast(FuncExp)e;
|
|
aliassym = fe.td ? cast(Dsymbol)fe.td : fe.fd;
|
|
}
|
|
else
|
|
{
|
|
aliassym = null;
|
|
type = Type.terror;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (aliassym.isTemplateInstance())
|
|
aliassym.semantic(sc);
|
|
return;
|
|
}
|
|
inuse = 1;
|
|
|
|
storage_class |= sc.stc & STCdeprecated;
|
|
protection = sc.protection;
|
|
userAttribDecl = sc.userAttribDecl;
|
|
|
|
// Given:
|
|
// alias foo.bar.abc def;
|
|
// it is not knowable from the syntax whether this is an alias
|
|
// for a type or an alias for a symbol. It is up to the semantic()
|
|
// pass to distinguish.
|
|
// If it is a type, then type is set and getType() will return that
|
|
// type. If it is a symbol, then aliassym is set and type is NULL -
|
|
// toAlias() will return aliasssym.
|
|
|
|
uint errors = global.errors;
|
|
Type oldtype = type;
|
|
|
|
// Ungag errors when not instantiated DeclDefs scope alias
|
|
auto ungag = Ungag(global.gag);
|
|
//printf("%s parent = %s, gag = %d, instantiated = %d\n", toChars(), parent, global.gag, isInstantiated());
|
|
if (parent && global.gag && !isInstantiated() && !toParent2().isFuncDeclaration())
|
|
{
|
|
//printf("%s type = %s\n", toPrettyChars(), type->toChars());
|
|
global.gag = 0;
|
|
}
|
|
|
|
/* This section is needed because Type.resolve() will:
|
|
* const x = 3;
|
|
* alias y = x;
|
|
* try to convert identifier x to 3.
|
|
*/
|
|
auto s = type.toDsymbol(sc);
|
|
if (errors != global.errors)
|
|
{
|
|
s = null;
|
|
type = Type.terror;
|
|
}
|
|
if (s && s == this)
|
|
{
|
|
error("cannot resolve");
|
|
s = null;
|
|
type = Type.terror;
|
|
}
|
|
if (!s || !s.isEnumMember())
|
|
{
|
|
Type t;
|
|
Expression e;
|
|
Scope* sc2 = sc;
|
|
if (storage_class & (STCref | STCnothrow | STCnogc | STCpure | STCdisable))
|
|
{
|
|
// For 'ref' to be attached to function types, and picked
|
|
// up by Type.resolve(), it has to go into sc.
|
|
sc2 = sc.push();
|
|
sc2.stc |= storage_class & (STCref | STCnothrow | STCnogc | STCpure | STCshared | STCdisable);
|
|
}
|
|
type = type.addSTC(storage_class);
|
|
type.resolve(loc, sc2, &e, &t, &s);
|
|
if (sc2 != sc)
|
|
sc2.pop();
|
|
|
|
if (e) // Try to convert Expression to Dsymbol
|
|
{
|
|
s = getDsymbol(e);
|
|
if (!s)
|
|
{
|
|
if (e.op != TOKerror)
|
|
error("cannot alias an expression %s", e.toChars());
|
|
t = Type.terror;
|
|
}
|
|
}
|
|
type = t;
|
|
}
|
|
if (s == this)
|
|
{
|
|
assert(global.errors);
|
|
type = Type.terror;
|
|
s = null;
|
|
}
|
|
if (!s) // it's a type alias
|
|
{
|
|
//printf("alias %s resolved to type %s\n", toChars(), type.toChars());
|
|
type = type.semantic(loc, sc);
|
|
aliassym = null;
|
|
}
|
|
else // it's a symbolic alias
|
|
{
|
|
//printf("alias %s resolved to %s %s\n", toChars(), s.kind(), s.toChars());
|
|
type = null;
|
|
aliassym = s;
|
|
}
|
|
if (global.gag && errors != global.errors)
|
|
{
|
|
type = oldtype;
|
|
aliassym = null;
|
|
}
|
|
inuse = 0;
|
|
semanticRun = PASSsemanticdone;
|
|
|
|
if (auto sx = overnext)
|
|
{
|
|
overnext = null;
|
|
if (!overloadInsert(sx))
|
|
ScopeDsymbol.multiplyDefined(Loc(), sx, this);
|
|
}
|
|
}
|
|
|
|
override bool overloadInsert(Dsymbol s)
|
|
{
|
|
//printf("[%s] AliasDeclaration::overloadInsert('%s') s = %s %s @ [%s]\n",
|
|
// loc.toChars(), toChars(), s.kind(), s.toChars(), s.loc.toChars());
|
|
|
|
/** Aliases aren't overloadable themselves, but if their Aliasee is
|
|
* overloadable they are converted to an overloadable Alias (either
|
|
* FuncAliasDeclaration or OverDeclaration).
|
|
*
|
|
* This is done by moving the Aliasee into such an overloadable alias
|
|
* which is then used to replace the existing Aliasee. The original
|
|
* Alias (_this_) remains a useless shell.
|
|
*
|
|
* This is a horrible mess. It was probably done to avoid replacing
|
|
* existing AST nodes and references, but it needs a major
|
|
* simplification b/c it's too complex to maintain.
|
|
*
|
|
* A simpler approach might be to merge any colliding symbols into a
|
|
* simple Overload class (an array) and then later have that resolve
|
|
* all collisions.
|
|
*/
|
|
if (semanticRun >= PASSsemanticdone)
|
|
{
|
|
/* Semantic analysis is already finished, and the aliased entity
|
|
* is not overloadable.
|
|
*/
|
|
if (type)
|
|
return false;
|
|
|
|
/* When s is added in member scope by static if, mixin("code") or others,
|
|
* aliassym is determined already. See the case in: test/compilable/test61.d
|
|
*/
|
|
auto sa = aliassym.toAlias();
|
|
if (auto fd = sa.isFuncDeclaration())
|
|
{
|
|
auto fa = new FuncAliasDeclaration(ident, fd);
|
|
fa.protection = protection;
|
|
fa.parent = parent;
|
|
aliassym = fa;
|
|
return aliassym.overloadInsert(s);
|
|
}
|
|
if (auto td = sa.isTemplateDeclaration())
|
|
{
|
|
auto od = new OverDeclaration(ident, td);
|
|
od.protection = protection;
|
|
od.parent = parent;
|
|
aliassym = od;
|
|
return aliassym.overloadInsert(s);
|
|
}
|
|
if (auto od = sa.isOverDeclaration())
|
|
{
|
|
if (sa.ident != ident || sa.parent != parent)
|
|
{
|
|
od = new OverDeclaration(ident, od);
|
|
od.protection = protection;
|
|
od.parent = parent;
|
|
aliassym = od;
|
|
}
|
|
return od.overloadInsert(s);
|
|
}
|
|
if (auto os = sa.isOverloadSet())
|
|
{
|
|
if (sa.ident != ident || sa.parent != parent)
|
|
{
|
|
os = new OverloadSet(ident, os);
|
|
// TODO: protection is lost here b/c OverloadSets have no protection attribute
|
|
// Might no be a practical issue, b/c the code below fails to resolve the overload anyhow.
|
|
// ----
|
|
// module os1;
|
|
// import a, b;
|
|
// private alias merged = foo; // private alias to overload set of a.foo and b.foo
|
|
// ----
|
|
// module os2;
|
|
// import a, b;
|
|
// public alias merged = bar; // public alias to overload set of a.bar and b.bar
|
|
// ----
|
|
// module bug;
|
|
// import os1, os2;
|
|
// void test() { merged(123); } // should only look at os2.merged
|
|
//
|
|
// os.protection = protection;
|
|
os.parent = parent;
|
|
aliassym = os;
|
|
}
|
|
os.push(s);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* Don't know yet what the aliased symbol is, so assume it can
|
|
* be overloaded and check later for correctness.
|
|
*/
|
|
if (overnext)
|
|
return overnext.overloadInsert(s);
|
|
if (s is this)
|
|
return true;
|
|
overnext = s;
|
|
return true;
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "alias";
|
|
}
|
|
|
|
override Type getType()
|
|
{
|
|
if (type)
|
|
return type;
|
|
return toAlias().getType();
|
|
}
|
|
|
|
override Dsymbol toAlias()
|
|
{
|
|
//printf("[%s] AliasDeclaration::toAlias('%s', this = %p, aliassym = %p, kind = '%s', inuse = %d)\n",
|
|
// loc.toChars(), toChars(), this, aliassym, aliassym ? aliassym.kind() : "", inuse);
|
|
assert(this != aliassym);
|
|
//static int count; if (++count == 10) *(char*)0=0;
|
|
if (inuse == 1 && type && _scope)
|
|
{
|
|
inuse = 2;
|
|
uint olderrors = global.errors;
|
|
Dsymbol s = type.toDsymbol(_scope);
|
|
//printf("[%s] type = %s, s = %p, this = %p\n", loc.toChars(), type->toChars(), s, this);
|
|
if (global.errors != olderrors)
|
|
goto Lerr;
|
|
if (s)
|
|
{
|
|
s = s.toAlias();
|
|
if (global.errors != olderrors)
|
|
goto Lerr;
|
|
aliassym = s;
|
|
inuse = 0;
|
|
}
|
|
else
|
|
{
|
|
Type t = type.semantic(loc, _scope);
|
|
if (t.ty == Terror)
|
|
goto Lerr;
|
|
if (global.errors != olderrors)
|
|
goto Lerr;
|
|
//printf("t = %s\n", t->toChars());
|
|
inuse = 0;
|
|
}
|
|
}
|
|
if (inuse)
|
|
{
|
|
error("recursive alias declaration");
|
|
Lerr:
|
|
// Avoid breaking "recursive alias" state during errors gagged
|
|
if (global.gag)
|
|
return this;
|
|
aliassym = new AliasDeclaration(loc, ident, Type.terror);
|
|
type = Type.terror;
|
|
return aliassym;
|
|
}
|
|
if (aliassym)
|
|
{
|
|
// semantic is already done.
|
|
|
|
// Even if type.deco !is null, "alias T = const int;` needs semantic
|
|
// call to take the storage class `const` as type qualifier.
|
|
}
|
|
else if (_import && _import._scope)
|
|
{
|
|
/* If this is an internal alias for selective/renamed import,
|
|
* resolve it under the correct scope.
|
|
*/
|
|
_import.semantic(null);
|
|
}
|
|
else if (_scope)
|
|
{
|
|
semantic(_scope);
|
|
}
|
|
inuse = 1;
|
|
Dsymbol s = aliassym ? aliassym.toAlias() : this;
|
|
inuse = 0;
|
|
return s;
|
|
}
|
|
|
|
override Dsymbol toAlias2()
|
|
{
|
|
if (inuse)
|
|
{
|
|
error("recursive alias declaration");
|
|
return this;
|
|
}
|
|
inuse = 1;
|
|
Dsymbol s = aliassym ? aliassym.toAlias2() : this;
|
|
inuse = 0;
|
|
return s;
|
|
}
|
|
|
|
override bool isOverloadable()
|
|
{
|
|
return aliassym && aliassym.isOverloadable();
|
|
}
|
|
|
|
override inout(AliasDeclaration) isAliasDeclaration() inout
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class OverDeclaration : Declaration
|
|
{
|
|
public:
|
|
Dsymbol overnext; // next in overload list
|
|
Dsymbol aliassym;
|
|
bool hasOverloads;
|
|
|
|
extern (D) this(Identifier ident, Dsymbol s, bool hasOverloads = true)
|
|
{
|
|
super(ident);
|
|
this.aliassym = s;
|
|
this.hasOverloads = hasOverloads;
|
|
if (hasOverloads)
|
|
{
|
|
if (OverDeclaration od = aliassym.isOverDeclaration())
|
|
this.hasOverloads = od.hasOverloads;
|
|
}
|
|
else
|
|
{
|
|
// for internal use
|
|
assert(!aliassym.isOverDeclaration());
|
|
}
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "overload alias"; // todo
|
|
}
|
|
|
|
override void semantic(Scope* sc)
|
|
{
|
|
}
|
|
|
|
override bool equals(RootObject o)
|
|
{
|
|
if (this == o)
|
|
return true;
|
|
Dsymbol s = isDsymbol(o);
|
|
if (!s)
|
|
return false;
|
|
OverDeclaration od1 = this;
|
|
if (OverDeclaration od2 = s.isOverDeclaration())
|
|
{
|
|
return od1.aliassym.equals(od2.aliassym) && od1.hasOverloads == od2.hasOverloads;
|
|
}
|
|
if (aliassym == s)
|
|
{
|
|
if (hasOverloads)
|
|
return true;
|
|
if (FuncDeclaration fd = s.isFuncDeclaration())
|
|
{
|
|
return fd.isUnique() !is null;
|
|
}
|
|
if (TemplateDeclaration td = s.isTemplateDeclaration())
|
|
{
|
|
return td.overnext is null;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
override bool overloadInsert(Dsymbol s)
|
|
{
|
|
//printf("OverDeclaration::overloadInsert('%s') aliassym = %p, overnext = %p\n", s->toChars(), aliassym, overnext);
|
|
if (overnext)
|
|
return overnext.overloadInsert(s);
|
|
if (s == this)
|
|
return true;
|
|
overnext = s;
|
|
return true;
|
|
}
|
|
|
|
override bool isOverloadable()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
Dsymbol isUnique()
|
|
{
|
|
if (!hasOverloads)
|
|
{
|
|
if (aliassym.isFuncDeclaration() ||
|
|
aliassym.isTemplateDeclaration())
|
|
{
|
|
return aliassym;
|
|
}
|
|
}
|
|
|
|
Dsymbol result = null;
|
|
overloadApply(aliassym, (Dsymbol s)
|
|
{
|
|
if (result)
|
|
{
|
|
result = null;
|
|
return 1; // ambiguous, done
|
|
}
|
|
else
|
|
{
|
|
result = s;
|
|
return 0;
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
|
|
override inout(OverDeclaration) isOverDeclaration() inout
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) class VarDeclaration : Declaration
|
|
{
|
|
public:
|
|
Initializer _init;
|
|
uint offset;
|
|
bool noscope; // if scope destruction is disabled
|
|
FuncDeclarations nestedrefs; // referenced by these lexically nested functions
|
|
bool isargptr; // if parameter that _argptr points to
|
|
structalign_t alignment;
|
|
bool ctorinit; // it has been initialized in a ctor
|
|
|
|
// 1: it has been allocated on the stack
|
|
// 2: on stack, run destructor anyway
|
|
short onstack;
|
|
|
|
int canassign; // it can be assigned to
|
|
bool overlapped; // if it is a field and has overlapping
|
|
ubyte isdataseg; // private data for isDataseg 0 unset, 1 true, 2 false
|
|
Dsymbol aliassym; // if redone as alias to another symbol
|
|
VarDeclaration lastVar; // Linked list of variables for goto-skips-init detection
|
|
|
|
// When interpreting, these point to the value (NULL if value not determinable)
|
|
// The index of this variable on the CTFE stack, -1 if not allocated
|
|
int ctfeAdrOnStack;
|
|
|
|
// if !=NULL, rundtor is tested at runtime to see
|
|
// if the destructor should be run. Used to prevent
|
|
// dtor calls on postblitted vars
|
|
VarDeclaration rundtor;
|
|
Expression edtor; // if !=null, does the destruction of the variable
|
|
IntRange* range; // if !=null, the variable is known to be within the range
|
|
|
|
final extern (D) this(Loc loc, Type type, Identifier id, Initializer _init)
|
|
{
|
|
super(id);
|
|
//printf("VarDeclaration('%s')\n", id->toChars());
|
|
assert(id);
|
|
debug
|
|
{
|
|
if (!type && !_init)
|
|
{
|
|
printf("VarDeclaration('%s')\n", id.toChars());
|
|
//*(char*)0=0;
|
|
}
|
|
}
|
|
assert(type || _init);
|
|
this.type = type;
|
|
this._init = _init;
|
|
this.loc = loc;
|
|
ctfeAdrOnStack = -1;
|
|
}
|
|
|
|
override Dsymbol syntaxCopy(Dsymbol s)
|
|
{
|
|
//printf("VarDeclaration::syntaxCopy(%s)\n", toChars());
|
|
assert(!s);
|
|
auto v = new VarDeclaration(loc, type ? type.syntaxCopy() : null, ident, _init ? _init.syntaxCopy() : null);
|
|
v.storage_class = storage_class;
|
|
return v;
|
|
}
|
|
|
|
override void semantic(Scope* sc)
|
|
{
|
|
version (none)
|
|
{
|
|
printf("VarDeclaration::semantic('%s', parent = '%s') sem = %d\n", toChars(), sc.parent ? sc.parent.toChars() : null, sem);
|
|
printf(" type = %s\n", type ? type.toChars() : "null");
|
|
printf(" stc = x%x\n", sc.stc);
|
|
printf(" storage_class = x%llx\n", storage_class);
|
|
printf("linkage = %d\n", sc.linkage);
|
|
//if (strcmp(toChars(), "mul") == 0) assert(0);
|
|
}
|
|
// if (sem > SemanticStart)
|
|
// return;
|
|
// sem = SemanticIn;
|
|
if (sem >= SemanticDone)
|
|
return;
|
|
Scope* scx = null;
|
|
if (_scope)
|
|
{
|
|
sc = _scope;
|
|
scx = sc;
|
|
_scope = null;
|
|
}
|
|
/* Pick up storage classes from context, but except synchronized,
|
|
* override, abstract, and final.
|
|
*/
|
|
storage_class |= (sc.stc & ~(STCsynchronized | STCoverride | STCabstract | STCfinal));
|
|
if (storage_class & STCextern && _init)
|
|
error("extern symbols cannot have initializers");
|
|
userAttribDecl = sc.userAttribDecl;
|
|
AggregateDeclaration ad = isThis();
|
|
if (ad)
|
|
storage_class |= ad.storage_class & STC_TYPECTOR;
|
|
/* If auto type inference, do the inference
|
|
*/
|
|
int inferred = 0;
|
|
if (!type)
|
|
{
|
|
inuse++;
|
|
// Infering the type requires running semantic,
|
|
// so mark the scope as ctfe if required
|
|
bool needctfe = (storage_class & (STCmanifest | STCstatic)) != 0;
|
|
if (needctfe)
|
|
sc = sc.startCTFE();
|
|
//printf("inferring type for %s with init %s\n", toChars(), init->toChars());
|
|
_init = _init.inferType(sc);
|
|
type = _init.toExpression().type;
|
|
if (needctfe)
|
|
sc = sc.endCTFE();
|
|
inuse--;
|
|
inferred = 1;
|
|
/* This is a kludge to support the existing syntax for RAII
|
|
* declarations.
|
|
*/
|
|
storage_class &= ~STCauto;
|
|
originalType = type.syntaxCopy();
|
|
}
|
|
else
|
|
{
|
|
if (!originalType)
|
|
originalType = type.syntaxCopy();
|
|
/* Prefix function attributes of variable declaration can affect
|
|
* its type:
|
|
* pure nothrow void function() fp;
|
|
* static assert(is(typeof(fp) == void function() pure nothrow));
|
|
*/
|
|
Scope* sc2 = sc.push();
|
|
sc2.stc |= (storage_class & STC_FUNCATTR);
|
|
inuse++;
|
|
type = type.semantic(loc, sc2);
|
|
inuse--;
|
|
sc2.pop();
|
|
}
|
|
//printf(" semantic type = %s\n", type ? type->toChars() : "null");
|
|
type.checkDeprecated(loc, sc);
|
|
linkage = sc.linkage;
|
|
this.parent = sc.parent;
|
|
//printf("this = %p, parent = %p, '%s'\n", this, parent, parent->toChars());
|
|
protection = sc.protection;
|
|
/* If scope's alignment is the default, use the type's alignment,
|
|
* otherwise the scope overrrides.
|
|
*/
|
|
alignment = sc.structalign;
|
|
if (alignment == STRUCTALIGN_DEFAULT)
|
|
alignment = type.alignment(); // use type's alignment
|
|
//printf("sc->stc = %x\n", sc->stc);
|
|
//printf("storage_class = x%x\n", storage_class);
|
|
if (global.params.vcomplex)
|
|
type.checkComplexTransition(loc);
|
|
// Calculate type size + safety checks
|
|
if (sc.func && !sc.intypeof && !isMember())
|
|
{
|
|
if (storage_class & STCgshared)
|
|
{
|
|
if (sc.func.setUnsafe())
|
|
error("__gshared not allowed in safe functions; use shared");
|
|
}
|
|
if (_init && _init.isVoidInitializer() && type.hasPointers()) // get type size
|
|
{
|
|
if (sc.func.setUnsafe())
|
|
error("void initializers for pointers not allowed in safe functions");
|
|
}
|
|
}
|
|
Dsymbol parent = toParent();
|
|
Type tb = type.toBasetype();
|
|
Type tbn = tb.baseElemOf();
|
|
if (tb.ty == Tvoid && !(storage_class & STClazy))
|
|
{
|
|
if (inferred)
|
|
{
|
|
error("type %s is inferred from initializer %s, and variables cannot be of type void", type.toChars(), _init.toChars());
|
|
}
|
|
else
|
|
error("variables cannot be of type void");
|
|
type = Type.terror;
|
|
tb = type;
|
|
}
|
|
if (tb.ty == Tfunction)
|
|
{
|
|
error("cannot be declared to be a function");
|
|
type = Type.terror;
|
|
tb = type;
|
|
}
|
|
if (tb.ty == Tstruct)
|
|
{
|
|
TypeStruct ts = cast(TypeStruct)tb;
|
|
if (!ts.sym.members)
|
|
{
|
|
error("no definition of struct %s", ts.toChars());
|
|
}
|
|
}
|
|
if ((storage_class & STCauto) && !inferred)
|
|
error("storage class 'auto' has no effect if type is not inferred, did you mean 'scope'?");
|
|
if (tb.ty == Ttuple)
|
|
{
|
|
/* Instead, declare variables for each of the tuple elements
|
|
* and add those.
|
|
*/
|
|
TypeTuple tt = cast(TypeTuple)tb;
|
|
size_t nelems = Parameter.dim(tt.arguments);
|
|
Expression ie = (_init && !_init.isVoidInitializer()) ? _init.toExpression() : null;
|
|
if (ie)
|
|
ie = ie.semantic(sc);
|
|
if (nelems > 0 && ie)
|
|
{
|
|
auto iexps = new Expressions();
|
|
iexps.push(ie);
|
|
auto exps = new Expressions();
|
|
for (size_t pos = 0; pos < iexps.dim; pos++)
|
|
{
|
|
Lexpand1:
|
|
Expression e = (*iexps)[pos];
|
|
Parameter arg = Parameter.getNth(tt.arguments, pos);
|
|
arg.type = arg.type.semantic(loc, sc);
|
|
//printf("[%d] iexps->dim = %d, ", pos, iexps->dim);
|
|
//printf("e = (%s %s, %s), ", Token::tochars[e->op], e->toChars(), e->type->toChars());
|
|
//printf("arg = (%s, %s)\n", arg->toChars(), arg->type->toChars());
|
|
if (e != ie)
|
|
{
|
|
if (iexps.dim > nelems)
|
|
goto Lnomatch;
|
|
if (e.type.implicitConvTo(arg.type))
|
|
continue;
|
|
}
|
|
if (e.op == TOKtuple)
|
|
{
|
|
TupleExp te = cast(TupleExp)e;
|
|
if (iexps.dim - 1 + te.exps.dim > nelems)
|
|
goto Lnomatch;
|
|
iexps.remove(pos);
|
|
iexps.insert(pos, te.exps);
|
|
(*iexps)[pos] = Expression.combine(te.e0, (*iexps)[pos]);
|
|
goto Lexpand1;
|
|
}
|
|
else if (isAliasThisTuple(e))
|
|
{
|
|
Identifier id = Identifier.generateId("__tup");
|
|
auto ei = new ExpInitializer(e.loc, e);
|
|
auto v = new VarDeclaration(loc, null, id, ei);
|
|
v.storage_class = STCtemp | STCctfe | STCref | STCforeach;
|
|
auto ve = new VarExp(loc, v);
|
|
ve.type = e.type;
|
|
exps.setDim(1);
|
|
(*exps)[0] = ve;
|
|
expandAliasThisTuples(exps, 0);
|
|
for (size_t u = 0; u < exps.dim; u++)
|
|
{
|
|
Lexpand2:
|
|
Expression ee = (*exps)[u];
|
|
arg = Parameter.getNth(tt.arguments, pos + u);
|
|
arg.type = arg.type.semantic(loc, sc);
|
|
//printf("[%d+%d] exps->dim = %d, ", pos, u, exps->dim);
|
|
//printf("ee = (%s %s, %s), ", Token::tochars[ee->op], ee->toChars(), ee->type->toChars());
|
|
//printf("arg = (%s, %s)\n", arg->toChars(), arg->type->toChars());
|
|
size_t iexps_dim = iexps.dim - 1 + exps.dim;
|
|
if (iexps_dim > nelems)
|
|
goto Lnomatch;
|
|
if (ee.type.implicitConvTo(arg.type))
|
|
continue;
|
|
if (expandAliasThisTuples(exps, u) != -1)
|
|
goto Lexpand2;
|
|
}
|
|
if ((*exps)[0] != ve)
|
|
{
|
|
Expression e0 = (*exps)[0];
|
|
(*exps)[0] = new CommaExp(loc, new DeclarationExp(loc, v), e0);
|
|
(*exps)[0].type = e0.type;
|
|
iexps.remove(pos);
|
|
iexps.insert(pos, exps);
|
|
goto Lexpand1;
|
|
}
|
|
}
|
|
}
|
|
if (iexps.dim < nelems)
|
|
goto Lnomatch;
|
|
ie = new TupleExp(_init.loc, iexps);
|
|
}
|
|
Lnomatch:
|
|
if (ie && ie.op == TOKtuple)
|
|
{
|
|
TupleExp te = cast(TupleExp)ie;
|
|
size_t tedim = te.exps.dim;
|
|
if (tedim != nelems)
|
|
{
|
|
.error(loc, "tuple of %d elements cannot be assigned to tuple of %d elements", cast(int)tedim, cast(int)nelems);
|
|
for (size_t u = tedim; u < nelems; u++) // fill dummy expression
|
|
te.exps.push(new ErrorExp());
|
|
}
|
|
}
|
|
|
|
auto exps = new Objects();
|
|
exps.setDim(nelems);
|
|
for (size_t i = 0; i < nelems; i++)
|
|
{
|
|
Parameter arg = Parameter.getNth(tt.arguments, i);
|
|
OutBuffer buf;
|
|
buf.printf("__%s_field_%llu", ident.toChars(), cast(ulong)i);
|
|
auto id = Identifier.idPool(buf.peekSlice());
|
|
|
|
Initializer ti;
|
|
if (ie)
|
|
{
|
|
Expression einit = ie;
|
|
if (ie.op == TOKtuple)
|
|
{
|
|
TupleExp te = cast(TupleExp)ie;
|
|
einit = (*te.exps)[i];
|
|
if (i == 0)
|
|
einit = Expression.combine(te.e0, einit);
|
|
}
|
|
ti = new ExpInitializer(einit.loc, einit);
|
|
}
|
|
else
|
|
ti = _init ? _init.syntaxCopy() : null;
|
|
|
|
auto v = new VarDeclaration(loc, arg.type, id, ti);
|
|
v.storage_class |= STCtemp | storage_class;
|
|
if (arg.storageClass & STCparameter)
|
|
v.storage_class |= arg.storageClass;
|
|
//printf("declaring field %s of type %s\n", v->toChars(), v->type->toChars());
|
|
v.semantic(sc);
|
|
|
|
if (sc.scopesym)
|
|
{
|
|
//printf("adding %s to %s\n", v->toChars(), sc->scopesym->toChars());
|
|
if (sc.scopesym.members)
|
|
// Note this prevents using foreach() over members, because the limits can change
|
|
sc.scopesym.members.push(v);
|
|
}
|
|
|
|
Expression e = new DsymbolExp(loc, v);
|
|
(*exps)[i] = e;
|
|
}
|
|
auto v2 = new TupleDeclaration(loc, ident, exps);
|
|
v2.parent = this.parent;
|
|
v2.isexp = true;
|
|
aliassym = v2;
|
|
sem = SemanticDone;
|
|
return;
|
|
}
|
|
/* Storage class can modify the type
|
|
*/
|
|
type = type.addStorageClass(storage_class);
|
|
/* Adjust storage class to reflect type
|
|
*/
|
|
if (type.isConst())
|
|
{
|
|
storage_class |= STCconst;
|
|
if (type.isShared())
|
|
storage_class |= STCshared;
|
|
}
|
|
else if (type.isImmutable())
|
|
storage_class |= STCimmutable;
|
|
else if (type.isShared())
|
|
storage_class |= STCshared;
|
|
else if (type.isWild())
|
|
storage_class |= STCwild;
|
|
if (StorageClass stc = storage_class & (STCsynchronized | STCoverride | STCabstract | STCfinal))
|
|
{
|
|
if (stc == STCfinal)
|
|
error("cannot be final, perhaps you meant const?");
|
|
else
|
|
{
|
|
OutBuffer buf;
|
|
stcToBuffer(&buf, stc);
|
|
error("cannot be %s", buf.peekString());
|
|
}
|
|
storage_class &= ~stc; // strip off
|
|
}
|
|
if (storage_class & (STCstatic | STCextern | STCmanifest | STCtemplateparameter | STCtls | STCgshared | STCctfe))
|
|
{
|
|
}
|
|
else
|
|
{
|
|
AggregateDeclaration aad = parent.isAggregateDeclaration();
|
|
if (aad)
|
|
{
|
|
if (global.params.vfield && storage_class & (STCconst | STCimmutable) && _init && !_init.isVoidInitializer())
|
|
{
|
|
const(char)* p = loc.toChars();
|
|
const(char)* s = (storage_class & STCimmutable) ? "immutable" : "const";
|
|
fprintf(global.stdmsg, "%s: %s.%s is %s field\n", p ? p : "", ad.toPrettyChars(), toChars(), s);
|
|
}
|
|
storage_class |= STCfield;
|
|
if (tbn.ty == Tstruct && (cast(TypeStruct)tbn).sym.noDefaultCtor)
|
|
{
|
|
if (!isThisDeclaration() && !_init)
|
|
aad.noDefaultCtor = true;
|
|
}
|
|
}
|
|
InterfaceDeclaration id = parent.isInterfaceDeclaration();
|
|
if (id)
|
|
{
|
|
error("field not allowed in interface");
|
|
}
|
|
else if (aad && aad.sizeok == SIZEOKdone)
|
|
{
|
|
error("cannot be further field because it will change the determined %s size", aad.toChars());
|
|
}
|
|
/* Templates cannot add fields to aggregates
|
|
*/
|
|
TemplateInstance ti = parent.isTemplateInstance();
|
|
if (ti)
|
|
{
|
|
// Take care of nested templates
|
|
while (1)
|
|
{
|
|
TemplateInstance ti2 = ti.tempdecl.parent.isTemplateInstance();
|
|
if (!ti2)
|
|
break;
|
|
ti = ti2;
|
|
}
|
|
// If it's a member template
|
|
AggregateDeclaration ad2 = ti.tempdecl.isMember();
|
|
if (ad2 && storage_class != STCundefined)
|
|
{
|
|
error("cannot use template to add field to aggregate '%s'", ad2.toChars());
|
|
}
|
|
}
|
|
}
|
|
if ((storage_class & (STCref | STCparameter | STCforeach)) == STCref && ident != Id.This)
|
|
{
|
|
error("only parameters or foreach declarations can be ref");
|
|
}
|
|
if (type.hasWild())
|
|
{
|
|
if (storage_class & (STCstatic | STCextern | STCtls | STCgshared | STCmanifest | STCfield) || isDataseg())
|
|
{
|
|
error("only parameters or stack based variables can be inout");
|
|
}
|
|
FuncDeclaration func = sc.func;
|
|
if (func)
|
|
{
|
|
if (func.fes)
|
|
func = func.fes.func;
|
|
bool isWild = false;
|
|
for (FuncDeclaration fd = func; fd; fd = fd.toParent2().isFuncDeclaration())
|
|
{
|
|
if ((cast(TypeFunction)fd.type).iswild)
|
|
{
|
|
isWild = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!isWild)
|
|
{
|
|
error("inout variables can only be declared inside inout functions");
|
|
}
|
|
}
|
|
}
|
|
if (!(storage_class & (STCctfe | STCref | STCresult)) && tbn.ty == Tstruct && (cast(TypeStruct)tbn).sym.noDefaultCtor)
|
|
{
|
|
if (!_init)
|
|
{
|
|
if (isField())
|
|
{
|
|
/* For fields, we'll check the constructor later to make sure it is initialized
|
|
*/
|
|
storage_class |= STCnodefaultctor;
|
|
}
|
|
else if (storage_class & STCparameter)
|
|
{
|
|
}
|
|
else
|
|
error("default construction is disabled for type %s", type.toChars());
|
|
}
|
|
}
|
|
FuncDeclaration fd = parent.isFuncDeclaration();
|
|
if (type.isscope() && !noscope)
|
|
{
|
|
if (storage_class & (STCfield | STCout | STCref | STCstatic | STCmanifest | STCtls | STCgshared) || !fd)
|
|
{
|
|
error("globals, statics, fields, manifest constants, ref and out parameters cannot be scope");
|
|
}
|
|
if (!(storage_class & STCscope))
|
|
{
|
|
if (!(storage_class & STCparameter) && ident != Id.withSym)
|
|
error("reference to scope class must be scope");
|
|
}
|
|
}
|
|
|
|
if (!_init && !fd)
|
|
{
|
|
// If not mutable, initializable by constructor only
|
|
storage_class |= STCctorinit;
|
|
}
|
|
if (_init)
|
|
storage_class |= STCinit; // remember we had an explicit initializer
|
|
else if (storage_class & STCmanifest)
|
|
error("manifest constants must have initializers");
|
|
|
|
bool isBlit = false;
|
|
if (!_init &&
|
|
!sc.inunion &&
|
|
!(storage_class & (STCstatic | STCgshared | STCextern)) &&
|
|
fd &&
|
|
(!(storage_class & (STCfield | STCin | STCforeach | STCparameter | STCresult)) ||
|
|
(storage_class & STCout)) &&
|
|
type.size() != 0)
|
|
{
|
|
// Provide a default initializer
|
|
|
|
//printf("Providing default initializer for '%s'\n", toChars());
|
|
|
|
Type tv = type;
|
|
while (tv.ty == Tsarray) // Don't skip Tenum
|
|
tv = tv.nextOf();
|
|
if (tv.needsNested())
|
|
{
|
|
/* Nested struct requires valid enclosing frame pointer.
|
|
* In StructLiteralExp::toElem(), it's calculated.
|
|
*/
|
|
assert(tbn.ty == Tstruct);
|
|
checkFrameAccess(loc, sc, (cast(TypeStruct)tbn).sym);
|
|
|
|
Expression e = tv.defaultInitLiteral(loc);
|
|
e = new BlitExp(loc, new VarExp(loc, this), e);
|
|
e = e.semantic(sc);
|
|
_init = new ExpInitializer(loc, e);
|
|
goto Ldtor;
|
|
}
|
|
if (tv.ty == Tstruct && (cast(TypeStruct)tv).sym.zeroInit == 1)
|
|
{
|
|
/* If a struct is all zeros, as a special case
|
|
* set it's initializer to the integer 0.
|
|
* In AssignExp::toElem(), we check for this and issue
|
|
* a memset() to initialize the struct.
|
|
* Must do same check in interpreter.
|
|
*/
|
|
Expression e = new IntegerExp(loc, 0, Type.tint32);
|
|
e = new BlitExp(loc, new VarExp(loc, this), e);
|
|
e.type = type; // don't type check this, it would fail
|
|
_init = new ExpInitializer(loc, e);
|
|
goto Ldtor;
|
|
}
|
|
if (type.baseElemOf().ty == Tvoid)
|
|
{
|
|
error("%s does not have a default initializer", type.toChars());
|
|
}
|
|
else if (auto e = type.defaultInit(loc))
|
|
{
|
|
_init = new ExpInitializer(loc, e);
|
|
}
|
|
|
|
// Default initializer is always a blit
|
|
isBlit = true;
|
|
}
|
|
if (_init)
|
|
{
|
|
sc = sc.push();
|
|
sc.stc &= ~(STC_TYPECTOR | STCpure | STCnothrow | STCnogc | STCref | STCdisable);
|
|
ExpInitializer ei = _init.isExpInitializer();
|
|
if (ei) // Bugzilla 13424: Preset the required type to fail in FuncLiteralDeclaration::semantic3
|
|
ei.exp = inferType(ei.exp, type);
|
|
// If inside function, there is no semantic3() call
|
|
if (sc.func || sc.intypeof == 1)
|
|
{
|
|
// If local variable, use AssignExp to handle all the various
|
|
// possibilities.
|
|
if (fd && !(storage_class & (STCmanifest | STCstatic | STCtls | STCgshared | STCextern)) && !_init.isVoidInitializer())
|
|
{
|
|
//printf("fd = '%s', var = '%s'\n", fd->toChars(), toChars());
|
|
if (!ei)
|
|
{
|
|
ArrayInitializer ai = _init.isArrayInitializer();
|
|
Expression e;
|
|
if (ai && tb.ty == Taarray)
|
|
e = ai.toAssocArrayLiteral();
|
|
else
|
|
e = _init.toExpression();
|
|
if (!e)
|
|
{
|
|
// Run semantic, but don't need to interpret
|
|
_init = _init.semantic(sc, type, INITnointerpret);
|
|
e = _init.toExpression();
|
|
if (!e)
|
|
{
|
|
error("is not a static and cannot have static initializer");
|
|
return;
|
|
}
|
|
}
|
|
ei = new ExpInitializer(_init.loc, e);
|
|
_init = ei;
|
|
}
|
|
Expression exp = ei.exp;
|
|
Expression e1 = new VarExp(loc, this);
|
|
if (isBlit)
|
|
exp = new BlitExp(loc, e1, exp);
|
|
else
|
|
exp = new ConstructExp(loc, e1, exp);
|
|
canassign++;
|
|
exp = exp.semantic(sc);
|
|
canassign--;
|
|
exp = exp.optimize(WANTvalue);
|
|
if (exp.op == TOKerror)
|
|
{
|
|
_init = new ErrorInitializer();
|
|
ei = null;
|
|
}
|
|
else
|
|
ei.exp = exp;
|
|
if (ei && isScope())
|
|
{
|
|
Expression ex = ei.exp;
|
|
while (ex.op == TOKcomma)
|
|
ex = (cast(CommaExp)ex).e2;
|
|
if (ex.op == TOKblit || ex.op == TOKconstruct)
|
|
ex = (cast(AssignExp)ex).e2;
|
|
if (ex.op == TOKnew)
|
|
{
|
|
// See if initializer is a NewExp that can be allocated on the stack
|
|
NewExp ne = cast(NewExp)ex;
|
|
if (!(ne.newargs && ne.newargs.dim > 1) && type.toBasetype().ty == Tclass)
|
|
{
|
|
ne.onstack = 1;
|
|
onstack = 1;
|
|
if (type.isBaseOf(ne.newtype.semantic(loc, sc), null))
|
|
onstack = 2;
|
|
}
|
|
}
|
|
else if (ex.op == TOKfunction)
|
|
{
|
|
// or a delegate that doesn't escape a reference to the function
|
|
FuncDeclaration f = (cast(FuncExp)ex).fd;
|
|
f.tookAddressOf--;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Bugzilla 14166: Don't run CTFE for the temporary variables inside typeof
|
|
_init = _init.semantic(sc, type, sc.intypeof == 1 ? INITnointerpret : INITinterpret);
|
|
}
|
|
}
|
|
else if (parent.isAggregateDeclaration())
|
|
{
|
|
_scope = scx ? scx : sc.copy();
|
|
_scope.setNoFree();
|
|
}
|
|
else if (storage_class & (STCconst | STCimmutable | STCmanifest) || type.isConst() || type.isImmutable())
|
|
{
|
|
/* Because we may need the results of a const declaration in a
|
|
* subsequent type, such as an array dimension, before semantic2()
|
|
* gets ordinarily run, try to run semantic2() now.
|
|
* Ignore failure.
|
|
*/
|
|
if (!inferred)
|
|
{
|
|
uint errors = global.errors;
|
|
inuse++;
|
|
if (ei)
|
|
{
|
|
Expression exp = ei.exp.syntaxCopy();
|
|
bool needctfe = isDataseg() || (storage_class & STCmanifest);
|
|
if (needctfe)
|
|
sc = sc.startCTFE();
|
|
exp = exp.semantic(sc);
|
|
exp = resolveProperties(sc, exp);
|
|
if (needctfe)
|
|
sc = sc.endCTFE();
|
|
Type tb2 = type.toBasetype();
|
|
Type ti = exp.type.toBasetype();
|
|
/* The problem is the following code:
|
|
* struct CopyTest {
|
|
* double x;
|
|
* this(double a) { x = a * 10.0;}
|
|
* this(this) { x += 2.0; }
|
|
* }
|
|
* const CopyTest z = CopyTest(5.3); // ok
|
|
* const CopyTest w = z; // not ok, postblit not run
|
|
* static assert(w.x == 55.0);
|
|
* because the postblit doesn't get run on the initialization of w.
|
|
*/
|
|
if (ti.ty == Tstruct)
|
|
{
|
|
StructDeclaration sd = (cast(TypeStruct)ti).sym;
|
|
/* Look to see if initializer involves a copy constructor
|
|
* (which implies a postblit)
|
|
*/
|
|
// there is a copy constructor
|
|
// and exp is the same struct
|
|
if (sd.postblit && tb2.toDsymbol(null) == sd)
|
|
{
|
|
// The only allowable initializer is a (non-copy) constructor
|
|
if (exp.isLvalue())
|
|
error("of type struct %s uses this(this), which is not allowed in static initialization", tb2.toChars());
|
|
}
|
|
}
|
|
ei.exp = exp;
|
|
}
|
|
_init = _init.semantic(sc, type, INITinterpret);
|
|
inuse--;
|
|
if (global.errors > errors)
|
|
{
|
|
_init = new ErrorInitializer();
|
|
type = Type.terror;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_scope = scx ? scx : sc.copy();
|
|
_scope.setNoFree();
|
|
}
|
|
}
|
|
sc = sc.pop();
|
|
}
|
|
Ldtor:
|
|
/* Build code to execute destruction, if necessary
|
|
*/
|
|
edtor = callScopeDtor(sc);
|
|
if (edtor)
|
|
{
|
|
if (sc.func && storage_class & (STCstatic | STCgshared))
|
|
edtor = edtor.semantic(sc._module._scope);
|
|
else
|
|
edtor = edtor.semantic(sc);
|
|
version (none)
|
|
{
|
|
// currently disabled because of std.stdio.stdin, stdout and stderr
|
|
if (isDataseg() && !(storage_class & STCextern))
|
|
error("static storage variables cannot have destructors");
|
|
}
|
|
}
|
|
sem = SemanticDone;
|
|
if (type.toBasetype().ty == Terror)
|
|
errors = true;
|
|
}
|
|
|
|
override final void setFieldOffset(AggregateDeclaration ad, uint* poffset, bool isunion)
|
|
{
|
|
//printf("VarDeclaration::setFieldOffset(ad = %s) %s\n", ad.toChars(), toChars());
|
|
if (aliassym)
|
|
{
|
|
// If this variable was really a tuple, set the offsets for the tuple fields
|
|
TupleDeclaration v2 = aliassym.isTupleDeclaration();
|
|
assert(v2);
|
|
for (size_t i = 0; i < v2.objects.dim; i++)
|
|
{
|
|
RootObject o = (*v2.objects)[i];
|
|
assert(o.dyncast() == DYNCAST_EXPRESSION);
|
|
Expression e = cast(Expression)o;
|
|
assert(e.op == TOKdsymbol);
|
|
DsymbolExp se = cast(DsymbolExp)e;
|
|
se.s.setFieldOffset(ad, poffset, isunion);
|
|
}
|
|
return;
|
|
}
|
|
if (!isField())
|
|
return;
|
|
assert(!(storage_class & (STCstatic | STCextern | STCparameter | STCtls)));
|
|
/* Fields that are tuples appear both as part of TupleDeclarations and
|
|
* as members. That means ignore them if they are already a field.
|
|
*/
|
|
if (offset)
|
|
{
|
|
// already a field
|
|
*poffset = ad.structsize; // Bugzilla 13613
|
|
return;
|
|
}
|
|
for (size_t i = 0; i < ad.fields.dim; i++)
|
|
{
|
|
if (ad.fields[i] == this)
|
|
{
|
|
// already a field
|
|
*poffset = ad.structsize; // Bugzilla 13613
|
|
return;
|
|
}
|
|
}
|
|
// Check for forward referenced types which will fail the size() call
|
|
Type t = type.toBasetype();
|
|
if (storage_class & STCref)
|
|
{
|
|
// References are the size of a pointer
|
|
t = Type.tvoidptr;
|
|
}
|
|
if (t.ty == Tstruct || t.ty == Tsarray)
|
|
{
|
|
Type tv = t.baseElemOf();
|
|
if (tv.ty == Tstruct)
|
|
{
|
|
TypeStruct ts = cast(TypeStruct)tv;
|
|
if (ts.sym == ad)
|
|
{
|
|
const(char)* s = (t.ty == Tsarray) ? "static array of " : "";
|
|
ad.error("cannot have field %s with %ssame struct type", toChars(), s);
|
|
return;
|
|
}
|
|
if (ts.sym.sizeok != SIZEOKdone && ts.sym._scope)
|
|
ts.sym.semantic(null);
|
|
if (ts.sym.sizeok != SIZEOKdone)
|
|
{
|
|
ad.sizeok = SIZEOKfwd; // cannot finish; flag as forward referenced
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (t.ty == Tident)
|
|
{
|
|
ad.sizeok = SIZEOKfwd; // cannot finish; flag as forward referenced
|
|
return;
|
|
}
|
|
|
|
// List in ad.fields. Even if the type is error, it's necessary to avoid
|
|
// pointless error diagnostic "more initializers than fields" on struct literal.
|
|
ad.fields.push(this);
|
|
|
|
if (t.ty == Terror)
|
|
return;
|
|
|
|
uint memsize = cast(uint)t.size(loc); // size of member
|
|
uint memalignsize = Target.fieldalign(t); // size of member for alignment purposes
|
|
offset = AggregateDeclaration.placeField(poffset, memsize, memalignsize, alignment, &ad.structsize, &ad.alignsize, isunion);
|
|
//printf("\t%s: memalignsize = %d\n", toChars(), memalignsize);
|
|
//printf(" addField '%s' to '%s' at offset %d, size = %d\n", toChars(), ad.toChars(), offset, memsize);
|
|
}
|
|
|
|
override final void semantic2(Scope* sc)
|
|
{
|
|
if (sem < SemanticDone && inuse)
|
|
return;
|
|
//printf("VarDeclaration::semantic2('%s')\n", toChars());
|
|
// Inside unions, default to void initializers
|
|
if (!_init && sc.inunion && !toParent().isFuncDeclaration())
|
|
{
|
|
AggregateDeclaration aad = parent.isAggregateDeclaration();
|
|
if (aad)
|
|
{
|
|
if (aad.fields[0] == this)
|
|
{
|
|
int hasinit = 0;
|
|
for (size_t i = 1; i < aad.fields.dim; i++)
|
|
{
|
|
if (aad.fields[i]._init && !aad.fields[i]._init.isVoidInitializer())
|
|
{
|
|
hasinit = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (!hasinit)
|
|
_init = new ExpInitializer(loc, type.defaultInitLiteral(loc));
|
|
}
|
|
else
|
|
_init = new VoidInitializer(loc);
|
|
}
|
|
}
|
|
if (_init && !toParent().isFuncDeclaration())
|
|
{
|
|
inuse++;
|
|
version (none)
|
|
{
|
|
ExpInitializer ei = _init.isExpInitializer();
|
|
if (ei)
|
|
{
|
|
ei.exp.print();
|
|
printf("type = %p\n", ei.exp.type);
|
|
}
|
|
}
|
|
// Bugzilla 14166: Don't run CTFE for the temporary variables inside typeof
|
|
_init = _init.semantic(sc, type, sc.intypeof == 1 ? INITnointerpret : INITinterpret);
|
|
inuse--;
|
|
}
|
|
if (storage_class & STCmanifest)
|
|
{
|
|
version (none)
|
|
{
|
|
if ((type.ty == Tclass) && type.isMutable())
|
|
{
|
|
error("is mutable. Only const and immutable class enum are allowed, not %s", type.toChars());
|
|
}
|
|
else if (type.ty == Tpointer && type.nextOf().ty == Tstruct && type.nextOf().isMutable())
|
|
{
|
|
ExpInitializer ei = _init.isExpInitializer();
|
|
if (ei.exp.op == TOKaddress && (cast(AddrExp)ei.exp).e1.op == TOKstructliteral)
|
|
{
|
|
error("is a pointer to mutable struct. Only pointers to const or immutable struct enum are allowed, not %s", type.toChars());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (type.ty == Tclass && _init)
|
|
{
|
|
ExpInitializer ei = _init.isExpInitializer();
|
|
if (ei.exp.op == TOKclassreference)
|
|
error(": Unable to initialize enum with class or pointer to struct. Use static const variable instead.");
|
|
}
|
|
else if (type.ty == Tpointer && type.nextOf().ty == Tstruct)
|
|
{
|
|
ExpInitializer ei = _init.isExpInitializer();
|
|
if (ei && ei.exp.op == TOKaddress && (cast(AddrExp)ei.exp).e1.op == TOKstructliteral)
|
|
{
|
|
error(": Unable to initialize enum with class or pointer to struct. Use static const variable instead.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (_init && isThreadlocal())
|
|
{
|
|
if ((type.ty == Tclass) && type.isMutable() && !type.isShared())
|
|
{
|
|
ExpInitializer ei = _init.isExpInitializer();
|
|
if (ei && ei.exp.op == TOKclassreference)
|
|
error("is mutable. Only const or immutable class thread local variable are allowed, not %s", type.toChars());
|
|
}
|
|
else if (type.ty == Tpointer && type.nextOf().ty == Tstruct && type.nextOf().isMutable() && !type.nextOf().isShared())
|
|
{
|
|
ExpInitializer ei = _init.isExpInitializer();
|
|
if (ei && ei.exp.op == TOKaddress && (cast(AddrExp)ei.exp).e1.op == TOKstructliteral)
|
|
{
|
|
error("is a pointer to mutable struct. Only pointers to const, immutable or shared struct thread local variable are allowed, not %s", type.toChars());
|
|
}
|
|
}
|
|
}
|
|
sem = Semantic2Done;
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "variable";
|
|
}
|
|
|
|
override final AggregateDeclaration isThis()
|
|
{
|
|
AggregateDeclaration ad = null;
|
|
if (!(storage_class & (STCstatic | STCextern | STCmanifest | STCtemplateparameter | STCtls | STCgshared | STCctfe)))
|
|
{
|
|
for (Dsymbol s = this; s; s = s.parent)
|
|
{
|
|
ad = s.isMember();
|
|
if (ad)
|
|
break;
|
|
if (!s.parent || !s.parent.isTemplateMixin())
|
|
break;
|
|
}
|
|
}
|
|
return ad;
|
|
}
|
|
|
|
override final bool needThis()
|
|
{
|
|
//printf("VarDeclaration::needThis(%s, x%x)\n", toChars(), storage_class);
|
|
return isField();
|
|
}
|
|
|
|
override final bool isExport()
|
|
{
|
|
return protection.kind == PROTexport;
|
|
}
|
|
|
|
override final bool isImportedSymbol()
|
|
{
|
|
if (protection.kind == PROTexport && !_init && (storage_class & STCstatic || parent.isModule()))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/*******************************
|
|
* Does symbol go into data segment?
|
|
* Includes extern variables.
|
|
*/
|
|
override final bool isDataseg()
|
|
{
|
|
version (none)
|
|
{
|
|
printf("VarDeclaration::isDataseg(%p, '%s')\n", this, toChars());
|
|
printf("%llx, isModule: %p, isTemplateInstance: %p\n", storage_class & (STCstatic | STCconst), parent.isModule(), parent.isTemplateInstance());
|
|
printf("parent = '%s'\n", parent.toChars());
|
|
}
|
|
|
|
if (isdataseg == 0) // the value is not cached
|
|
{
|
|
isdataseg = 2; // The Variables does not go into the datasegment
|
|
|
|
if (!canTakeAddressOf())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Dsymbol parent = toParent();
|
|
if (!parent && !(storage_class & STCstatic))
|
|
{
|
|
error("forward referenced");
|
|
type = Type.terror;
|
|
}
|
|
else if (storage_class & (STCstatic | STCextern | STCtls | STCgshared) ||
|
|
parent.isModule() || parent.isTemplateInstance())
|
|
{
|
|
isdataseg = 1; // It is in the DataSegment
|
|
}
|
|
}
|
|
|
|
return (isdataseg == 1);
|
|
}
|
|
/************************************
|
|
* Does symbol go into thread local storage?
|
|
*/
|
|
override final bool isThreadlocal()
|
|
{
|
|
//printf("VarDeclaration::isThreadlocal(%p, '%s')\n", this, toChars());
|
|
/* Data defaults to being thread-local. It is not thread-local
|
|
* if it is immutable, const or shared.
|
|
*/
|
|
bool i = isDataseg() && !(storage_class & (STCimmutable | STCconst | STCshared | STCgshared));
|
|
//printf("\treturn %d\n", i);
|
|
return i;
|
|
}
|
|
|
|
/********************************************
|
|
* Can variable be read and written by CTFE?
|
|
*/
|
|
final bool isCTFE()
|
|
{
|
|
return (storage_class & STCctfe) != 0; // || !isDataseg();
|
|
}
|
|
|
|
final bool isOverlappedWith(VarDeclaration v)
|
|
{
|
|
return ( offset < v.offset + v.type.size() &&
|
|
v.offset < offset + type.size());
|
|
}
|
|
|
|
override final bool hasPointers()
|
|
{
|
|
//printf("VarDeclaration::hasPointers() %s, ty = %d\n", toChars(), type->ty);
|
|
return (!isDataseg() && type.hasPointers());
|
|
}
|
|
|
|
/*************************************
|
|
* Return true if we can take the address of this variable.
|
|
*/
|
|
final bool canTakeAddressOf()
|
|
{
|
|
return !(storage_class & STCmanifest);
|
|
}
|
|
|
|
/******************************************
|
|
* Return true if variable needs to call the destructor.
|
|
*/
|
|
final bool needsScopeDtor()
|
|
{
|
|
//printf("VarDeclaration::needsScopeDtor() %s\n", toChars());
|
|
return edtor && !noscope;
|
|
}
|
|
|
|
/******************************************
|
|
* If a variable has a scope destructor call, return call for it.
|
|
* Otherwise, return NULL.
|
|
*/
|
|
final Expression callScopeDtor(Scope* sc)
|
|
{
|
|
//printf("VarDeclaration::callScopeDtor() %s\n", toChars());
|
|
// Destruction of STCfield's is handled by buildDtor()
|
|
if (noscope || storage_class & (STCnodtor | STCref | STCout | STCfield))
|
|
{
|
|
return null;
|
|
}
|
|
Expression e = null;
|
|
// Destructors for structs and arrays of structs
|
|
Type tv = type.baseElemOf();
|
|
if (tv.ty == Tstruct)
|
|
{
|
|
StructDeclaration sd = (cast(TypeStruct)tv).sym;
|
|
if (!sd.dtor || !type.size())
|
|
return null;
|
|
if (type.toBasetype().ty == Tstruct)
|
|
{
|
|
// v.__xdtor()
|
|
e = new VarExp(loc, this);
|
|
/* This is a hack so we can call destructors on const/immutable objects.
|
|
* Need to add things like "const ~this()" and "immutable ~this()" to
|
|
* fix properly.
|
|
*/
|
|
e.type = e.type.mutableOf();
|
|
e = new DotVarExp(loc, e, sd.dtor, false);
|
|
e = new CallExp(loc, e);
|
|
}
|
|
else
|
|
{
|
|
// _ArrayDtor(v[0 .. n])
|
|
e = new VarExp(loc, this);
|
|
uinteger_t n = type.size() / sd.type.size();
|
|
e = new SliceExp(loc, e, new IntegerExp(loc, 0, Type.tsize_t), new IntegerExp(loc, n, Type.tsize_t));
|
|
// Prevent redundant bounds check
|
|
(cast(SliceExp)e).upperIsInBounds = true;
|
|
(cast(SliceExp)e).lowerIsLessThanUpper = true;
|
|
// This is a hack so we can call destructors on const/immutable objects.
|
|
e.type = sd.type.arrayOf();
|
|
e = new CallExp(loc, new IdentifierExp(loc, Id._ArrayDtor), e);
|
|
}
|
|
return e;
|
|
}
|
|
// Destructors for classes
|
|
if (storage_class & (STCauto | STCscope))
|
|
{
|
|
for (ClassDeclaration cd = type.isClassHandle(); cd; cd = cd.baseClass)
|
|
{
|
|
/* We can do better if there's a way with onstack
|
|
* classes to determine if there's no way the monitor
|
|
* could be set.
|
|
*/
|
|
//if (cd->isInterfaceDeclaration())
|
|
//error("interface %s cannot be scope", cd->toChars());
|
|
if (cd.cpp)
|
|
{
|
|
// Destructors are not supported on extern(C++) classes
|
|
break;
|
|
}
|
|
if (1 || onstack || cd.dtors.dim) // if any destructors
|
|
{
|
|
// delete this;
|
|
Expression ec;
|
|
ec = new VarExp(loc, this);
|
|
e = new DeleteExp(loc, ec);
|
|
e.type = Type.tvoid;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return e;
|
|
}
|
|
|
|
/*******************************************
|
|
* If variable has a constant expression initializer, get it.
|
|
* Otherwise, return null.
|
|
*/
|
|
final Expression getConstInitializer(bool needFullType = true)
|
|
{
|
|
assert(type && _init);
|
|
|
|
// Ungag errors when not speculative
|
|
uint oldgag = global.gag;
|
|
if (global.gag)
|
|
{
|
|
Dsymbol sym = toParent().isAggregateDeclaration();
|
|
if (sym && !sym.isSpeculative())
|
|
global.gag = 0;
|
|
}
|
|
|
|
if (_scope)
|
|
{
|
|
inuse++;
|
|
_init = _init.semantic(_scope, type, INITinterpret);
|
|
_scope = null;
|
|
inuse--;
|
|
}
|
|
|
|
Expression e = _init.toExpression(needFullType ? type : null);
|
|
global.gag = oldgag;
|
|
return e;
|
|
}
|
|
|
|
/*******************************************
|
|
* Helper function for the expansion of manifest constant.
|
|
*/
|
|
final Expression expandInitializer(Loc loc)
|
|
{
|
|
assert((storage_class & STCmanifest) && _init);
|
|
|
|
auto e = getConstInitializer();
|
|
if (!e)
|
|
{
|
|
.error(loc, "cannot make expression out of initializer for %s", toChars());
|
|
return new ErrorExp();
|
|
}
|
|
|
|
e = e.copy();
|
|
e.loc = loc; // for better error message
|
|
return e;
|
|
}
|
|
|
|
override final void checkCtorConstInit()
|
|
{
|
|
version (none)
|
|
{
|
|
/* doesn't work if more than one static ctor */
|
|
if (ctorinit == 0 && isCtorinit() && !isField())
|
|
error("missing initializer in static constructor for const variable");
|
|
}
|
|
}
|
|
|
|
/************************************
|
|
* Check to see if this variable is actually in an enclosing function
|
|
* rather than the current one.
|
|
* Returns true if error occurs.
|
|
*/
|
|
final bool checkNestedReference(Scope* sc, Loc loc)
|
|
{
|
|
//printf("VarDeclaration::checkNestedReference() %s\n", toChars());
|
|
if (sc.intypeof == 1 || (sc.flags & SCOPEctfe))
|
|
return false;
|
|
if (!parent || parent == sc.parent)
|
|
return false;
|
|
if (isDataseg() || (storage_class & STCmanifest))
|
|
return false;
|
|
|
|
// The current function
|
|
FuncDeclaration fdthis = sc.parent.isFuncDeclaration();
|
|
if (!fdthis)
|
|
return false; // out of function scope
|
|
|
|
Dsymbol p = toParent2();
|
|
|
|
// Function literals from fdthis to p must be delegates
|
|
checkNestedRef(fdthis, p);
|
|
|
|
// The function that this variable is in
|
|
FuncDeclaration fdv = p.isFuncDeclaration();
|
|
if (!fdv || fdv == fdthis)
|
|
return false;
|
|
|
|
// Add fdthis to nestedrefs[] if not already there
|
|
for (size_t i = 0; 1; i++)
|
|
{
|
|
if (i == nestedrefs.dim)
|
|
{
|
|
nestedrefs.push(fdthis);
|
|
break;
|
|
}
|
|
if (nestedrefs[i] == fdthis)
|
|
break;
|
|
}
|
|
|
|
/* __require and __ensure will always get called directly,
|
|
* so they never make outer functions closure.
|
|
*/
|
|
if (fdthis.ident == Id.require || fdthis.ident == Id.ensure)
|
|
return false;
|
|
|
|
//printf("\tfdv = %s\n", fdv.toChars());
|
|
//printf("\tfdthis = %s\n", fdthis.toChars());
|
|
if (loc.filename)
|
|
{
|
|
int lv = fdthis.getLevel(loc, sc, fdv);
|
|
if (lv == -2) // error
|
|
return true;
|
|
}
|
|
|
|
// Add this to fdv.closureVars[] if not already there
|
|
for (size_t i = 0; 1; i++)
|
|
{
|
|
if (i == fdv.closureVars.dim)
|
|
{
|
|
if (!sc.intypeof && !(sc.flags & SCOPEcompile))
|
|
fdv.closureVars.push(this);
|
|
break;
|
|
}
|
|
if (fdv.closureVars[i] == this)
|
|
break;
|
|
}
|
|
|
|
//printf("fdthis is %s\n", fdthis.toChars());
|
|
//printf("var %s in function %s is nested ref\n", toChars(), fdv.toChars());
|
|
// __dollar creates problems because it isn't a real variable Bugzilla 3326
|
|
if (ident == Id.dollar)
|
|
{
|
|
.error(loc, "cannnot use $ inside a function literal");
|
|
return true;
|
|
}
|
|
if (ident == Id.withSym) // Bugzilla 1759
|
|
{
|
|
ExpInitializer ez = _init.isExpInitializer();
|
|
assert(ez);
|
|
Expression e = ez.exp;
|
|
if (e.op == TOKconstruct || e.op == TOKblit)
|
|
e = (cast(AssignExp)e).e2;
|
|
return lambdaCheckForNestedRef(e, sc);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
override final Dsymbol toAlias()
|
|
{
|
|
//printf("VarDeclaration::toAlias('%s', this = %p, aliassym = %p)\n", toChars(), this, aliassym);
|
|
if ((!type || !type.deco) && _scope)
|
|
semantic(_scope);
|
|
|
|
assert(this != aliassym);
|
|
Dsymbol s = aliassym ? aliassym.toAlias() : this;
|
|
return s;
|
|
}
|
|
|
|
// Eliminate need for dynamic_cast
|
|
override final inout(VarDeclaration) isVarDeclaration() inout
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
* This is a shell around a back end symbol
|
|
*/
|
|
extern (C++) final class SymbolDeclaration : Declaration
|
|
{
|
|
public:
|
|
StructDeclaration dsym;
|
|
|
|
extern (D) this(Loc loc, StructDeclaration dsym)
|
|
{
|
|
super(dsym.ident);
|
|
this.loc = loc;
|
|
this.dsym = dsym;
|
|
storage_class |= STCconst;
|
|
}
|
|
|
|
// Eliminate need for dynamic_cast
|
|
override inout(SymbolDeclaration) isSymbolDeclaration() inout
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) class TypeInfoDeclaration : VarDeclaration
|
|
{
|
|
public:
|
|
Type tinfo;
|
|
|
|
final extern (D) this(Type tinfo, int internal)
|
|
{
|
|
super(Loc(), Type.dtypeinfo.type, tinfo.getTypeInfoIdent(internal), null);
|
|
this.tinfo = tinfo;
|
|
storage_class = STCstatic | STCgshared;
|
|
protection = Prot(PROTpublic);
|
|
linkage = LINKc;
|
|
}
|
|
|
|
final static TypeInfoDeclaration create(Type tinfo, int internal)
|
|
{
|
|
return new TypeInfoDeclaration(tinfo, internal);
|
|
}
|
|
|
|
override final Dsymbol syntaxCopy(Dsymbol s)
|
|
{
|
|
assert(0); // should never be produced by syntax
|
|
}
|
|
|
|
override final void semantic(Scope* sc)
|
|
{
|
|
assert(linkage == LINKc);
|
|
}
|
|
|
|
override final const(char)* toChars()
|
|
{
|
|
//printf("TypeInfoDeclaration::toChars() tinfo = %s\n", tinfo->toChars());
|
|
OutBuffer buf;
|
|
buf.writestring("typeid(");
|
|
buf.writestring(tinfo.toChars());
|
|
buf.writeByte(')');
|
|
return buf.extractString();
|
|
}
|
|
|
|
override final inout(TypeInfoDeclaration) isTypeInfoDeclaration() inout
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeInfoStructDeclaration : TypeInfoDeclaration
|
|
{
|
|
public:
|
|
extern (D) this(Type tinfo)
|
|
{
|
|
super(tinfo, 0);
|
|
if (!Type.typeinfostruct)
|
|
{
|
|
ObjectNotFound(Id.TypeInfo_Struct);
|
|
}
|
|
type = Type.typeinfostruct.type;
|
|
}
|
|
|
|
static TypeInfoStructDeclaration create(Type tinfo)
|
|
{
|
|
return new TypeInfoStructDeclaration(tinfo);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeInfoClassDeclaration : TypeInfoDeclaration
|
|
{
|
|
public:
|
|
extern (D) this(Type tinfo)
|
|
{
|
|
super(tinfo, 0);
|
|
if (!Type.typeinfoclass)
|
|
{
|
|
ObjectNotFound(Id.TypeInfo_Class);
|
|
}
|
|
type = Type.typeinfoclass.type;
|
|
}
|
|
|
|
static TypeInfoClassDeclaration create(Type tinfo)
|
|
{
|
|
return new TypeInfoClassDeclaration(tinfo);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeInfoInterfaceDeclaration : TypeInfoDeclaration
|
|
{
|
|
public:
|
|
extern (D) this(Type tinfo)
|
|
{
|
|
super(tinfo, 0);
|
|
if (!Type.typeinfointerface)
|
|
{
|
|
ObjectNotFound(Id.TypeInfo_Interface);
|
|
}
|
|
type = Type.typeinfointerface.type;
|
|
}
|
|
|
|
static TypeInfoInterfaceDeclaration create(Type tinfo)
|
|
{
|
|
return new TypeInfoInterfaceDeclaration(tinfo);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeInfoPointerDeclaration : TypeInfoDeclaration
|
|
{
|
|
public:
|
|
extern (D) this(Type tinfo)
|
|
{
|
|
super(tinfo, 0);
|
|
if (!Type.typeinfopointer)
|
|
{
|
|
ObjectNotFound(Id.TypeInfo_Pointer);
|
|
}
|
|
type = Type.typeinfopointer.type;
|
|
}
|
|
|
|
static TypeInfoPointerDeclaration create(Type tinfo)
|
|
{
|
|
return new TypeInfoPointerDeclaration(tinfo);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeInfoArrayDeclaration : TypeInfoDeclaration
|
|
{
|
|
public:
|
|
extern (D) this(Type tinfo)
|
|
{
|
|
super(tinfo, 0);
|
|
if (!Type.typeinfoarray)
|
|
{
|
|
ObjectNotFound(Id.TypeInfo_Array);
|
|
}
|
|
type = Type.typeinfoarray.type;
|
|
}
|
|
|
|
static TypeInfoArrayDeclaration create(Type tinfo)
|
|
{
|
|
return new TypeInfoArrayDeclaration(tinfo);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeInfoStaticArrayDeclaration : TypeInfoDeclaration
|
|
{
|
|
public:
|
|
extern (D) this(Type tinfo)
|
|
{
|
|
super(tinfo, 0);
|
|
if (!Type.typeinfostaticarray)
|
|
{
|
|
ObjectNotFound(Id.TypeInfo_StaticArray);
|
|
}
|
|
type = Type.typeinfostaticarray.type;
|
|
}
|
|
|
|
static TypeInfoStaticArrayDeclaration create(Type tinfo)
|
|
{
|
|
return new TypeInfoStaticArrayDeclaration(tinfo);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeInfoAssociativeArrayDeclaration : TypeInfoDeclaration
|
|
{
|
|
public:
|
|
extern (D) this(Type tinfo)
|
|
{
|
|
super(tinfo, 0);
|
|
if (!Type.typeinfoassociativearray)
|
|
{
|
|
ObjectNotFound(Id.TypeInfo_AssociativeArray);
|
|
}
|
|
type = Type.typeinfoassociativearray.type;
|
|
}
|
|
|
|
static TypeInfoAssociativeArrayDeclaration create(Type tinfo)
|
|
{
|
|
return new TypeInfoAssociativeArrayDeclaration(tinfo);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeInfoEnumDeclaration : TypeInfoDeclaration
|
|
{
|
|
public:
|
|
extern (D) this(Type tinfo)
|
|
{
|
|
super(tinfo, 0);
|
|
if (!Type.typeinfoenum)
|
|
{
|
|
ObjectNotFound(Id.TypeInfo_Enum);
|
|
}
|
|
type = Type.typeinfoenum.type;
|
|
}
|
|
|
|
static TypeInfoEnumDeclaration create(Type tinfo)
|
|
{
|
|
return new TypeInfoEnumDeclaration(tinfo);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeInfoFunctionDeclaration : TypeInfoDeclaration
|
|
{
|
|
public:
|
|
extern (D) this(Type tinfo)
|
|
{
|
|
super(tinfo, 0);
|
|
if (!Type.typeinfofunction)
|
|
{
|
|
ObjectNotFound(Id.TypeInfo_Function);
|
|
}
|
|
type = Type.typeinfofunction.type;
|
|
}
|
|
|
|
static TypeInfoFunctionDeclaration create(Type tinfo)
|
|
{
|
|
return new TypeInfoFunctionDeclaration(tinfo);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeInfoDelegateDeclaration : TypeInfoDeclaration
|
|
{
|
|
public:
|
|
extern (D) this(Type tinfo)
|
|
{
|
|
super(tinfo, 0);
|
|
if (!Type.typeinfodelegate)
|
|
{
|
|
ObjectNotFound(Id.TypeInfo_Delegate);
|
|
}
|
|
type = Type.typeinfodelegate.type;
|
|
}
|
|
|
|
static TypeInfoDelegateDeclaration create(Type tinfo)
|
|
{
|
|
return new TypeInfoDelegateDeclaration(tinfo);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeInfoTupleDeclaration : TypeInfoDeclaration
|
|
{
|
|
public:
|
|
extern (D) this(Type tinfo)
|
|
{
|
|
super(tinfo, 0);
|
|
if (!Type.typeinfotypelist)
|
|
{
|
|
ObjectNotFound(Id.TypeInfo_Tuple);
|
|
}
|
|
type = Type.typeinfotypelist.type;
|
|
}
|
|
|
|
static TypeInfoTupleDeclaration create(Type tinfo)
|
|
{
|
|
return new TypeInfoTupleDeclaration(tinfo);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeInfoConstDeclaration : TypeInfoDeclaration
|
|
{
|
|
public:
|
|
extern (D) this(Type tinfo)
|
|
{
|
|
super(tinfo, 0);
|
|
if (!Type.typeinfoconst)
|
|
{
|
|
ObjectNotFound(Id.TypeInfo_Const);
|
|
}
|
|
type = Type.typeinfoconst.type;
|
|
}
|
|
|
|
static TypeInfoConstDeclaration create(Type tinfo)
|
|
{
|
|
return new TypeInfoConstDeclaration(tinfo);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeInfoInvariantDeclaration : TypeInfoDeclaration
|
|
{
|
|
public:
|
|
extern (D) this(Type tinfo)
|
|
{
|
|
super(tinfo, 0);
|
|
if (!Type.typeinfoinvariant)
|
|
{
|
|
ObjectNotFound(Id.TypeInfo_Invariant);
|
|
}
|
|
type = Type.typeinfoinvariant.type;
|
|
}
|
|
|
|
static TypeInfoInvariantDeclaration create(Type tinfo)
|
|
{
|
|
return new TypeInfoInvariantDeclaration(tinfo);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeInfoSharedDeclaration : TypeInfoDeclaration
|
|
{
|
|
public:
|
|
extern (D) this(Type tinfo)
|
|
{
|
|
super(tinfo, 0);
|
|
if (!Type.typeinfoshared)
|
|
{
|
|
ObjectNotFound(Id.TypeInfo_Shared);
|
|
}
|
|
type = Type.typeinfoshared.type;
|
|
}
|
|
|
|
static TypeInfoSharedDeclaration create(Type tinfo)
|
|
{
|
|
return new TypeInfoSharedDeclaration(tinfo);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeInfoWildDeclaration : TypeInfoDeclaration
|
|
{
|
|
public:
|
|
extern (D) this(Type tinfo)
|
|
{
|
|
super(tinfo, 0);
|
|
if (!Type.typeinfowild)
|
|
{
|
|
ObjectNotFound(Id.TypeInfo_Wild);
|
|
}
|
|
type = Type.typeinfowild.type;
|
|
}
|
|
|
|
static TypeInfoWildDeclaration create(Type tinfo)
|
|
{
|
|
return new TypeInfoWildDeclaration(tinfo);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeInfoVectorDeclaration : TypeInfoDeclaration
|
|
{
|
|
public:
|
|
extern (D) this(Type tinfo)
|
|
{
|
|
super(tinfo, 0);
|
|
if (!Type.typeinfovector)
|
|
{
|
|
ObjectNotFound(Id.TypeInfo_Vector);
|
|
}
|
|
type = Type.typeinfovector.type;
|
|
}
|
|
|
|
static TypeInfoVectorDeclaration create(Type tinfo)
|
|
{
|
|
return new TypeInfoVectorDeclaration(tinfo);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
* For the "this" parameter to member functions
|
|
*/
|
|
extern (C++) final class ThisDeclaration : VarDeclaration
|
|
{
|
|
public:
|
|
extern (D) this(Loc loc, Type t)
|
|
{
|
|
super(loc, t, Id.This, null);
|
|
noscope = true;
|
|
}
|
|
|
|
override Dsymbol syntaxCopy(Dsymbol s)
|
|
{
|
|
assert(0); // should never be produced by syntax
|
|
}
|
|
|
|
override inout(ThisDeclaration) isThisDeclaration() inout
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|