mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-10 12:59:21 +03:00
789 lines
26 KiB
D
789 lines
26 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.dscope;
|
|
|
|
import core.stdc.stdio;
|
|
import core.stdc.string;
|
|
import ddmd.aggregate;
|
|
import ddmd.arraytypes;
|
|
import ddmd.attrib;
|
|
import ddmd.dclass;
|
|
import ddmd.declaration;
|
|
import ddmd.dmodule;
|
|
import ddmd.doc;
|
|
import ddmd.dsymbol;
|
|
import ddmd.dtemplate;
|
|
import ddmd.errors;
|
|
import ddmd.func;
|
|
import ddmd.globals;
|
|
import ddmd.globals;
|
|
import ddmd.id;
|
|
import ddmd.identifier;
|
|
import ddmd.mtype;
|
|
import ddmd.root.outbuffer;
|
|
import ddmd.root.rmem;
|
|
import ddmd.root.speller;
|
|
import ddmd.root.stringtable;
|
|
import ddmd.statement;
|
|
|
|
//version=LOGSEARCH;
|
|
|
|
extern (C++) bool mergeFieldInit(Loc loc, ref uint fieldInit, uint fi, bool mustInit)
|
|
{
|
|
if (fi != fieldInit)
|
|
{
|
|
// Have any branches returned?
|
|
bool aRet = (fi & CSXreturn) != 0;
|
|
bool bRet = (fieldInit & CSXreturn) != 0;
|
|
// Have any branches halted?
|
|
bool aHalt = (fi & CSXhalt) != 0;
|
|
bool bHalt = (fieldInit & CSXhalt) != 0;
|
|
bool ok;
|
|
if (aHalt && bHalt)
|
|
{
|
|
ok = true;
|
|
fieldInit = CSXhalt;
|
|
}
|
|
else if (!aHalt && aRet)
|
|
{
|
|
ok = !mustInit || (fi & CSXthis_ctor);
|
|
fieldInit = fieldInit;
|
|
}
|
|
else if (!bHalt && bRet)
|
|
{
|
|
ok = !mustInit || (fieldInit & CSXthis_ctor);
|
|
fieldInit = fi;
|
|
}
|
|
else if (aHalt)
|
|
{
|
|
ok = !mustInit || (fieldInit & CSXthis_ctor);
|
|
fieldInit = fieldInit;
|
|
}
|
|
else if (bHalt)
|
|
{
|
|
ok = !mustInit || (fi & CSXthis_ctor);
|
|
fieldInit = fi;
|
|
}
|
|
else
|
|
{
|
|
ok = !mustInit || !((fieldInit ^ fi) & CSXthis_ctor);
|
|
fieldInit |= fi;
|
|
}
|
|
return ok;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
enum CSXthis_ctor = 0x01; // called this()
|
|
enum CSXsuper_ctor = 0x02; // called super()
|
|
enum CSXthis = 0x04; // referenced this
|
|
enum CSXsuper = 0x08; // referenced super
|
|
enum CSXlabel = 0x10; // seen a label
|
|
enum CSXreturn = 0x20; // seen a return statement
|
|
enum CSXany_ctor = 0x40; // either this() or super() was called
|
|
enum CSXhalt = 0x80; // assert(0)
|
|
|
|
// Flags that would not be inherited beyond scope nesting
|
|
enum SCOPEctor = 0x0001; // constructor type
|
|
enum SCOPEcondition = 0x0004; // inside static if/assert condition
|
|
enum SCOPEdebug = 0x0008; // inside debug conditional
|
|
|
|
// Flags that would be inherited beyond scope nesting
|
|
enum SCOPEnoaccesscheck = 0x0002; // don't do access checks
|
|
enum SCOPEconstraint = 0x0010; // inside template constraint
|
|
enum SCOPEinvariant = 0x0020; // inside invariant code
|
|
enum SCOPErequire = 0x0040; // inside in contract code
|
|
enum SCOPEensure = 0x0060; // inside out contract code
|
|
enum SCOPEcontract = 0x0060; // [mask] we're inside contract code
|
|
enum SCOPEctfe = 0x0080; // inside a ctfe-only expression
|
|
enum SCOPEcompile = 0x0100; // inside __traits(compile)
|
|
enum SCOPEignoresymbolvisibility = 0x0200; // ignore symbol visibility (Bugzilla 15907)
|
|
enum SCOPEfree = 0x8000; // is on free list
|
|
|
|
enum SCOPEfullinst = 0x10000; // fully instantiate templates
|
|
|
|
struct Scope
|
|
{
|
|
Scope* enclosing; // enclosing Scope
|
|
|
|
Module _module; // Root module
|
|
ScopeDsymbol scopesym; // current symbol
|
|
ScopeDsymbol sds; // if in static if, and declaring new symbols,
|
|
// sds gets the addMember()
|
|
FuncDeclaration func; // function we are in
|
|
Dsymbol parent; // parent to use
|
|
LabelStatement slabel; // enclosing labelled statement
|
|
SwitchStatement sw; // enclosing switch statement
|
|
TryFinallyStatement tf; // enclosing try finally statement
|
|
OnScopeStatement os; // enclosing scope(xxx) statement
|
|
Statement sbreak; // enclosing statement that supports "break"
|
|
Statement scontinue; // enclosing statement that supports "continue"
|
|
ForeachStatement fes; // if nested function for ForeachStatement, this is it
|
|
Scope* callsc; // used for __FUNCTION__, __PRETTY_FUNCTION__ and __MODULE__
|
|
int inunion; // we're processing members of a union
|
|
int nofree; // set if shouldn't free it
|
|
int noctor; // set if constructor calls aren't allowed
|
|
int intypeof; // in typeof(exp)
|
|
VarDeclaration lastVar; // Previous symbol used to prevent goto-skips-init
|
|
|
|
/* If minst && !tinst, it's in definitely non-speculative scope (eg. module member scope).
|
|
* If !minst && !tinst, it's in definitely speculative scope (eg. template constraint).
|
|
* If minst && tinst, it's in instantiated code scope without speculation.
|
|
* If !minst && tinst, it's in instantiated code scope with speculation.
|
|
*/
|
|
Module minst; // root module where the instantiated templates should belong to
|
|
TemplateInstance tinst; // enclosing template instance
|
|
|
|
// primitive flow analysis for constructors
|
|
uint callSuper;
|
|
|
|
// primitive flow analysis for field initializations
|
|
uint* fieldinit;
|
|
size_t fieldinit_dim;
|
|
|
|
// alignment for struct members
|
|
structalign_t structalign = STRUCTALIGN_DEFAULT;
|
|
|
|
// linkage for external functions
|
|
LINK linkage = LINKd;
|
|
|
|
// mangle type
|
|
CPPMANGLE cppmangle = CPPMANGLE.def;
|
|
|
|
// inlining strategy for functions
|
|
PINLINE inlining = PINLINEdefault;
|
|
|
|
// protection for class members
|
|
Prot protection = Prot(PROTpublic);
|
|
int explicitProtection; // set if in an explicit protection attribute
|
|
|
|
StorageClass stc; // storage class
|
|
DeprecatedDeclaration depdecl; // customized deprecation message
|
|
|
|
uint flags;
|
|
|
|
// user defined attributes
|
|
UserAttributeDeclaration userAttribDecl;
|
|
|
|
version(IN_LLVM)
|
|
{
|
|
bool emitInstrumentation = true; // whether to emit instrumentation with -fprofile-instr-generate
|
|
}
|
|
|
|
DocComment* lastdc; // documentation comment for last symbol at this scope
|
|
uint[void*] anchorCounts; // lookup duplicate anchor name count
|
|
Identifier prevAnchor; // qualified symbol name of last doc anchor
|
|
|
|
extern (C++) static __gshared Scope* freelist;
|
|
|
|
extern (C++) static Scope* alloc()
|
|
{
|
|
if (freelist)
|
|
{
|
|
Scope* s = freelist;
|
|
freelist = s.enclosing;
|
|
//printf("freelist %p\n", s);
|
|
assert(s.flags & SCOPEfree);
|
|
s.flags &= ~SCOPEfree;
|
|
return s;
|
|
}
|
|
return new Scope();
|
|
}
|
|
|
|
extern (C++) static Scope* createGlobal(Module _module)
|
|
{
|
|
Scope* sc = Scope.alloc();
|
|
*sc = Scope.init;
|
|
sc._module = _module;
|
|
sc.minst = _module;
|
|
sc.scopesym = new ScopeDsymbol();
|
|
sc.scopesym.symtab = new DsymbolTable();
|
|
// Add top level package as member of this global scope
|
|
Dsymbol m = _module;
|
|
while (m.parent)
|
|
m = m.parent;
|
|
m.addMember(null, sc.scopesym);
|
|
m.parent = null; // got changed by addMember()
|
|
// Create the module scope underneath the global scope
|
|
sc = sc.push(_module);
|
|
sc.parent = _module;
|
|
return sc;
|
|
}
|
|
|
|
extern (C++) Scope* copy()
|
|
{
|
|
Scope* sc = Scope.alloc();
|
|
*sc = this;
|
|
/* Bugzilla 11777: The copied scope should not inherit fieldinit.
|
|
*/
|
|
sc.fieldinit = null;
|
|
return sc;
|
|
}
|
|
|
|
extern (C++) Scope* push()
|
|
{
|
|
Scope* s = copy();
|
|
//printf("Scope::push(this = %p) new = %p\n", this, s);
|
|
assert(!(flags & SCOPEfree));
|
|
s.scopesym = null;
|
|
s.sds = null;
|
|
s.enclosing = &this;
|
|
debug
|
|
{
|
|
if (enclosing)
|
|
assert(!(enclosing.flags & SCOPEfree));
|
|
if (s == enclosing)
|
|
{
|
|
printf("this = %p, enclosing = %p, enclosing->enclosing = %p\n", s, &this, enclosing);
|
|
}
|
|
assert(s != enclosing);
|
|
}
|
|
s.slabel = null;
|
|
s.nofree = 0;
|
|
s.fieldinit = saveFieldInit();
|
|
s.flags = (flags & (SCOPEcontract | SCOPEdebug | SCOPEctfe | SCOPEcompile | SCOPEconstraint |
|
|
SCOPEnoaccesscheck | SCOPEignoresymbolvisibility));
|
|
s.lastdc = null;
|
|
assert(&this != s);
|
|
return s;
|
|
}
|
|
|
|
extern (C++) Scope* push(ScopeDsymbol ss)
|
|
{
|
|
//printf("Scope::push(%s)\n", ss->toChars());
|
|
Scope* s = push();
|
|
s.scopesym = ss;
|
|
return s;
|
|
}
|
|
|
|
extern (C++) Scope* pop()
|
|
{
|
|
//printf("Scope::pop() %p nofree = %d\n", this, nofree);
|
|
Scope* enc = enclosing;
|
|
if (enclosing)
|
|
{
|
|
enclosing.callSuper |= callSuper;
|
|
if (fieldinit)
|
|
{
|
|
if (enclosing.fieldinit)
|
|
{
|
|
assert(fieldinit != enclosing.fieldinit);
|
|
foreach (i; 0 .. fieldinit_dim)
|
|
enclosing.fieldinit[i] |= fieldinit[i];
|
|
}
|
|
freeFieldinit();
|
|
}
|
|
}
|
|
if (!nofree)
|
|
{
|
|
enclosing = freelist;
|
|
freelist = &this;
|
|
flags |= SCOPEfree;
|
|
}
|
|
return enc;
|
|
}
|
|
|
|
void allocFieldinit(size_t dim)
|
|
{
|
|
fieldinit = cast(typeof(fieldinit))mem.xcalloc(typeof(*fieldinit).sizeof, dim);
|
|
fieldinit_dim = dim;
|
|
}
|
|
|
|
void freeFieldinit()
|
|
{
|
|
if (fieldinit)
|
|
mem.xfree(fieldinit);
|
|
fieldinit = null;
|
|
fieldinit_dim = 0;
|
|
}
|
|
|
|
extern (C++) Scope* startCTFE()
|
|
{
|
|
Scope* sc = this.push();
|
|
sc.flags = this.flags | SCOPEctfe;
|
|
version (none)
|
|
{
|
|
/* TODO: Currently this is not possible, because we need to
|
|
* unspeculative some types and symbols if they are necessary for the
|
|
* final executable. Consider:
|
|
*
|
|
* struct S(T) {
|
|
* string toString() const { return "instantiated"; }
|
|
* }
|
|
* enum x = S!int();
|
|
* void main() {
|
|
* // To call x.toString in runtime, compiler should unspeculative S!int.
|
|
* assert(x.toString() == "instantiated");
|
|
* }
|
|
*/
|
|
// If a template is instantiated from CT evaluated expression,
|
|
// compiler can elide its code generation.
|
|
sc.tinst = null;
|
|
sc.minst = null;
|
|
}
|
|
return sc;
|
|
}
|
|
|
|
extern (C++) Scope* endCTFE()
|
|
{
|
|
assert(flags & SCOPEctfe);
|
|
return pop();
|
|
}
|
|
|
|
extern (C++) void mergeCallSuper(Loc loc, uint cs)
|
|
{
|
|
// This does a primitive flow analysis to support the restrictions
|
|
// regarding when and how constructors can appear.
|
|
// It merges the results of two paths.
|
|
// The two paths are callSuper and cs; the result is merged into callSuper.
|
|
if (cs != callSuper)
|
|
{
|
|
// Have ALL branches called a constructor?
|
|
int aAll = (cs & (CSXthis_ctor | CSXsuper_ctor)) != 0;
|
|
int bAll = (callSuper & (CSXthis_ctor | CSXsuper_ctor)) != 0;
|
|
// Have ANY branches called a constructor?
|
|
bool aAny = (cs & CSXany_ctor) != 0;
|
|
bool bAny = (callSuper & CSXany_ctor) != 0;
|
|
// Have any branches returned?
|
|
bool aRet = (cs & CSXreturn) != 0;
|
|
bool bRet = (callSuper & CSXreturn) != 0;
|
|
// Have any branches halted?
|
|
bool aHalt = (cs & CSXhalt) != 0;
|
|
bool bHalt = (callSuper & CSXhalt) != 0;
|
|
bool ok = true;
|
|
if (aHalt && bHalt)
|
|
{
|
|
callSuper = CSXhalt;
|
|
}
|
|
else if ((!aHalt && aRet && !aAny && bAny) || (!bHalt && bRet && !bAny && aAny))
|
|
{
|
|
// If one has returned without a constructor call, there must be never
|
|
// have been ctor calls in the other.
|
|
ok = false;
|
|
}
|
|
else if (aHalt || aRet && aAll)
|
|
{
|
|
// If one branch has called a ctor and then exited, anything the
|
|
// other branch has done is OK (except returning without a
|
|
// ctor call, but we already checked that).
|
|
callSuper |= cs & (CSXany_ctor | CSXlabel);
|
|
}
|
|
else if (bHalt || bRet && bAll)
|
|
{
|
|
callSuper = cs | (callSuper & (CSXany_ctor | CSXlabel));
|
|
}
|
|
else
|
|
{
|
|
// Both branches must have called ctors, or both not.
|
|
ok = (aAll == bAll);
|
|
// If one returned without a ctor, we must remember that
|
|
// (Don't bother if we've already found an error)
|
|
if (ok && aRet && !aAny)
|
|
callSuper |= CSXreturn;
|
|
callSuper |= cs & (CSXany_ctor | CSXlabel);
|
|
}
|
|
if (!ok)
|
|
error(loc, "one path skips constructor");
|
|
}
|
|
}
|
|
|
|
extern (C++) uint* saveFieldInit()
|
|
{
|
|
uint* fi = null;
|
|
if (fieldinit) // copy
|
|
{
|
|
size_t dim = fieldinit_dim;
|
|
fi = cast(uint*)mem.xmalloc(uint.sizeof * dim);
|
|
version(IN_LLVM)
|
|
{ // ASan
|
|
memcpy(fi, fieldinit, (*fi).sizeof * dim);
|
|
}
|
|
else
|
|
{
|
|
for (size_t i = 0; i < dim; i++)
|
|
fi[i] = fieldinit[i];
|
|
}
|
|
}
|
|
return fi;
|
|
}
|
|
|
|
extern (C++) void mergeFieldInit(Loc loc, uint* fies)
|
|
{
|
|
if (fieldinit && fies)
|
|
{
|
|
FuncDeclaration f = func;
|
|
if (fes)
|
|
f = fes.func;
|
|
AggregateDeclaration ad = f.isAggregateMember2();
|
|
assert(ad);
|
|
for (size_t i = 0; i < ad.fields.dim; i++)
|
|
{
|
|
VarDeclaration v = ad.fields[i];
|
|
bool mustInit = (v.storage_class & STCnodefaultctor || v.type.needsNested());
|
|
if (!.mergeFieldInit(loc, fieldinit[i], fies[i], mustInit))
|
|
{
|
|
.error(loc, "one path skips field %s", ad.fields[i].toChars());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extern (C++) Module instantiatingModule()
|
|
{
|
|
// TODO: in speculative context, returning 'module' is correct?
|
|
return minst ? minst : _module;
|
|
}
|
|
|
|
/************************************
|
|
* Perform unqualified name lookup by following the chain of scopes up
|
|
* until found.
|
|
*
|
|
* Params:
|
|
* loc = location to use for error messages
|
|
* ident = name to look up
|
|
* pscopesym = if supplied and name is found, set to scope that ident was found in
|
|
* flags = modify search based on flags
|
|
*
|
|
* Returns:
|
|
* symbol if found, null if not
|
|
*/
|
|
extern (C++) Dsymbol search(Loc loc, Identifier ident, Dsymbol* pscopesym, int flags = IgnoreNone)
|
|
{
|
|
version (LOGSEARCH)
|
|
{
|
|
printf("Scope.search(%p, '%s' flags=x%x)\n", &this, ident.toChars(), flags);
|
|
// Print scope chain
|
|
for (Scope* sc = &this; sc; sc = sc.enclosing)
|
|
{
|
|
if (!sc.scopesym)
|
|
continue;
|
|
printf("\tscope %s\n", sc.scopesym.toChars());
|
|
}
|
|
|
|
static void printMsg(string txt, Dsymbol s)
|
|
{
|
|
printf("%.*s %s.%s, kind = '%s'\n", cast(int)msg.length, msg.ptr,
|
|
s.parent ? s.parent.toChars() : "", s.toChars(), s.kind());
|
|
}
|
|
}
|
|
|
|
// This function is called only for unqualified lookup
|
|
assert(!(flags & (SearchLocalsOnly | SearchImportsOnly)));
|
|
|
|
/* If ident is "start at module scope", only look at module scope
|
|
*/
|
|
if (ident == Id.empty)
|
|
{
|
|
// Look for module scope
|
|
for (Scope* sc = &this; sc; sc = sc.enclosing)
|
|
{
|
|
assert(sc != sc.enclosing);
|
|
if (!sc.scopesym)
|
|
continue;
|
|
if (Dsymbol s = sc.scopesym.isModule())
|
|
{
|
|
//printMsg("\tfound", s);
|
|
if (pscopesym)
|
|
*pscopesym = sc.scopesym;
|
|
return s;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
Dsymbol searchScopes(int flags)
|
|
{
|
|
for (Scope* sc = &this; sc; sc = sc.enclosing)
|
|
{
|
|
assert(sc != sc.enclosing);
|
|
if (!sc.scopesym)
|
|
continue;
|
|
//printf("\tlooking in scopesym '%s', kind = '%s', flags = x%x\n", sc.scopesym.toChars(), sc.scopesym.kind(), flags);
|
|
|
|
if (sc.scopesym.isModule())
|
|
flags |= SearchUnqualifiedModule; // tell Module.search() that SearchLocalsOnly is to be obeyed
|
|
|
|
if (Dsymbol s = sc.scopesym.search(loc, ident, flags))
|
|
{
|
|
if (!(flags & (SearchImportsOnly | IgnoreErrors)) &&
|
|
ident == Id.length && sc.scopesym.isArrayScopeSymbol() &&
|
|
sc.enclosing && sc.enclosing.search(loc, ident, null, flags))
|
|
{
|
|
warning(s.loc, "array 'length' hides other 'length' name in outer scope");
|
|
}
|
|
//printMsg("\tfound local", s);
|
|
if (pscopesym)
|
|
*pscopesym = sc.scopesym;
|
|
return s;
|
|
}
|
|
// Stop when we hit a module, but keep going if that is not just under the global scope
|
|
if (sc.scopesym.isModule() && !(sc.enclosing && !sc.enclosing.enclosing))
|
|
break;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
if (this.flags & SCOPEignoresymbolvisibility)
|
|
flags |= IgnoreSymbolVisibility;
|
|
|
|
Dsymbol sold = void;
|
|
if (global.params.bug10378 || global.params.check10378)
|
|
{
|
|
sold = searchScopes(flags | IgnoreSymbolVisibility);
|
|
if (!global.params.check10378)
|
|
return sold;
|
|
|
|
if (ident == Id.dollar) // Bugzilla 15825
|
|
return sold;
|
|
|
|
// Search both ways
|
|
}
|
|
|
|
// First look in local scopes
|
|
Dsymbol s = searchScopes(flags | SearchLocalsOnly);
|
|
version (LOGSEARCH) if (s) printMsg("-Scope.search() found local", s);
|
|
if (!s)
|
|
{
|
|
// Second look in imported modules
|
|
s = searchScopes(flags | SearchImportsOnly);
|
|
version (LOGSEARCH) if (s) printMsg("-Scope.search() found import", s);
|
|
|
|
/** Still find private symbols, so that symbols that weren't access
|
|
* checked by the compiler remain usable. Once the deprecation is over,
|
|
* this should be moved to search_correct instead.
|
|
*/
|
|
if (!s && !(flags & IgnoreSymbolVisibility))
|
|
{
|
|
s = searchScopes(flags | SearchLocalsOnly | IgnoreSymbolVisibility);
|
|
if (!s)
|
|
s = searchScopes(flags | SearchImportsOnly | IgnoreSymbolVisibility);
|
|
|
|
if (s && !(flags & IgnoreErrors))
|
|
.deprecation(loc, "%s is not visible from module %s", s.toPrettyChars(), _module.toChars());
|
|
version (LOGSEARCH) if (s) printMsg("-Scope.search() found imported private symbol", s);
|
|
}
|
|
}
|
|
if (global.params.check10378)
|
|
{
|
|
alias snew = s;
|
|
if (sold !is snew)
|
|
deprecation10378(loc, sold, snew);
|
|
if (global.params.bug10378)
|
|
s = sold;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/* A helper function to show deprecation message for new name lookup rule.
|
|
*/
|
|
extern (C++) static void deprecation10378(Loc loc, Dsymbol sold, Dsymbol snew)
|
|
{
|
|
// Bugzilla 15857
|
|
//
|
|
// The overloadset found via the new lookup rules is either
|
|
// equal or a subset of the overloadset found via the old
|
|
// lookup rules, so it suffices to compare the dimension to
|
|
// check for equality.
|
|
OverloadSet osold, osnew;
|
|
if (sold && (osold = sold.isOverloadSet()) !is null &&
|
|
snew && (osnew = snew.isOverloadSet()) !is null &&
|
|
osold.a.dim == osnew.a.dim)
|
|
return;
|
|
|
|
OutBuffer buf;
|
|
buf.writestring("local import search method found ");
|
|
if (osold)
|
|
buf.printf("%s %s (%d overloads)", sold.kind(), sold.toPrettyChars(), cast(int) osold.a.dim);
|
|
else if (sold)
|
|
buf.printf("%s %s", sold.kind(), sold.toPrettyChars());
|
|
else
|
|
buf.writestring("nothing");
|
|
buf.writestring(" instead of ");
|
|
if (osnew)
|
|
buf.printf("%s %s (%d overloads)", snew.kind(), snew.toPrettyChars(), cast(int) osnew.a.dim);
|
|
else if (snew)
|
|
buf.printf("%s %s", snew.kind(), snew.toPrettyChars());
|
|
else
|
|
buf.writestring("nothing");
|
|
|
|
deprecation(loc, buf.peekString());
|
|
}
|
|
|
|
extern (C++) Dsymbol search_correct(Identifier ident)
|
|
{
|
|
if (global.gag)
|
|
return null; // don't do it for speculative compiles; too time consuming
|
|
|
|
/************************************************
|
|
* Given the failed search attempt, try to find
|
|
* one with a close spelling.
|
|
*/
|
|
extern (D) void* scope_search_fp(const(char)* seed, ref int cost)
|
|
{
|
|
//printf("scope_search_fp('%s')\n", seed);
|
|
/* If not in the lexer's string table, it certainly isn't in the symbol table.
|
|
* Doing this first is a lot faster.
|
|
*/
|
|
size_t len = strlen(seed);
|
|
if (!len)
|
|
return null;
|
|
Identifier id = Identifier.lookup(seed, len);
|
|
if (!id)
|
|
return null;
|
|
Scope* sc = &this;
|
|
Module.clearCache();
|
|
Dsymbol scopesym = null;
|
|
Dsymbol s = sc.search(Loc(), id, &scopesym, IgnoreErrors);
|
|
if (s)
|
|
{
|
|
for (cost = 0; sc; sc = sc.enclosing, ++cost)
|
|
if (sc.scopesym == scopesym)
|
|
break;
|
|
if (scopesym != s.parent)
|
|
{
|
|
++cost; // got to the symbol through an import
|
|
if (s.prot().kind == PROTprivate)
|
|
return null;
|
|
}
|
|
}
|
|
return cast(void*)s;
|
|
}
|
|
|
|
return cast(Dsymbol)speller(ident.toChars(), &scope_search_fp, idchars);
|
|
}
|
|
|
|
extern (C++) Dsymbol insert(Dsymbol s)
|
|
{
|
|
if (VarDeclaration vd = s.isVarDeclaration())
|
|
{
|
|
if (lastVar)
|
|
vd.lastVar = lastVar;
|
|
lastVar = vd;
|
|
}
|
|
else if (WithScopeSymbol ss = s.isWithScopeSymbol())
|
|
{
|
|
if (VarDeclaration vd = ss.withstate.wthis)
|
|
{
|
|
if (lastVar)
|
|
vd.lastVar = lastVar;
|
|
lastVar = vd;
|
|
}
|
|
return null;
|
|
}
|
|
for (Scope* sc = &this; sc; sc = sc.enclosing)
|
|
{
|
|
//printf("\tsc = %p\n", sc);
|
|
if (sc.scopesym)
|
|
{
|
|
//printf("\t\tsc->scopesym = %p\n", sc->scopesym);
|
|
if (!sc.scopesym.symtab)
|
|
sc.scopesym.symtab = new DsymbolTable();
|
|
return sc.scopesym.symtabInsert(s);
|
|
}
|
|
}
|
|
assert(0);
|
|
}
|
|
|
|
/********************************************
|
|
* Search enclosing scopes for ClassDeclaration.
|
|
*/
|
|
extern (C++) ClassDeclaration getClassScope()
|
|
{
|
|
for (Scope* sc = &this; sc; sc = sc.enclosing)
|
|
{
|
|
if (!sc.scopesym)
|
|
continue;
|
|
ClassDeclaration cd = sc.scopesym.isClassDeclaration();
|
|
if (cd)
|
|
return cd;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/********************************************
|
|
* Search enclosing scopes for ClassDeclaration.
|
|
*/
|
|
extern (C++) AggregateDeclaration getStructClassScope()
|
|
{
|
|
for (Scope* sc = &this; sc; sc = sc.enclosing)
|
|
{
|
|
if (!sc.scopesym)
|
|
continue;
|
|
AggregateDeclaration ad = sc.scopesym.isClassDeclaration();
|
|
if (ad)
|
|
return ad;
|
|
ad = sc.scopesym.isStructDeclaration();
|
|
if (ad)
|
|
return ad;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/*******************************************
|
|
* For TemplateDeclarations, we need to remember the Scope
|
|
* where it was declared. So mark the Scope as not
|
|
* to be free'd.
|
|
*/
|
|
extern (C++) void setNoFree()
|
|
{
|
|
//int i = 0;
|
|
//printf("Scope::setNoFree(this = %p)\n", this);
|
|
for (Scope* sc = &this; sc; sc = sc.enclosing)
|
|
{
|
|
//printf("\tsc = %p\n", sc);
|
|
sc.nofree = 1;
|
|
assert(!(flags & SCOPEfree));
|
|
//assert(sc != sc->enclosing);
|
|
//assert(!sc->enclosing || sc != sc->enclosing->enclosing);
|
|
//if (++i == 10)
|
|
//assert(0);
|
|
}
|
|
}
|
|
|
|
extern (D) this(ref Scope sc)
|
|
{
|
|
this._module = sc._module;
|
|
this.scopesym = sc.scopesym;
|
|
this.sds = sc.sds;
|
|
this.enclosing = sc.enclosing;
|
|
this.parent = sc.parent;
|
|
this.sw = sc.sw;
|
|
this.tf = sc.tf;
|
|
this.os = sc.os;
|
|
this.tinst = sc.tinst;
|
|
this.minst = sc.minst;
|
|
this.sbreak = sc.sbreak;
|
|
this.scontinue = sc.scontinue;
|
|
this.fes = sc.fes;
|
|
this.callsc = sc.callsc;
|
|
this.structalign = sc.structalign;
|
|
this.func = sc.func;
|
|
this.slabel = sc.slabel;
|
|
this.linkage = sc.linkage;
|
|
this.cppmangle = sc.cppmangle;
|
|
this.inlining = sc.inlining;
|
|
this.protection = sc.protection;
|
|
this.explicitProtection = sc.explicitProtection;
|
|
this.stc = sc.stc;
|
|
this.depdecl = sc.depdecl;
|
|
this.inunion = sc.inunion;
|
|
this.nofree = sc.nofree;
|
|
this.noctor = sc.noctor;
|
|
this.intypeof = sc.intypeof;
|
|
this.lastVar = sc.lastVar;
|
|
this.callSuper = sc.callSuper;
|
|
this.fieldinit = sc.fieldinit;
|
|
this.fieldinit_dim = sc.fieldinit_dim;
|
|
this.flags = sc.flags;
|
|
this.lastdc = sc.lastdc;
|
|
this.anchorCounts = sc.anchorCounts;
|
|
this.prevAnchor = sc.prevAnchor;
|
|
this.userAttribDecl = sc.userAttribDecl;
|
|
}
|
|
}
|