mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-06 02:45:25 +03:00
5762 lines
200 KiB
D
5762 lines
200 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.func;
|
|
|
|
import core.stdc.stdio;
|
|
import core.stdc.string;
|
|
import ddmd.aggregate;
|
|
import ddmd.arraytypes;
|
|
import ddmd.attrib;
|
|
import ddmd.gluelayer;
|
|
import ddmd.builtin;
|
|
import ddmd.ctfeexpr;
|
|
import ddmd.dclass;
|
|
import ddmd.declaration;
|
|
import ddmd.delegatize;
|
|
import ddmd.dinterpret;
|
|
import ddmd.dmodule;
|
|
import ddmd.doc;
|
|
import ddmd.dscope;
|
|
import ddmd.dstruct;
|
|
import ddmd.dsymbol;
|
|
import ddmd.dtemplate;
|
|
import ddmd.errors;
|
|
import ddmd.escape;
|
|
import ddmd.expression;
|
|
import ddmd.globals;
|
|
import ddmd.hdrgen;
|
|
import ddmd.id;
|
|
import ddmd.identifier;
|
|
import ddmd.init;
|
|
import ddmd.inline;
|
|
import ddmd.mars;
|
|
import ddmd.mtype;
|
|
import ddmd.nogc;
|
|
import ddmd.objc;
|
|
import ddmd.opover;
|
|
import ddmd.root.filename;
|
|
import ddmd.root.outbuffer;
|
|
import ddmd.root.rmem;
|
|
import ddmd.root.rootobject;
|
|
import ddmd.statement;
|
|
import ddmd.target;
|
|
import ddmd.tokens;
|
|
import ddmd.visitor;
|
|
|
|
enum ILS : int
|
|
{
|
|
ILSuninitialized, // not computed yet
|
|
ILSno, // cannot inline
|
|
ILSyes, // can inline
|
|
}
|
|
|
|
alias ILSuninitialized = ILS.ILSuninitialized;
|
|
alias ILSno = ILS.ILSno;
|
|
alias ILSyes = ILS.ILSyes;
|
|
|
|
enum BUILTIN : int
|
|
{
|
|
BUILTINunknown = -1, // not known if this is a builtin
|
|
BUILTINno, // this is not a builtin
|
|
BUILTINyes, // this is a builtin
|
|
}
|
|
|
|
alias BUILTINunknown = BUILTIN.BUILTINunknown;
|
|
alias BUILTINno = BUILTIN.BUILTINno;
|
|
alias BUILTINyes = BUILTIN.BUILTINyes;
|
|
|
|
/* A visitor to walk entire statements and provides ability to replace any sub-statements.
|
|
*/
|
|
extern (C++) class StatementRewriteWalker : Visitor
|
|
{
|
|
alias visit = super.visit;
|
|
|
|
/* Point the currently visited statement.
|
|
* By using replaceCurrent() method, you can replace AST during walking.
|
|
*/
|
|
Statement* ps;
|
|
|
|
public:
|
|
final void visitStmt(ref Statement s)
|
|
{
|
|
ps = &s;
|
|
s.accept(this);
|
|
}
|
|
|
|
final void replaceCurrent(Statement s)
|
|
{
|
|
*ps = s;
|
|
}
|
|
|
|
override void visit(ErrorStatement s)
|
|
{
|
|
}
|
|
|
|
override void visit(PeelStatement s)
|
|
{
|
|
if (s.s)
|
|
visitStmt(s.s);
|
|
}
|
|
|
|
override void visit(ExpStatement s)
|
|
{
|
|
}
|
|
|
|
override void visit(DtorExpStatement s)
|
|
{
|
|
}
|
|
|
|
override void visit(CompileStatement s)
|
|
{
|
|
}
|
|
|
|
override void visit(CompoundStatement s)
|
|
{
|
|
if (s.statements && s.statements.dim)
|
|
{
|
|
for (size_t i = 0; i < s.statements.dim; i++)
|
|
{
|
|
if ((*s.statements)[i])
|
|
visitStmt((*s.statements)[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
override void visit(CompoundDeclarationStatement s)
|
|
{
|
|
visit(cast(CompoundStatement)s);
|
|
}
|
|
|
|
override void visit(UnrolledLoopStatement s)
|
|
{
|
|
if (s.statements && s.statements.dim)
|
|
{
|
|
for (size_t i = 0; i < s.statements.dim; i++)
|
|
{
|
|
if ((*s.statements)[i])
|
|
visitStmt((*s.statements)[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
override void visit(ScopeStatement s)
|
|
{
|
|
if (s.statement)
|
|
visitStmt(s.statement);
|
|
}
|
|
|
|
override void visit(WhileStatement s)
|
|
{
|
|
if (s._body)
|
|
visitStmt(s._body);
|
|
}
|
|
|
|
override void visit(DoStatement s)
|
|
{
|
|
if (s._body)
|
|
visitStmt(s._body);
|
|
}
|
|
|
|
override void visit(ForStatement s)
|
|
{
|
|
if (s._init)
|
|
visitStmt(s._init);
|
|
if (s._body)
|
|
visitStmt(s._body);
|
|
}
|
|
|
|
override void visit(ForeachStatement s)
|
|
{
|
|
if (s._body)
|
|
visitStmt(s._body);
|
|
}
|
|
|
|
override void visit(ForeachRangeStatement s)
|
|
{
|
|
if (s._body)
|
|
visitStmt(s._body);
|
|
}
|
|
|
|
override void visit(IfStatement s)
|
|
{
|
|
if (s.ifbody)
|
|
visitStmt(s.ifbody);
|
|
if (s.elsebody)
|
|
visitStmt(s.elsebody);
|
|
}
|
|
|
|
override void visit(ConditionalStatement s)
|
|
{
|
|
}
|
|
|
|
override void visit(PragmaStatement s)
|
|
{
|
|
}
|
|
|
|
override void visit(StaticAssertStatement s)
|
|
{
|
|
}
|
|
|
|
override void visit(SwitchStatement s)
|
|
{
|
|
if (s._body)
|
|
visitStmt(s._body);
|
|
}
|
|
|
|
override void visit(CaseStatement s)
|
|
{
|
|
if (s.statement)
|
|
visitStmt(s.statement);
|
|
}
|
|
|
|
override void visit(CaseRangeStatement s)
|
|
{
|
|
if (s.statement)
|
|
visitStmt(s.statement);
|
|
}
|
|
|
|
override void visit(DefaultStatement s)
|
|
{
|
|
if (s.statement)
|
|
visitStmt(s.statement);
|
|
}
|
|
|
|
override void visit(GotoDefaultStatement s)
|
|
{
|
|
}
|
|
|
|
override void visit(GotoCaseStatement s)
|
|
{
|
|
}
|
|
|
|
override void visit(SwitchErrorStatement s)
|
|
{
|
|
}
|
|
|
|
override void visit(ReturnStatement s)
|
|
{
|
|
}
|
|
|
|
override void visit(BreakStatement s)
|
|
{
|
|
}
|
|
|
|
override void visit(ContinueStatement s)
|
|
{
|
|
}
|
|
|
|
override void visit(SynchronizedStatement s)
|
|
{
|
|
if (s._body)
|
|
visitStmt(s._body);
|
|
}
|
|
|
|
override void visit(WithStatement s)
|
|
{
|
|
if (s._body)
|
|
visitStmt(s._body);
|
|
}
|
|
|
|
override void visit(TryCatchStatement s)
|
|
{
|
|
if (s._body)
|
|
visitStmt(s._body);
|
|
if (s.catches && s.catches.dim)
|
|
{
|
|
for (size_t i = 0; i < s.catches.dim; i++)
|
|
{
|
|
Catch c = (*s.catches)[i];
|
|
if (c && c.handler)
|
|
visitStmt(c.handler);
|
|
}
|
|
}
|
|
}
|
|
|
|
override void visit(TryFinallyStatement s)
|
|
{
|
|
if (s._body)
|
|
visitStmt(s._body);
|
|
if (s.finalbody)
|
|
visitStmt(s.finalbody);
|
|
}
|
|
|
|
override void visit(OnScopeStatement s)
|
|
{
|
|
}
|
|
|
|
override void visit(ThrowStatement s)
|
|
{
|
|
}
|
|
|
|
override void visit(DebugStatement s)
|
|
{
|
|
if (s.statement)
|
|
visitStmt(s.statement);
|
|
}
|
|
|
|
override void visit(GotoStatement s)
|
|
{
|
|
}
|
|
|
|
override void visit(LabelStatement s)
|
|
{
|
|
if (s.statement)
|
|
visitStmt(s.statement);
|
|
}
|
|
|
|
override void visit(AsmStatement s)
|
|
{
|
|
}
|
|
|
|
override void visit(ImportStatement s)
|
|
{
|
|
}
|
|
}
|
|
|
|
/* Tweak all return statements and dtor call for nrvo_var, for correct NRVO.
|
|
*/
|
|
extern (C++) final class NrvoWalker : StatementRewriteWalker
|
|
{
|
|
alias visit = super.visit;
|
|
public:
|
|
FuncDeclaration fd;
|
|
Scope* sc;
|
|
|
|
override void visit(ReturnStatement s)
|
|
{
|
|
// See if all returns are instead to be replaced with a goto returnLabel;
|
|
if (fd.returnLabel)
|
|
{
|
|
/* Rewrite:
|
|
* return exp;
|
|
* as:
|
|
* vresult = exp; goto Lresult;
|
|
*/
|
|
auto gs = new GotoStatement(s.loc, Id.returnLabel);
|
|
gs.label = fd.returnLabel;
|
|
Statement s1 = gs;
|
|
if (s.exp)
|
|
s1 = new CompoundStatement(s.loc, new ExpStatement(s.loc, s.exp), gs);
|
|
replaceCurrent(s1);
|
|
}
|
|
}
|
|
|
|
override void visit(TryFinallyStatement s)
|
|
{
|
|
DtorExpStatement des;
|
|
if (fd.nrvo_can && s.finalbody && (des = s.finalbody.isDtorExpStatement()) !is null && fd.nrvo_var == des.var)
|
|
{
|
|
/* Normally local variable dtors are called regardless exceptions.
|
|
* But for nrvo_var, its dtor should be called only when exception is thrown.
|
|
*
|
|
* Rewrite:
|
|
* try { s->body; } finally { nrvo_var->edtor; }
|
|
* // equivalent with:
|
|
* // s->body; scope(exit) nrvo_var->edtor;
|
|
* as:
|
|
* try { s->body; } catch(__o) { nrvo_var->edtor; throw __o; }
|
|
* // equivalent with:
|
|
* // s->body; scope(failure) nrvo_var->edtor;
|
|
*/
|
|
Statement sexception = new DtorExpStatement(Loc(), fd.nrvo_var.edtor, fd.nrvo_var);
|
|
Identifier id = Identifier.generateId("__o");
|
|
Statement handler = new PeelStatement(sexception);
|
|
if (sexception.blockExit(fd, false) & BEfallthru)
|
|
{
|
|
auto ts = new ThrowStatement(Loc(), new IdentifierExp(Loc(), id));
|
|
ts.internalThrow = true;
|
|
handler = new CompoundStatement(Loc(), handler, ts);
|
|
}
|
|
auto catches = new Catches();
|
|
auto ctch = new Catch(Loc(), null, id, handler);
|
|
ctch.internalCatch = true;
|
|
ctch.semantic(sc); // Run semantic to resolve identifier '__o'
|
|
catches.push(ctch);
|
|
Statement s2 = new TryCatchStatement(Loc(), s._body, catches);
|
|
replaceCurrent(s2);
|
|
s2.accept(this);
|
|
}
|
|
else
|
|
StatementRewriteWalker.visit(s);
|
|
}
|
|
}
|
|
|
|
enum FUNCFLAGpurityInprocess = 1; // working on determining purity
|
|
enum FUNCFLAGsafetyInprocess = 2; // working on determining safety
|
|
enum FUNCFLAGnothrowInprocess = 4; // working on determining nothrow
|
|
enum FUNCFLAGnogcInprocess = 8; // working on determining @nogc
|
|
enum FUNCFLAGreturnInprocess = 0x10; // working on inferring 'return' for parameters
|
|
enum FUNCFLAGinlineScanned = 0x20; // function has been scanned for inline possibilities
|
|
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) class FuncDeclaration : Declaration
|
|
{
|
|
public:
|
|
Types* fthrows; // Array of Type's of exceptions (not used)
|
|
Statement frequire;
|
|
Statement fensure;
|
|
Statement fbody;
|
|
|
|
FuncDeclarations foverrides; // functions this function overrides
|
|
FuncDeclaration fdrequire; // function that does the in contract
|
|
FuncDeclaration fdensure; // function that does the out contract
|
|
|
|
const(char)* mangleString; // mangled symbol created from mangleExact()
|
|
|
|
version(IN_LLVM)
|
|
{
|
|
// Argument lists for the __require/__ensure calls. NULL if not a virtual
|
|
// function with contracts.
|
|
Expressions* fdrequireParams;
|
|
Expressions* fdensureParams;
|
|
|
|
const(char)* intrinsicName;
|
|
uint priority;
|
|
|
|
// true if overridden with the pragma(LDC_allow_inline); statement
|
|
bool allowInlining = false;
|
|
|
|
// true if set with the pragma(LDC_never_inline); statement
|
|
bool neverInline = false;
|
|
|
|
// Whether to emit instrumentation code if -fprofile-instr-generate is specified,
|
|
// the value is set with pragma(LDC_profile_instr, true|false)
|
|
bool emitInstrumentation = true;
|
|
}
|
|
|
|
Identifier outId; // identifier for out statement
|
|
VarDeclaration vresult; // variable corresponding to outId
|
|
LabelDsymbol returnLabel; // where the return goes
|
|
|
|
// used to prevent symbols in different
|
|
// scopes from having the same name
|
|
DsymbolTable localsymtab;
|
|
VarDeclaration vthis; // 'this' parameter (member and nested)
|
|
VarDeclaration v_arguments; // '_arguments' parameter
|
|
Objc_FuncDeclaration objc;
|
|
|
|
version (IN_GCC)
|
|
{
|
|
VarDeclaration v_argptr; // '_argptr' variable
|
|
}
|
|
|
|
VarDeclaration v_argsave; // save area for args passed in registers for variadic functions
|
|
VarDeclarations* parameters; // Array of VarDeclaration's for parameters
|
|
DsymbolTable labtab; // statement label symbol table
|
|
Dsymbol overnext; // next in overload list
|
|
FuncDeclaration overnext0; // next in overload list (only used during IFTI)
|
|
Loc endloc; // location of closing curly bracket
|
|
int vtblIndex = -1; // for member functions, index into vtbl[]
|
|
bool naked; // true if naked
|
|
ILS inlineStatusStmt = ILSuninitialized;
|
|
ILS inlineStatusExp = ILSuninitialized;
|
|
PINLINE inlining = PINLINEdefault;
|
|
|
|
CompiledCtfeFunction* ctfeCode; // Compiled code for interpreter
|
|
int inlineNest; // !=0 if nested inline
|
|
bool isArrayOp; // true if array operation
|
|
// true if errors in semantic3 this function's frame ptr
|
|
bool semantic3Errors;
|
|
ForeachStatement fes; // if foreach body, this is the foreach
|
|
BaseClass* interfaceVirtual; // if virtual, but only appears in base interface vtbl[]
|
|
bool introducing; // true if 'introducing' function
|
|
// if !=NULL, then this is the type
|
|
// of the 'introducing' function
|
|
// this one is overriding
|
|
Type tintro;
|
|
bool inferRetType; // true if return type is to be inferred
|
|
StorageClass storage_class2; // storage class for template onemember's
|
|
|
|
// Things that should really go into Scope
|
|
|
|
// 1 if there's a return exp; statement
|
|
// 2 if there's a throw statement
|
|
// 4 if there's an assert(0)
|
|
// 8 if there's inline asm
|
|
int hasReturnExp;
|
|
|
|
// Support for NRVO (named return value optimization)
|
|
bool nrvo_can = true; // true means we can do it
|
|
VarDeclaration nrvo_var; // variable to replace with shidden
|
|
Symbol* shidden; // hidden pointer passed to function
|
|
|
|
ReturnStatements* returns;
|
|
|
|
GotoStatements* gotos; // Gotos with forward references
|
|
|
|
// set if this is a known, builtin function we can evaluate at compile time
|
|
BUILTIN builtin = BUILTINunknown;
|
|
|
|
// set if someone took the address of this function
|
|
int tookAddressOf;
|
|
|
|
bool requiresClosure; // this function needs a closure
|
|
|
|
// local variables in this function which are referenced by nested functions
|
|
VarDeclarations closureVars;
|
|
// Sibling nested functions which called this one
|
|
FuncDeclarations siblingCallers;
|
|
|
|
uint flags; // FUNCFLAGxxxxx
|
|
|
|
final extern (D) this(Loc loc, Loc endloc, Identifier id, StorageClass storage_class, Type type)
|
|
{
|
|
super(id);
|
|
objc = Objc_FuncDeclaration(this);
|
|
//printf("FuncDeclaration(id = '%s', type = %p)\n", id->toChars(), type);
|
|
//printf("storage_class = x%x\n", storage_class);
|
|
this.storage_class = storage_class;
|
|
this.type = type;
|
|
if (type)
|
|
{
|
|
// Normalize storage_class, because function-type related attributes
|
|
// are already set in the 'type' in parsing phase.
|
|
this.storage_class &= ~(STC_TYPECTOR | STC_FUNCATTR);
|
|
}
|
|
this.loc = loc;
|
|
this.endloc = endloc;
|
|
/* The type given for "infer the return type" is a TypeFunction with
|
|
* NULL for the return type.
|
|
*/
|
|
inferRetType = (type && type.nextOf() is null);
|
|
}
|
|
|
|
override Dsymbol syntaxCopy(Dsymbol s)
|
|
{
|
|
//printf("FuncDeclaration::syntaxCopy('%s')\n", toChars());
|
|
FuncDeclaration f = s ? cast(FuncDeclaration)s : new FuncDeclaration(loc, endloc, ident, storage_class, type.syntaxCopy());
|
|
f.outId = outId;
|
|
f.frequire = frequire ? frequire.syntaxCopy() : null;
|
|
f.fensure = fensure ? fensure.syntaxCopy() : null;
|
|
f.fbody = fbody ? fbody.syntaxCopy() : null;
|
|
assert(!fthrows); // deprecated
|
|
version(IN_LLVM)
|
|
{
|
|
f.intrinsicName = intrinsicName ? strdup(intrinsicName) : null;
|
|
}
|
|
return f;
|
|
}
|
|
|
|
version(IN_LLVM)
|
|
{
|
|
final private Parameters* outToRef(Parameters* params)
|
|
{
|
|
auto result = new Parameters();
|
|
|
|
int outToRefDg(size_t n, Parameter p)
|
|
{
|
|
if (p.storageClass & STCout)
|
|
{
|
|
p = p.syntaxCopy();
|
|
p.storageClass &= ~STCout;
|
|
p.storageClass |= STCref;
|
|
}
|
|
result.push(p);
|
|
return 0;
|
|
}
|
|
|
|
Parameter._foreach(params, &outToRefDg);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// Do the semantic analysis on the external interface to the function.
|
|
override void semantic(Scope* sc)
|
|
{
|
|
TypeFunction f;
|
|
AggregateDeclaration ad;
|
|
InterfaceDeclaration id;
|
|
version (none)
|
|
{
|
|
printf("FuncDeclaration::semantic(sc = %p, this = %p, '%s', linkage = %d)\n", sc, this, toPrettyChars(), sc.linkage);
|
|
if (isFuncLiteralDeclaration())
|
|
printf("\tFuncLiteralDeclaration()\n");
|
|
printf("sc->parent = %s, parent = %s\n", sc.parent.toChars(), parent ? parent.toChars() : "");
|
|
printf("type: %p, %s\n", type, type.toChars());
|
|
}
|
|
if (semanticRun != PASSinit && isFuncLiteralDeclaration())
|
|
{
|
|
/* Member functions that have return types that are
|
|
* forward references can have semantic() run more than
|
|
* once on them.
|
|
* See test\interface2.d, test20
|
|
*/
|
|
return;
|
|
}
|
|
if (semanticRun >= PASSsemanticdone)
|
|
return;
|
|
assert(semanticRun <= PASSsemantic);
|
|
semanticRun = PASSsemantic;
|
|
parent = sc.parent;
|
|
Dsymbol parent = toParent();
|
|
if (_scope)
|
|
{
|
|
sc = _scope;
|
|
_scope = null;
|
|
}
|
|
uint dprogress_save = Module.dprogress;
|
|
foverrides.setDim(0); // reset in case semantic() is being retried for this function
|
|
storage_class |= sc.stc & ~STCref;
|
|
ad = isThis();
|
|
if (ad)
|
|
{
|
|
storage_class |= ad.storage_class & (STC_TYPECTOR | STCsynchronized);
|
|
if (StructDeclaration sd = ad.isStructDeclaration())
|
|
sd.makeNested();
|
|
}
|
|
if (sc.func)
|
|
storage_class |= sc.func.storage_class & STCdisable;
|
|
// Remove prefix storage classes silently.
|
|
if ((storage_class & STC_TYPECTOR) && !(ad || isNested()))
|
|
storage_class &= ~STC_TYPECTOR;
|
|
//printf("function storage_class = x%llx, sc->stc = x%llx, %x\n", storage_class, sc->stc, Declaration::isFinal());
|
|
FuncLiteralDeclaration fld = isFuncLiteralDeclaration();
|
|
if (fld && fld.treq)
|
|
{
|
|
Type treq = fld.treq;
|
|
assert(treq.nextOf().ty == Tfunction);
|
|
if (treq.ty == Tdelegate)
|
|
fld.tok = TOKdelegate;
|
|
else if (treq.ty == Tpointer && treq.nextOf().ty == Tfunction)
|
|
fld.tok = TOKfunction;
|
|
else
|
|
assert(0);
|
|
linkage = (cast(TypeFunction)treq.nextOf()).linkage;
|
|
}
|
|
else
|
|
linkage = sc.linkage;
|
|
inlining = sc.inlining;
|
|
protection = sc.protection;
|
|
userAttribDecl = sc.userAttribDecl;
|
|
version(IN_LLVM)
|
|
{
|
|
emitInstrumentation = sc.emitInstrumentation;
|
|
}
|
|
|
|
if (!originalType)
|
|
originalType = type.syntaxCopy();
|
|
if (!type.deco)
|
|
{
|
|
sc = sc.push();
|
|
sc.stc |= storage_class & (STCdisable | STCdeprecated); // forward to function type
|
|
TypeFunction tf = cast(TypeFunction)type;
|
|
if (sc.func)
|
|
{
|
|
/* If the parent is @safe, then this function defaults to safe too.
|
|
*/
|
|
if (tf.trust == TRUSTdefault)
|
|
{
|
|
FuncDeclaration fd = sc.func;
|
|
/* If the parent's @safe-ty is inferred, then this function's @safe-ty needs
|
|
* to be inferred first.
|
|
* If this function's @safe-ty is inferred, then it needs to be infeerd first.
|
|
* (local template function inside @safe function can be inferred to @system).
|
|
*/
|
|
if (fd.isSafeBypassingInference() && !isInstantiated())
|
|
tf.trust = TRUSTsafe; // default to @safe
|
|
}
|
|
/* If the nesting parent is pure without inference,
|
|
* then this function defaults to pure too.
|
|
*
|
|
* auto foo() pure {
|
|
* auto bar() {} // become a weak purity funciton
|
|
* class C { // nested class
|
|
* auto baz() {} // become a weak purity funciton
|
|
* }
|
|
*
|
|
* static auto boo() {} // typed as impure
|
|
* // Even though, boo cannot call any impure functions.
|
|
* // See also Expression::checkPurity().
|
|
* }
|
|
*/
|
|
if (tf.purity == PUREimpure && (isNested() || isThis()))
|
|
{
|
|
FuncDeclaration fd = null;
|
|
for (Dsymbol p = toParent2(); p; p = p.toParent2())
|
|
{
|
|
if (AggregateDeclaration adx = p.isAggregateDeclaration())
|
|
{
|
|
if (adx.isNested())
|
|
continue;
|
|
break;
|
|
}
|
|
if ((fd = p.isFuncDeclaration()) !is null)
|
|
break;
|
|
}
|
|
/* If the parent's purity is inferred, then this function's purity needs
|
|
* to be inferred first.
|
|
*/
|
|
if (fd && fd.isPureBypassingInference() >= PUREweak && !isInstantiated())
|
|
{
|
|
tf.purity = PUREfwdref; // default to pure
|
|
}
|
|
}
|
|
}
|
|
if (tf.isref)
|
|
sc.stc |= STCref;
|
|
if (tf.isnothrow)
|
|
sc.stc |= STCnothrow;
|
|
if (tf.isnogc)
|
|
sc.stc |= STCnogc;
|
|
if (tf.isproperty)
|
|
sc.stc |= STCproperty;
|
|
if (tf.purity == PUREfwdref)
|
|
sc.stc |= STCpure;
|
|
if (tf.trust != TRUSTdefault)
|
|
sc.stc &= ~(STCsafe | STCsystem | STCtrusted);
|
|
if (tf.trust == TRUSTsafe)
|
|
sc.stc |= STCsafe;
|
|
if (tf.trust == TRUSTsystem)
|
|
sc.stc |= STCsystem;
|
|
if (tf.trust == TRUSTtrusted)
|
|
sc.stc |= STCtrusted;
|
|
if (isCtorDeclaration())
|
|
{
|
|
sc.flags |= SCOPEctor;
|
|
Type tret = ad.handleType();
|
|
assert(tret);
|
|
tret = tret.addStorageClass(storage_class | sc.stc);
|
|
tret = tret.addMod(type.mod);
|
|
tf.next = tret;
|
|
if (ad.isStructDeclaration())
|
|
sc.stc |= STCref;
|
|
}
|
|
sc.linkage = linkage;
|
|
if (!tf.isNaked() && !(isThis() || isNested()))
|
|
{
|
|
OutBuffer buf;
|
|
MODtoBuffer(&buf, tf.mod);
|
|
error("without 'this' cannot be %s", buf.peekString());
|
|
tf.mod = 0; // remove qualifiers
|
|
}
|
|
/* Apply const, immutable, wild and shared storage class
|
|
* to the function type. Do this before type semantic.
|
|
*/
|
|
StorageClass stc = storage_class;
|
|
if (type.isImmutable())
|
|
stc |= STCimmutable;
|
|
if (type.isConst())
|
|
stc |= STCconst;
|
|
if (type.isShared() || storage_class & STCsynchronized)
|
|
stc |= STCshared;
|
|
if (type.isWild())
|
|
stc |= STCwild;
|
|
switch (stc & STC_TYPECTOR)
|
|
{
|
|
case STCimmutable:
|
|
case STCimmutable | STCconst:
|
|
case STCimmutable | STCwild:
|
|
case STCimmutable | STCwild | STCconst:
|
|
case STCimmutable | STCshared:
|
|
case STCimmutable | STCshared | STCconst:
|
|
case STCimmutable | STCshared | STCwild:
|
|
case STCimmutable | STCshared | STCwild | STCconst:
|
|
// Don't use immutableOf(), as that will do a merge()
|
|
type = type.makeImmutable();
|
|
break;
|
|
case STCconst:
|
|
type = type.makeConst();
|
|
break;
|
|
case STCwild:
|
|
type = type.makeWild();
|
|
break;
|
|
case STCwild | STCconst:
|
|
type = type.makeWildConst();
|
|
break;
|
|
case STCshared:
|
|
type = type.makeShared();
|
|
break;
|
|
case STCshared | STCconst:
|
|
type = type.makeSharedConst();
|
|
break;
|
|
case STCshared | STCwild:
|
|
type = type.makeSharedWild();
|
|
break;
|
|
case STCshared | STCwild | STCconst:
|
|
type = type.makeSharedWildConst();
|
|
break;
|
|
case 0:
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
type = type.semantic(loc, sc);
|
|
sc = sc.pop();
|
|
}
|
|
if (type.ty != Tfunction)
|
|
{
|
|
if (type.ty != Terror)
|
|
{
|
|
error("%s must be a function instead of %s", toChars(), type.toChars());
|
|
type = Type.terror;
|
|
}
|
|
errors = true;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// Merge back function attributes into 'originalType'.
|
|
// It's used for mangling, ddoc, and json output.
|
|
TypeFunction tfo = cast(TypeFunction)originalType;
|
|
TypeFunction tfx = cast(TypeFunction)type;
|
|
tfo.mod = tfx.mod;
|
|
tfo.isref = tfx.isref;
|
|
tfo.isnothrow = tfx.isnothrow;
|
|
tfo.isnogc = tfx.isnogc;
|
|
tfo.isproperty = tfx.isproperty;
|
|
tfo.purity = tfx.purity;
|
|
tfo.trust = tfx.trust;
|
|
storage_class &= ~(STC_TYPECTOR | STC_FUNCATTR);
|
|
}
|
|
f = cast(TypeFunction)type;
|
|
size_t nparams = Parameter.dim(f.parameters);
|
|
if ((storage_class & STCauto) && !f.isref && !inferRetType)
|
|
error("storage class 'auto' has no effect if return type is not inferred");
|
|
if (storage_class & STCscope)
|
|
error("functions cannot be scope");
|
|
if (isAbstract() && !isVirtual())
|
|
{
|
|
const(char)* sfunc;
|
|
if (isStatic())
|
|
sfunc = "static";
|
|
else if (protection.kind == PROTprivate || protection.kind == PROTpackage)
|
|
sfunc = protectionToChars(protection.kind);
|
|
else
|
|
sfunc = "non-virtual";
|
|
error("%s functions cannot be abstract", sfunc);
|
|
}
|
|
if (isOverride() && !isVirtual())
|
|
{
|
|
PROTKIND kind = prot().kind;
|
|
if ((kind == PROTprivate || kind == PROTpackage) && isMember())
|
|
error("%s method is not virtual and cannot override", protectionToChars(kind));
|
|
else
|
|
error("cannot override a non-virtual function");
|
|
}
|
|
if (isAbstract() && isFinalFunc())
|
|
error("cannot be both final and abstract");
|
|
version (none)
|
|
{
|
|
if (isAbstract() && fbody)
|
|
error("abstract functions cannot have bodies");
|
|
}
|
|
version (none)
|
|
{
|
|
if (isStaticConstructor() || isStaticDestructor())
|
|
{
|
|
if (!isStatic() || type.nextOf().ty != Tvoid)
|
|
error("static constructors / destructors must be static void");
|
|
if (f.arguments && f.arguments.dim)
|
|
error("static constructors / destructors must have empty parameter list");
|
|
// BUG: check for invalid storage classes
|
|
}
|
|
}
|
|
id = parent.isInterfaceDeclaration();
|
|
if (id)
|
|
{
|
|
storage_class |= STCabstract;
|
|
if (isCtorDeclaration() || isPostBlitDeclaration() || isDtorDeclaration() || isInvariantDeclaration() || isNewDeclaration() || isDelete())
|
|
error("constructors, destructors, postblits, invariants, new and delete functions are not allowed in interface %s", id.toChars());
|
|
if (fbody && isVirtual())
|
|
error("function body only allowed in final functions in interface %s", id.toChars());
|
|
}
|
|
if (UnionDeclaration ud = parent.isUnionDeclaration())
|
|
{
|
|
if (isPostBlitDeclaration() || isDtorDeclaration() || isInvariantDeclaration())
|
|
error("destructors, postblits and invariants are not allowed in union %s", ud.toChars());
|
|
}
|
|
/* Contracts can only appear without a body when they are virtual interface functions
|
|
*/
|
|
if (!fbody && (fensure || frequire) && !(id && isVirtual()))
|
|
error("in and out contracts require function body");
|
|
if (StructDeclaration sd = parent.isStructDeclaration())
|
|
{
|
|
if (isCtorDeclaration())
|
|
{
|
|
goto Ldone;
|
|
}
|
|
}
|
|
if (ClassDeclaration cd = parent.isClassDeclaration())
|
|
{
|
|
if (isCtorDeclaration())
|
|
{
|
|
goto Ldone;
|
|
}
|
|
if (storage_class & STCabstract)
|
|
cd.isabstract = true;
|
|
// if static function, do not put in vtbl[]
|
|
if (!isVirtual())
|
|
{
|
|
//printf("\tnot virtual\n");
|
|
goto Ldone;
|
|
}
|
|
// Suppress further errors if the return type is an error
|
|
if (type.nextOf() == Type.terror)
|
|
goto Ldone;
|
|
bool may_override = false;
|
|
for (size_t i = 0; i < cd.baseclasses.dim; i++)
|
|
{
|
|
BaseClass* b = (*cd.baseclasses)[i];
|
|
ClassDeclaration cbd = b.type.toBasetype().isClassHandle();
|
|
if (!cbd)
|
|
continue;
|
|
for (size_t j = 0; j < cbd.vtbl.dim; j++)
|
|
{
|
|
FuncDeclaration f2 = cbd.vtbl[j].isFuncDeclaration();
|
|
if (!f2 || f2.ident != ident)
|
|
continue;
|
|
if (cbd.parent && cbd.parent.isTemplateInstance())
|
|
{
|
|
if (!f2.functionSemantic())
|
|
goto Ldone;
|
|
}
|
|
may_override = true;
|
|
}
|
|
}
|
|
if (may_override && type.nextOf() is null)
|
|
{
|
|
/* If same name function exists in base class but 'this' is auto return,
|
|
* cannot find index of base class's vtbl[] to override.
|
|
*/
|
|
error("return type inference is not supported if may override base class function");
|
|
}
|
|
/* Find index of existing function in base class's vtbl[] to override
|
|
* (the index will be the same as in cd's current vtbl[])
|
|
*/
|
|
int vi = cd.baseClass ? findVtblIndex(&cd.baseClass.vtbl, cast(int)cd.baseClass.vtbl.dim) : -1;
|
|
bool doesoverride = false;
|
|
switch (vi)
|
|
{
|
|
case -1:
|
|
Lintro:
|
|
/* Didn't find one, so
|
|
* This is an 'introducing' function which gets a new
|
|
* slot in the vtbl[].
|
|
*/
|
|
// Verify this doesn't override previous final function
|
|
if (cd.baseClass)
|
|
{
|
|
Dsymbol s = cd.baseClass.search(loc, ident);
|
|
if (s)
|
|
{
|
|
FuncDeclaration f2 = s.isFuncDeclaration();
|
|
if (f2)
|
|
{
|
|
f2 = f2.overloadExactMatch(type);
|
|
if (f2 && f2.isFinalFunc() && f2.prot().kind != PROTprivate)
|
|
error("cannot override final function %s", f2.toPrettyChars());
|
|
}
|
|
}
|
|
}
|
|
|
|
/* These quirky conditions mimic what VC++ appears to do
|
|
*/
|
|
if (global.params.mscoff && cd.cpp &&
|
|
cd.baseClass && cd.baseClass.vtbl.dim)
|
|
{
|
|
/* if overriding an interface function, then this is not
|
|
* introducing and don't put it in the class vtbl[]
|
|
*/
|
|
interfaceVirtual = overrideInterface();
|
|
if (interfaceVirtual)
|
|
{
|
|
//printf("\tinterface function %s\n", toChars());
|
|
cd.vtblFinal.push(this);
|
|
goto Linterfaces;
|
|
}
|
|
}
|
|
|
|
if (isFinalFunc())
|
|
{
|
|
// Don't check here, as it may override an interface function
|
|
//if (isOverride())
|
|
//error("is marked as override, but does not override any function");
|
|
cd.vtblFinal.push(this);
|
|
}
|
|
else
|
|
{
|
|
//printf("\tintroducing function %s\n", toChars());
|
|
introducing = 1;
|
|
if (cd.cpp && Target.reverseCppOverloads)
|
|
{
|
|
// with dmc, overloaded functions are grouped and in reverse order
|
|
vtblIndex = cast(int)cd.vtbl.dim;
|
|
for (size_t i = 0; i < cd.vtbl.dim; i++)
|
|
{
|
|
if (cd.vtbl[i].ident == ident && cd.vtbl[i].parent == parent)
|
|
{
|
|
vtblIndex = cast(int)i;
|
|
break;
|
|
}
|
|
}
|
|
// shift all existing functions back
|
|
for (size_t i = cd.vtbl.dim; i > vtblIndex; i--)
|
|
{
|
|
FuncDeclaration fd = cd.vtbl[i - 1].isFuncDeclaration();
|
|
assert(fd);
|
|
fd.vtblIndex++;
|
|
}
|
|
cd.vtbl.insert(vtblIndex, this);
|
|
}
|
|
else
|
|
{
|
|
// Append to end of vtbl[]
|
|
vi = cast(int)cd.vtbl.dim;
|
|
cd.vtbl.push(this);
|
|
vtblIndex = vi;
|
|
}
|
|
}
|
|
break;
|
|
case -2:
|
|
// can't determine because of fwd refs
|
|
cd.sizeok = SIZEOKfwd; // can't finish due to forward reference
|
|
Module.dprogress = dprogress_save;
|
|
return;
|
|
default:
|
|
{
|
|
FuncDeclaration fdv = cd.baseClass.vtbl[vi].isFuncDeclaration();
|
|
FuncDeclaration fdc = cd.vtbl[vi].isFuncDeclaration();
|
|
// This function is covariant with fdv
|
|
if (fdc == this)
|
|
{
|
|
doesoverride = true;
|
|
break;
|
|
}
|
|
if (fdc.toParent() == parent)
|
|
{
|
|
//printf("vi = %d,\tthis = %p %s %s @ [%s]\n\tfdc = %p %s %s @ [%s]\n\tfdv = %p %s %s @ [%s]\n",
|
|
// vi, this, this->toChars(), this->type->toChars(), this->loc.toChars(),
|
|
// fdc, fdc ->toChars(), fdc ->type->toChars(), fdc ->loc.toChars(),
|
|
// fdv, fdv ->toChars(), fdv ->type->toChars(), fdv ->loc.toChars());
|
|
// fdc overrides fdv exactly, then this introduces new function.
|
|
if (fdc.type.mod == fdv.type.mod && this.type.mod != fdv.type.mod)
|
|
goto Lintro;
|
|
}
|
|
// This function overrides fdv
|
|
if (fdv.isFinalFunc())
|
|
error("cannot override final function %s", fdv.toPrettyChars());
|
|
doesoverride = true;
|
|
if (!isOverride())
|
|
.deprecation(loc, "implicitly overriding base class method %s with %s deprecated; add 'override' attribute", fdv.toPrettyChars(), toPrettyChars());
|
|
if (fdc.toParent() == parent)
|
|
{
|
|
// If both are mixins, or both are not, then error.
|
|
// If either is not, the one that is not overrides the other.
|
|
bool thismixin = this.parent.isClassDeclaration() !is null;
|
|
bool fdcmixin = fdc.parent.isClassDeclaration() !is null;
|
|
if (thismixin == fdcmixin)
|
|
{
|
|
error("multiple overrides of same function");
|
|
}
|
|
else if (!thismixin) // fdc overrides fdv
|
|
{
|
|
// this doesn't override any function
|
|
break;
|
|
}
|
|
}
|
|
cd.vtbl[vi] = this;
|
|
vtblIndex = vi;
|
|
/* Remember which functions this overrides
|
|
*/
|
|
foverrides.push(fdv);
|
|
/* This works by whenever this function is called,
|
|
* it actually returns tintro, which gets dynamically
|
|
* cast to type. But we know that tintro is a base
|
|
* of type, so we could optimize it by not doing a
|
|
* dynamic cast, but just subtracting the isBaseOf()
|
|
* offset if the value is != null.
|
|
*/
|
|
if (fdv.tintro)
|
|
tintro = fdv.tintro;
|
|
else if (!type.equals(fdv.type))
|
|
{
|
|
/* Only need to have a tintro if the vptr
|
|
* offsets differ
|
|
*/
|
|
int offset;
|
|
if (fdv.type.nextOf().isBaseOf(type.nextOf(), &offset))
|
|
{
|
|
tintro = fdv.type;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
/* Go through all the interface bases.
|
|
* If this function is covariant with any members of those interface
|
|
* functions, set the tintro.
|
|
*/
|
|
Linterfaces:
|
|
foreach (b; cd.interfaces)
|
|
{
|
|
vi = findVtblIndex(&b.sym.vtbl, cast(int)b.sym.vtbl.dim);
|
|
switch (vi)
|
|
{
|
|
case -1:
|
|
break;
|
|
case -2:
|
|
cd.sizeok = SIZEOKfwd; // can't finish due to forward reference
|
|
Module.dprogress = dprogress_save;
|
|
return;
|
|
default:
|
|
{
|
|
FuncDeclaration fdv = cast(FuncDeclaration)b.sym.vtbl[vi];
|
|
Type ti = null;
|
|
/* Remember which functions this overrides
|
|
*/
|
|
foverrides.push(fdv);
|
|
/* Should we really require 'override' when implementing
|
|
* an interface function?
|
|
*/
|
|
//if (!isOverride())
|
|
//warning(loc, "overrides base class function %s, but is not marked with 'override'", fdv->toPrettyChars());
|
|
if (fdv.tintro)
|
|
ti = fdv.tintro;
|
|
else if (!type.equals(fdv.type))
|
|
{
|
|
/* Only need to have a tintro if the vptr
|
|
* offsets differ
|
|
*/
|
|
uint errors = global.errors;
|
|
global.gag++; // suppress printing of error messages
|
|
int offset;
|
|
int baseOf = fdv.type.nextOf().isBaseOf(type.nextOf(), &offset);
|
|
global.gag--; // suppress printing of error messages
|
|
if (errors != global.errors)
|
|
{
|
|
// any error in isBaseOf() is a forward reference error, so we bail out
|
|
global.errors = errors;
|
|
cd.sizeok = SIZEOKfwd; // can't finish due to forward reference
|
|
Module.dprogress = dprogress_save;
|
|
return;
|
|
}
|
|
if (baseOf)
|
|
{
|
|
ti = fdv.type;
|
|
}
|
|
}
|
|
if (ti)
|
|
{
|
|
if (tintro)
|
|
{
|
|
if (!tintro.nextOf().equals(ti.nextOf()) && !tintro.nextOf().isBaseOf(ti.nextOf(), null) && !ti.nextOf().isBaseOf(tintro.nextOf(), null))
|
|
{
|
|
error("incompatible covariant types %s and %s", tintro.toChars(), ti.toChars());
|
|
}
|
|
}
|
|
tintro = ti;
|
|
}
|
|
goto L2;
|
|
}
|
|
}
|
|
}
|
|
if (!doesoverride && isOverride() && (type.nextOf() || !may_override))
|
|
{
|
|
Dsymbol s = null;
|
|
for (size_t i = 0; i < cd.baseclasses.dim; i++)
|
|
{
|
|
s = (*cd.baseclasses)[i].sym.search_correct(ident);
|
|
if (s)
|
|
break;
|
|
}
|
|
if (s)
|
|
error("does not override any function, did you mean to override '%s'?", s.toPrettyChars());
|
|
else
|
|
error("does not override any function");
|
|
}
|
|
L2:
|
|
/* Go through all the interface bases.
|
|
* Disallow overriding any final functions in the interface(s).
|
|
*/
|
|
foreach (b; cd.interfaces)
|
|
{
|
|
if (b.sym)
|
|
{
|
|
Dsymbol s = search_function(b.sym, ident);
|
|
if (s)
|
|
{
|
|
FuncDeclaration f2 = s.isFuncDeclaration();
|
|
if (f2)
|
|
{
|
|
f2 = f2.overloadExactMatch(type);
|
|
if (f2 && f2.isFinalFunc() && f2.prot().kind != PROTprivate)
|
|
error("cannot override final function %s.%s", b.sym.toChars(), f2.toPrettyChars());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (isOverride() && !parent.isTemplateInstance())
|
|
error("override only applies to class member functions");
|
|
// Reflect this->type to f because it could be changed by findVtblIndex
|
|
assert(type.ty == Tfunction);
|
|
f = cast(TypeFunction)type;
|
|
/* Do not allow template instances to add virtual functions
|
|
* to a class.
|
|
*/
|
|
if (isVirtual())
|
|
{
|
|
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
|
|
ClassDeclaration cd = ti.tempdecl.isClassMember();
|
|
if (cd)
|
|
{
|
|
error("cannot use template to add virtual function to class '%s'", cd.toChars());
|
|
}
|
|
}
|
|
}
|
|
if (isMain())
|
|
{
|
|
// Check parameters to see if they are either () or (char[][] args)
|
|
switch (nparams)
|
|
{
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
{
|
|
Parameter fparam0 = Parameter.getNth(f.parameters, 0);
|
|
if (fparam0.type.ty != Tarray || fparam0.type.nextOf().ty != Tarray || fparam0.type.nextOf().nextOf().ty != Tchar || fparam0.storageClass & (STCout | STCref | STClazy))
|
|
goto Lmainerr;
|
|
break;
|
|
}
|
|
default:
|
|
goto Lmainerr;
|
|
}
|
|
if (!f.nextOf())
|
|
error("must return int or void");
|
|
else if (f.nextOf().ty != Tint32 && f.nextOf().ty != Tvoid)
|
|
error("must return int or void, not %s", f.nextOf().toChars());
|
|
if (f.varargs)
|
|
{
|
|
Lmainerr:
|
|
error("parameters must be main() or main(string[] args)");
|
|
}
|
|
}
|
|
if (isVirtual() && semanticRun != PASSsemanticdone)
|
|
{
|
|
/* Rewrite contracts as nested functions, then call them.
|
|
* Doing it as nested functions means that overriding functions
|
|
* can call them.
|
|
*/
|
|
if (frequire)
|
|
{
|
|
version(IN_LLVM)
|
|
{
|
|
/* In LDC, we can't rely on the codegen hacks DMD has to be able
|
|
* to just magically call the contract function parameterless with
|
|
* the parameters being picked up from the outer stack frame.
|
|
*
|
|
* Thus, we actually pass all the function parameters to the
|
|
* __require call, rewriting out parameters to ref ones because
|
|
* they have already been zeroed in the outer function.
|
|
*
|
|
* Also initialize fdrequireParams here - it will get filled in
|
|
* in semantic3.
|
|
*/
|
|
fdrequireParams = new Expressions();
|
|
auto params = outToRef((cast(TypeFunction)type).parameters);
|
|
auto tf = new TypeFunction(params, Type.tvoid, 0, LINKd);
|
|
}
|
|
else
|
|
{
|
|
/* in { ... }
|
|
* becomes:
|
|
* void __require() { ... }
|
|
* __require();
|
|
*/
|
|
auto tf = new TypeFunction(null, Type.tvoid, 0, LINKd);
|
|
}
|
|
Loc loc = frequire.loc;
|
|
tf.isnothrow = f.isnothrow;
|
|
tf.isnogc = f.isnogc;
|
|
tf.purity = f.purity;
|
|
tf.trust = f.trust;
|
|
auto fd = new FuncDeclaration(loc, loc, Id.require, STCundefined, tf);
|
|
fd.fbody = frequire;
|
|
Statement s1 = new ExpStatement(loc, fd);
|
|
version(IN_LLVM)
|
|
{
|
|
Expression e = new CallExp(loc, new VarExp(loc, fd, false), fdrequireParams);
|
|
}
|
|
else
|
|
{
|
|
Expression e = new CallExp(loc, new VarExp(loc, fd, false), cast(Expressions*)null);
|
|
}
|
|
Statement s2 = new ExpStatement(loc, e);
|
|
frequire = new CompoundStatement(loc, s1, s2);
|
|
fdrequire = fd;
|
|
}
|
|
if (!outId && f.nextOf() && f.nextOf().toBasetype().ty != Tvoid)
|
|
outId = Id.result; // provide a default
|
|
version(IN_LLVM)
|
|
{
|
|
/* We need to initialize fdensureParams here and not in the block below
|
|
* to have the parameter available when calling a base class ensure(),
|
|
* even if this functions doesn't have an out contract.
|
|
*/
|
|
fdensureParams = new Expressions();
|
|
if (outId)
|
|
fdensureParams.push(new IdentifierExp(loc, outId));
|
|
}
|
|
if (fensure)
|
|
{
|
|
version(IN_LLVM)
|
|
{
|
|
/* Same as for in contracts, see above. */
|
|
auto fparams = outToRef((cast(TypeFunction)type).parameters);
|
|
}
|
|
else
|
|
{
|
|
/* out (result) { ... }
|
|
* becomes:
|
|
* void __ensure(ref tret result) { ... }
|
|
* __ensure(result);
|
|
*/
|
|
auto fparams = new Parameters();
|
|
}
|
|
Loc loc = fensure.loc;
|
|
Parameter p = null;
|
|
if (outId)
|
|
{
|
|
p = new Parameter(STCref | STCconst, f.nextOf(), outId, null);
|
|
version(IN_LLVM)
|
|
{
|
|
fparams.insert(0, p);
|
|
}
|
|
else
|
|
{
|
|
fparams.push(p);
|
|
}
|
|
}
|
|
auto tf = new TypeFunction(fparams, Type.tvoid, 0, LINKd);
|
|
tf.isnothrow = f.isnothrow;
|
|
tf.isnogc = f.isnogc;
|
|
tf.purity = f.purity;
|
|
tf.trust = f.trust;
|
|
auto fd = new FuncDeclaration(loc, loc, Id.ensure, STCundefined, tf);
|
|
fd.fbody = fensure;
|
|
Statement s1 = new ExpStatement(loc, fd);
|
|
version(IN_LLVM)
|
|
{
|
|
Expression e = new CallExp(loc, new VarExp(loc, fd, false), fdensureParams);
|
|
}
|
|
else
|
|
{
|
|
Expression eresult = null;
|
|
if (outId)
|
|
eresult = new IdentifierExp(loc, outId);
|
|
Expression e = new CallExp(loc, new VarExp(loc, fd, false), eresult);
|
|
}
|
|
Statement s2 = new ExpStatement(loc, e);
|
|
fensure = new CompoundStatement(loc, s1, s2);
|
|
fdensure = fd;
|
|
}
|
|
}
|
|
Ldone:
|
|
/* Purity and safety can be inferred for some functions by examining
|
|
* the function body.
|
|
*/
|
|
TemplateInstance ti;
|
|
if (fbody && (isFuncLiteralDeclaration() || (storage_class & STCinference) || (inferRetType && !isCtorDeclaration()) || isInstantiated() && !isVirtualMethod() && !(ti = parent.isTemplateInstance(), ti && !ti.isTemplateMixin() && ti.tempdecl.ident != ident)))
|
|
{
|
|
if (f.purity == PUREimpure) // purity not specified
|
|
flags |= FUNCFLAGpurityInprocess;
|
|
if (f.trust == TRUSTdefault)
|
|
flags |= FUNCFLAGsafetyInprocess;
|
|
if (!f.isnothrow)
|
|
flags |= FUNCFLAGnothrowInprocess;
|
|
if (!f.isnogc)
|
|
flags |= FUNCFLAGnogcInprocess;
|
|
if (!isVirtual() || introducing)
|
|
flags |= FUNCFLAGreturnInprocess;
|
|
}
|
|
Module.dprogress++;
|
|
version(IN_LLVM)
|
|
{
|
|
//LDC relies on semanticRun variable not being reset here
|
|
if(semanticRun < PASSsemanticdone)
|
|
semanticRun = PASSsemanticdone;
|
|
}
|
|
else
|
|
{
|
|
semanticRun = PASSsemanticdone;
|
|
}
|
|
/* Save scope for possible later use (if we need the
|
|
* function internals)
|
|
*/
|
|
_scope = sc.copy();
|
|
_scope.setNoFree();
|
|
static __gshared bool printedMain = false; // semantic might run more than once
|
|
if (global.params.verbose && !printedMain)
|
|
{
|
|
const(char)* type = isMain() ? "main" : isWinMain() ? "winmain" : isDllMain() ? "dllmain" : cast(const(char)*)null;
|
|
Module mod = sc._module;
|
|
if (type && mod)
|
|
{
|
|
printedMain = true;
|
|
const(char)* name = FileName.searchPath(global.path, mod.srcfile.toChars(), true);
|
|
fprintf(global.stdmsg, "entry %-10s\t%s\n", type, name);
|
|
}
|
|
}
|
|
if (fbody && isMain() && sc._module.isRoot())
|
|
genCmain(sc);
|
|
assert(type.ty != Terror || errors);
|
|
}
|
|
|
|
override final void semantic2(Scope* sc)
|
|
{
|
|
if (semanticRun >= PASSsemantic2done)
|
|
return;
|
|
assert(semanticRun <= PASSsemantic2);
|
|
semanticRun = PASSsemantic2;
|
|
objc_FuncDeclaration_semantic_setSelector(this, sc);
|
|
objc_FuncDeclaration_semantic_validateSelector(this);
|
|
if (ClassDeclaration cd = parent.isClassDeclaration())
|
|
{
|
|
objc_FuncDeclaration_semantic_checkLinkage(this);
|
|
}
|
|
}
|
|
|
|
// Do the semantic analysis on the internals of the function.
|
|
override final void semantic3(Scope* sc)
|
|
{
|
|
VarDeclaration argptr = null;
|
|
VarDeclaration _arguments = null;
|
|
|
|
if (!parent)
|
|
{
|
|
if (global.errors)
|
|
return;
|
|
//printf("FuncDeclaration::semantic3(%s '%s', sc = %p)\n", kind(), toChars(), sc);
|
|
assert(0);
|
|
}
|
|
if (isError(parent))
|
|
return;
|
|
//printf("FuncDeclaration::semantic3('%s.%s', %p, sc = %p, loc = %s)\n", parent->toChars(), toChars(), this, sc, loc.toChars());
|
|
//fflush(stdout);
|
|
//printf("storage class = x%x %x\n", sc->stc, storage_class);
|
|
//{ static int x; if (++x == 2) *(char*)0=0; }
|
|
//printf("\tlinkage = %d\n", sc->linkage);
|
|
|
|
if (ident == Id.assign && !inuse)
|
|
{
|
|
if (storage_class & STCinference)
|
|
{
|
|
/* Bugzilla 15044: For generated opAssign function, any errors
|
|
* from its body need to be gagged.
|
|
*/
|
|
uint oldErrors = global.startGagging();
|
|
++inuse;
|
|
semantic3(sc);
|
|
--inuse;
|
|
if (global.endGagging(oldErrors)) // if errors happened
|
|
{
|
|
// Disable generated opAssign, because some members forbid identity assignment.
|
|
storage_class |= STCdisable;
|
|
fbody = null; // remove fbody which contains the error
|
|
semantic3Errors = false;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
//printf(" sc->incontract = %d\n", (sc->flags & SCOPEcontract));
|
|
if (semanticRun >= PASSsemantic3)
|
|
return;
|
|
semanticRun = PASSsemantic3;
|
|
semantic3Errors = false;
|
|
if (!type || type.ty != Tfunction)
|
|
return;
|
|
TypeFunction f = cast(TypeFunction)type;
|
|
if (!inferRetType && f.next.ty == Terror)
|
|
return;
|
|
if (!fbody && inferRetType && !f.next)
|
|
{
|
|
error("has no function body with return type inference");
|
|
return;
|
|
}
|
|
|
|
uint oldErrors = global.errors;
|
|
|
|
if (frequire)
|
|
{
|
|
for (size_t i = 0; i < foverrides.dim; i++)
|
|
{
|
|
FuncDeclaration fdv = foverrides[i];
|
|
if (fdv.fbody && !fdv.frequire)
|
|
{
|
|
error("cannot have an in contract when overriden function %s does not have an in contract", fdv.toPrettyChars());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
frequire = mergeFrequire(frequire);
|
|
fensure = mergeFensure(fensure, outId);
|
|
if (fbody || frequire || fensure)
|
|
{
|
|
/* Symbol table into which we place parameters and nested functions,
|
|
* solely to diagnose name collisions.
|
|
*/
|
|
localsymtab = new DsymbolTable();
|
|
// Establish function scope
|
|
auto ss = new ScopeDsymbol();
|
|
ss.parent = sc.scopesym;
|
|
Scope* sc2 = sc.push(ss);
|
|
sc2.func = this;
|
|
sc2.parent = this;
|
|
sc2.callSuper = 0;
|
|
sc2.sbreak = null;
|
|
sc2.scontinue = null;
|
|
sc2.sw = null;
|
|
sc2.fes = fes;
|
|
sc2.linkage = LINKd;
|
|
sc2.stc &= ~(STCauto | STCscope | STCstatic | STCabstract | STCdeprecated | STCoverride | STC_TYPECTOR | STCfinal | STCtls | STCgshared | STCref | STCreturn | STCproperty | STCnothrow | STCpure | STCsafe | STCtrusted | STCsystem);
|
|
sc2.protection = Prot(PROTpublic);
|
|
sc2.explicitProtection = 0;
|
|
sc2.structalign = STRUCTALIGN_DEFAULT;
|
|
if (this.ident != Id.require && this.ident != Id.ensure)
|
|
sc2.flags = sc.flags & ~SCOPEcontract;
|
|
sc2.flags &= ~SCOPEcompile;
|
|
sc2.tf = null;
|
|
sc2.os = null;
|
|
sc2.noctor = 0;
|
|
sc2.userAttribDecl = null;
|
|
if (sc2.intypeof == 1)
|
|
sc2.intypeof = 2;
|
|
sc2.fieldinit = null;
|
|
sc2.fieldinit_dim = 0;
|
|
if (isMember2())
|
|
{
|
|
FuncLiteralDeclaration fld = isFuncLiteralDeclaration();
|
|
if (fld && !sc.intypeof)
|
|
{
|
|
if (fld.tok == TOKreserved)
|
|
fld.tok = TOKfunction;
|
|
if (isNested())
|
|
{
|
|
error("cannot be class members");
|
|
return;
|
|
}
|
|
}
|
|
assert(!isNested() || sc.intypeof); // can't be both member and nested
|
|
}
|
|
// Declare 'this'
|
|
AggregateDeclaration ad = isThis();
|
|
vthis = declareThis(sc2, ad);
|
|
// Declare hidden variable _arguments[] and _argptr
|
|
if (f.varargs == 1)
|
|
{
|
|
static if (!IN_GCC && !IN_LLVM)
|
|
{
|
|
if (global.params.is64bit && !global.params.isWindows)
|
|
{
|
|
// Declare save area for varargs registers
|
|
Type t = new TypeIdentifier(loc, Id.va_argsave_t);
|
|
t = t.semantic(loc, sc);
|
|
if (t == Type.terror)
|
|
{
|
|
error("must import core.vararg to use variadic functions");
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
v_argsave = new VarDeclaration(loc, t, Id.va_argsave, null);
|
|
v_argsave.storage_class |= STCtemp;
|
|
v_argsave.semantic(sc2);
|
|
sc2.insert(v_argsave);
|
|
v_argsave.parent = this;
|
|
}
|
|
}
|
|
}
|
|
if (f.linkage == LINKd)
|
|
{
|
|
// Declare _arguments[]
|
|
v_arguments = new VarDeclaration(Loc(), Type.typeinfotypelist.type, Id._arguments_typeinfo, null);
|
|
v_arguments.storage_class |= STCtemp | STCparameter;
|
|
v_arguments.semantic(sc2);
|
|
sc2.insert(v_arguments);
|
|
v_arguments.parent = this;
|
|
//Type *t = Type::typeinfo->type->constOf()->arrayOf();
|
|
Type t = Type.dtypeinfo.type.arrayOf();
|
|
_arguments = new VarDeclaration(Loc(), t, Id._arguments, null);
|
|
_arguments.storage_class |= STCtemp;
|
|
_arguments.semantic(sc2);
|
|
sc2.insert(_arguments);
|
|
_arguments.parent = this;
|
|
}
|
|
if (f.linkage == LINKd || (f.parameters && Parameter.dim(f.parameters)))
|
|
{
|
|
// Declare _argptr
|
|
Type t = Type.tvalist.semantic(loc, sc);
|
|
argptr = new VarDeclaration(Loc(), t, Id._argptr, null);
|
|
argptr.storage_class |= STCtemp;
|
|
argptr.semantic(sc2);
|
|
sc2.insert(argptr);
|
|
argptr.parent = this;
|
|
}
|
|
}
|
|
|
|
version(IN_LLVM)
|
|
{
|
|
// Make sure semantic analysis has been run on argument types. This is
|
|
// e.g. needed for TypeTuple!(int, int) to be picked up as two int
|
|
// parameters by the Parameter functions.
|
|
if (f.parameters)
|
|
{
|
|
for (size_t i = 0; i < Parameter.dim(f.parameters); i++)
|
|
{ Parameter arg = Parameter.getNth(f.parameters, i);
|
|
Type nw = arg.type.semantic(Loc(), sc);
|
|
if (arg.type != nw) {
|
|
arg.type = nw;
|
|
// Examine this index again.
|
|
// This is important if it turned into a tuple.
|
|
// In particular, the empty tuple should be handled or the
|
|
// next parameter will be skipped.
|
|
// LDC_FIXME: Maybe we only need to do this for tuples,
|
|
// and can add tuple.length after decrement?
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Declare all the function parameters as variables
|
|
* and install them in parameters[]
|
|
*/
|
|
size_t nparams = Parameter.dim(f.parameters);
|
|
if (nparams)
|
|
{
|
|
/* parameters[] has all the tuples removed, as the back end
|
|
* doesn't know about tuples
|
|
*/
|
|
parameters = new VarDeclarations();
|
|
parameters.reserve(nparams);
|
|
for (size_t i = 0; i < nparams; i++)
|
|
{
|
|
Parameter fparam = Parameter.getNth(f.parameters, i);
|
|
Identifier id = fparam.ident;
|
|
StorageClass stc = 0;
|
|
if (!id)
|
|
{
|
|
/* Generate identifier for un-named parameter,
|
|
* because we need it later on.
|
|
*/
|
|
fparam.ident = id = Identifier.generateId("_param_", i);
|
|
stc |= STCtemp;
|
|
}
|
|
Type vtype = fparam.type;
|
|
auto v = new VarDeclaration(loc, vtype, id, null);
|
|
//printf("declaring parameter %s of type %s\n", v->toChars(), v->type->toChars());
|
|
stc |= STCparameter;
|
|
if (f.varargs == 2 && i + 1 == nparams)
|
|
stc |= STCvariadic;
|
|
stc |= fparam.storageClass & (STCin | STCout | STCref | STCreturn | STClazy | STCfinal | STC_TYPECTOR | STCnodtor);
|
|
v.storage_class = stc;
|
|
v.semantic(sc2);
|
|
if (!sc2.insert(v))
|
|
error("parameter %s.%s is already defined", toChars(), v.toChars());
|
|
else
|
|
parameters.push(v);
|
|
localsymtab.insert(v);
|
|
v.parent = this;
|
|
version(IN_LLVM)
|
|
{
|
|
if (fdrequireParams)
|
|
fdrequireParams.push(new VarExp(loc, v));
|
|
if (fdensureParams)
|
|
fdensureParams.push(new VarExp(loc, v));
|
|
}
|
|
}
|
|
}
|
|
// Declare the tuple symbols and put them in the symbol table,
|
|
// but not in parameters[].
|
|
if (f.parameters)
|
|
{
|
|
for (size_t i = 0; i < f.parameters.dim; i++)
|
|
{
|
|
Parameter fparam = (*f.parameters)[i];
|
|
if (!fparam.ident)
|
|
continue;
|
|
// never used, so ignore
|
|
if (fparam.type.ty == Ttuple)
|
|
{
|
|
TypeTuple t = cast(TypeTuple)fparam.type;
|
|
size_t dim = Parameter.dim(t.arguments);
|
|
auto exps = new Objects();
|
|
exps.setDim(dim);
|
|
for (size_t j = 0; j < dim; j++)
|
|
{
|
|
Parameter narg = Parameter.getNth(t.arguments, j);
|
|
assert(narg.ident);
|
|
VarDeclaration v = sc2.search(Loc(), narg.ident, null).isVarDeclaration();
|
|
assert(v);
|
|
Expression e = new VarExp(v.loc, v);
|
|
(*exps)[j] = e;
|
|
}
|
|
assert(fparam.ident);
|
|
auto v = new TupleDeclaration(loc, fparam.ident, exps);
|
|
//printf("declaring tuple %s\n", v->toChars());
|
|
v.isexp = true;
|
|
if (!sc2.insert(v))
|
|
error("parameter %s.%s is already defined", toChars(), v.toChars());
|
|
localsymtab.insert(v);
|
|
v.parent = this;
|
|
}
|
|
}
|
|
}
|
|
// Precondition invariant
|
|
Statement fpreinv = null;
|
|
if (addPreInvariant())
|
|
{
|
|
Expression e = addInvariant(loc, sc, ad, vthis, isDtorDeclaration() !is null);
|
|
if (e)
|
|
fpreinv = new ExpStatement(Loc(), e);
|
|
}
|
|
// Postcondition invariant
|
|
Statement fpostinv = null;
|
|
if (addPostInvariant())
|
|
{
|
|
Expression e = addInvariant(loc, sc, ad, vthis, isCtorDeclaration() !is null);
|
|
if (e)
|
|
fpostinv = new ExpStatement(Loc(), e);
|
|
}
|
|
Scope* scout = null;
|
|
if (fensure || addPostInvariant())
|
|
{
|
|
if ((fensure && global.params.useOut) || fpostinv)
|
|
{
|
|
returnLabel = new LabelDsymbol(Id.returnLabel);
|
|
}
|
|
// scope of out contract (need for vresult->semantic)
|
|
auto sym = new ScopeDsymbol();
|
|
sym.parent = sc2.scopesym;
|
|
scout = sc2.push(sym);
|
|
}
|
|
if (fbody)
|
|
{
|
|
auto sym = new ScopeDsymbol();
|
|
sym.parent = sc2.scopesym;
|
|
sc2 = sc2.push(sym);
|
|
AggregateDeclaration ad2 = isAggregateMember2();
|
|
/* If this is a class constructor
|
|
*/
|
|
if (ad2 && isCtorDeclaration())
|
|
{
|
|
sc2.allocFieldinit(ad2.fields.dim);
|
|
foreach (v; ad2.fields)
|
|
{
|
|
v.ctorinit = 0;
|
|
}
|
|
}
|
|
if (!inferRetType && retStyle(f) != RETstack)
|
|
nrvo_can = 0;
|
|
bool inferRef = (f.isref && (storage_class & STCauto));
|
|
fbody = fbody.semantic(sc2);
|
|
if (!fbody)
|
|
fbody = new CompoundStatement(Loc(), new Statements());
|
|
assert(type == f || (type.ty == Tfunction && f.purity == PUREimpure && (cast(TypeFunction)type).purity >= PUREfwdref));
|
|
f = cast(TypeFunction)type;
|
|
if (inferRetType)
|
|
{
|
|
// If no return type inferred yet, then infer a void
|
|
if (!f.next)
|
|
f.next = Type.tvoid;
|
|
if (f.checkRetType(loc))
|
|
fbody = new ErrorStatement();
|
|
}
|
|
if (global.params.vcomplex && f.next !is null)
|
|
f.next.checkComplexTransition(loc);
|
|
if (returns && !fbody.isErrorStatement())
|
|
{
|
|
for (size_t i = 0; i < returns.dim;)
|
|
{
|
|
Expression exp = (*returns)[i].exp;
|
|
if (exp.op == TOKvar && (cast(VarExp)exp).var == vresult)
|
|
{
|
|
if (f.next.ty == Tvoid && isMain())
|
|
exp.type = Type.tint32;
|
|
else
|
|
exp.type = f.next;
|
|
// Remove `return vresult;` from returns
|
|
returns.remove(i);
|
|
continue;
|
|
}
|
|
if (inferRef && f.isref && !exp.type.constConv(f.next)) // Bugzilla 13336
|
|
f.isref = false;
|
|
i++;
|
|
}
|
|
}
|
|
if (f.isref) // Function returns a reference
|
|
{
|
|
if (storage_class & STCauto)
|
|
storage_class &= ~STCauto;
|
|
}
|
|
if (retStyle(f) != RETstack)
|
|
nrvo_can = 0;
|
|
if (fbody.isErrorStatement())
|
|
{
|
|
}
|
|
else if (isStaticCtorDeclaration())
|
|
{
|
|
/* It's a static constructor. Ensure that all
|
|
* ctor consts were initialized.
|
|
*/
|
|
ScopeDsymbol pd = toParent().isScopeDsymbol();
|
|
for (size_t i = 0; i < pd.members.dim; i++)
|
|
{
|
|
Dsymbol s = (*pd.members)[i];
|
|
s.checkCtorConstInit();
|
|
}
|
|
}
|
|
else if (ad2 && isCtorDeclaration())
|
|
{
|
|
ClassDeclaration cd = ad2.isClassDeclaration();
|
|
// Verify that all the ctorinit fields got initialized
|
|
if (!(sc2.callSuper & CSXthis_ctor))
|
|
{
|
|
for (size_t i = 0; i < ad2.fields.dim; i++)
|
|
{
|
|
VarDeclaration v = ad2.fields[i];
|
|
if (v.ctorinit == 0)
|
|
{
|
|
/* Current bugs in the flow analysis:
|
|
* 1. union members should not produce error messages even if
|
|
* not assigned to
|
|
* 2. structs should recognize delegating opAssign calls as well
|
|
* as delegating calls to other constructors
|
|
*/
|
|
if (v.isCtorinit() && !v.type.isMutable() && cd)
|
|
error("missing initializer for %s field %s", MODtoChars(v.type.mod), v.toChars());
|
|
else if (v.storage_class & STCnodefaultctor)
|
|
.error(loc, "field %s must be initialized in constructor", v.toChars());
|
|
else if (v.type.needsNested())
|
|
.error(loc, "field %s must be initialized in constructor, because it is nested struct", v.toChars());
|
|
}
|
|
else
|
|
{
|
|
bool mustInit = (v.storage_class & STCnodefaultctor || v.type.needsNested());
|
|
if (mustInit && !(sc2.fieldinit[i] & CSXthis_ctor))
|
|
{
|
|
error("field %s must be initialized but skipped", v.toChars());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
sc2.freeFieldinit();
|
|
if (cd && !(sc2.callSuper & CSXany_ctor) && cd.baseClass && cd.baseClass.ctor)
|
|
{
|
|
sc2.callSuper = 0;
|
|
// Insert implicit super() at start of fbody
|
|
FuncDeclaration fd = resolveFuncCall(Loc(), sc2, cd.baseClass.ctor, null, null, null, 1);
|
|
if (!fd)
|
|
{
|
|
error("no match for implicit super() call in constructor");
|
|
}
|
|
else if (fd.storage_class & STCdisable)
|
|
{
|
|
error("cannot call super() implicitly because it is annotated with @disable");
|
|
}
|
|
else
|
|
{
|
|
Expression e1 = new SuperExp(Loc());
|
|
Expression e = new CallExp(Loc(), e1);
|
|
e = e.semantic(sc2);
|
|
Statement s = new ExpStatement(Loc(), e);
|
|
fbody = new CompoundStatement(Loc(), s, fbody);
|
|
}
|
|
}
|
|
//printf("callSuper = x%x\n", sc2->callSuper);
|
|
}
|
|
int blockexit = BEnone;
|
|
if (!fbody.isErrorStatement())
|
|
{
|
|
// Check for errors related to 'nothrow'.
|
|
uint nothrowErrors = global.errors;
|
|
blockexit = fbody.blockExit(this, f.isnothrow);
|
|
if (f.isnothrow && (global.errors != nothrowErrors))
|
|
.error(loc, "%s '%s' is nothrow yet may throw", kind(), toPrettyChars());
|
|
if (flags & FUNCFLAGnothrowInprocess)
|
|
{
|
|
if (type == f)
|
|
f = cast(TypeFunction)f.copy();
|
|
f.isnothrow = !(blockexit & BEthrow);
|
|
}
|
|
}
|
|
if (fbody.isErrorStatement())
|
|
{
|
|
}
|
|
else if (ad2 && isCtorDeclaration())
|
|
{
|
|
/* Append:
|
|
* return this;
|
|
* to function body
|
|
*/
|
|
if (blockexit & BEfallthru)
|
|
{
|
|
Statement s = new ReturnStatement(loc, null);
|
|
s = s.semantic(sc2);
|
|
fbody = new CompoundStatement(loc, fbody, s);
|
|
hasReturnExp |= 1;
|
|
}
|
|
}
|
|
else if (fes)
|
|
{
|
|
// For foreach(){} body, append a return 0;
|
|
if (blockexit & BEfallthru)
|
|
{
|
|
Expression e = new IntegerExp(0);
|
|
Statement s = new ReturnStatement(Loc(), e);
|
|
fbody = new CompoundStatement(Loc(), fbody, s);
|
|
hasReturnExp |= 1;
|
|
}
|
|
assert(!returnLabel);
|
|
}
|
|
else
|
|
{
|
|
const(bool) inlineAsm = (hasReturnExp & 8) != 0;
|
|
if ((blockexit & BEfallthru) && f.next.ty != Tvoid && !inlineAsm)
|
|
{
|
|
Expression e;
|
|
if (!hasReturnExp)
|
|
error("has no return statement, but is expected to return a value of type %s", f.next.toChars());
|
|
else
|
|
error("no return exp; or assert(0); at end of function");
|
|
if (global.params.useAssert && !global.params.useInline)
|
|
{
|
|
/* Add an assert(0, msg); where the missing return
|
|
* should be.
|
|
*/
|
|
e = new AssertExp(endloc, new IntegerExp(0), new StringExp(loc, cast(char*)"missing return expression"));
|
|
}
|
|
else
|
|
e = new HaltExp(endloc);
|
|
e = new CommaExp(Loc(), e, f.next.defaultInit());
|
|
e = e.semantic(sc2);
|
|
Statement s = new ExpStatement(Loc(), e);
|
|
fbody = new CompoundStatement(Loc(), fbody, s);
|
|
}
|
|
}
|
|
if (returns)
|
|
{
|
|
bool implicit0 = (f.next.ty == Tvoid && isMain());
|
|
Type tret = implicit0 ? Type.tint32 : f.next;
|
|
assert(tret.ty != Tvoid);
|
|
if (vresult || returnLabel)
|
|
buildResultVar(scout ? scout : sc2, tret);
|
|
|
|
/* Cannot move this loop into NrvoWalker, because
|
|
* returns[i] may be in the nested delegate for foreach-body.
|
|
*/
|
|
for (size_t i = 0; i < returns.dim; i++)
|
|
{
|
|
ReturnStatement rs = (*returns)[i];
|
|
Expression exp = rs.exp;
|
|
if (exp.op == TOKerror)
|
|
continue;
|
|
if (tret.ty == Terror)
|
|
{
|
|
// Bugzilla 13702
|
|
exp = checkGC(sc2, exp);
|
|
continue;
|
|
}
|
|
|
|
if (!exp.implicitConvTo(tret) && parametersIntersect(exp.type))
|
|
{
|
|
if (exp.type.immutableOf().implicitConvTo(tret))
|
|
exp = exp.castTo(sc2, exp.type.immutableOf());
|
|
else if (exp.type.wildOf().implicitConvTo(tret))
|
|
exp = exp.castTo(sc2, exp.type.wildOf());
|
|
}
|
|
exp = exp.implicitCastTo(sc2, tret);
|
|
|
|
if (f.isref)
|
|
{
|
|
// Function returns a reference
|
|
exp = exp.toLvalue(sc2, exp);
|
|
checkEscapeRef(sc2, exp, false);
|
|
}
|
|
else
|
|
{
|
|
exp = exp.optimize(WANTvalue);
|
|
|
|
/* Bugzilla 10789:
|
|
* If NRVO is not possible, all returned lvalues should call their postblits.
|
|
*/
|
|
if (!nrvo_can)
|
|
exp = doCopyOrMove(sc2, exp);
|
|
|
|
checkEscape(sc2, exp, false);
|
|
}
|
|
|
|
exp = checkGC(sc2, exp);
|
|
|
|
if (vresult)
|
|
{
|
|
// Create: return vresult = exp;
|
|
exp = new BlitExp(rs.loc, vresult, exp);
|
|
exp.type = vresult.type;
|
|
|
|
if (rs.caseDim)
|
|
exp = Expression.combine(exp, new IntegerExp(rs.caseDim));
|
|
}
|
|
else if (tintro && !tret.equals(tintro.nextOf()))
|
|
{
|
|
exp = exp.implicitCastTo(sc2, tintro.nextOf());
|
|
}
|
|
rs.exp = exp;
|
|
}
|
|
}
|
|
if (nrvo_var || returnLabel)
|
|
{
|
|
scope NrvoWalker nw = new NrvoWalker();
|
|
nw.fd = this;
|
|
nw.sc = sc2;
|
|
nw.visitStmt(fbody);
|
|
}
|
|
sc2 = sc2.pop();
|
|
}
|
|
Statement freq = frequire;
|
|
Statement fens = fensure;
|
|
/* Do the semantic analysis on the [in] preconditions and
|
|
* [out] postconditions.
|
|
*/
|
|
if (freq)
|
|
{
|
|
/* frequire is composed of the [in] contracts
|
|
*/
|
|
auto sym = new ScopeDsymbol();
|
|
sym.parent = sc2.scopesym;
|
|
sc2 = sc2.push(sym);
|
|
sc2.flags = (sc2.flags & ~SCOPEcontract) | SCOPErequire;
|
|
// BUG: need to error if accessing out parameters
|
|
// BUG: need to treat parameters as const
|
|
// BUG: need to disallow returns and throws
|
|
// BUG: verify that all in and ref parameters are read
|
|
freq = freq.semantic(sc2);
|
|
sc2 = sc2.pop();
|
|
if (!global.params.useIn)
|
|
freq = null;
|
|
}
|
|
if (fens)
|
|
{
|
|
/* fensure is composed of the [out] contracts
|
|
*/
|
|
if (f.next.ty == Tvoid && outId)
|
|
error("void functions have no result");
|
|
if (fensure && f.next.ty != Tvoid)
|
|
buildResultVar(scout, f.next);
|
|
sc2 = scout; //push
|
|
sc2.flags = (sc2.flags & ~SCOPEcontract) | SCOPEensure;
|
|
// BUG: need to treat parameters as const
|
|
// BUG: need to disallow returns and throws
|
|
if (inferRetType && fdensure && (cast(TypeFunction)fdensure.type).parameters)
|
|
{
|
|
// Return type was unknown in the first semantic pass
|
|
Parameter p = (*(cast(TypeFunction)fdensure.type).parameters)[0];
|
|
p.type = f.next;
|
|
}
|
|
fens = fens.semantic(sc2);
|
|
sc2 = sc2.pop();
|
|
if (!global.params.useOut)
|
|
fens = null;
|
|
}
|
|
if (fbody && fbody.isErrorStatement())
|
|
{
|
|
}
|
|
else
|
|
{
|
|
auto a = new Statements();
|
|
// Merge in initialization of 'out' parameters
|
|
if (parameters)
|
|
{
|
|
for (size_t i = 0; i < parameters.dim; i++)
|
|
{
|
|
VarDeclaration v = (*parameters)[i];
|
|
if (v.storage_class & STCout)
|
|
{
|
|
assert(v._init);
|
|
ExpInitializer ie = v._init.isExpInitializer();
|
|
assert(ie);
|
|
if (ie.exp.op == TOKconstruct)
|
|
ie.exp.op = TOKassign; // construction occured in parameter processing
|
|
a.push(new ExpStatement(Loc(), ie.exp));
|
|
}
|
|
}
|
|
}
|
|
// we'll handle variadics ourselves
|
|
static if (!IN_LLVM) {
|
|
if (argptr)
|
|
{
|
|
// Initialize _argptr
|
|
version (IN_GCC)
|
|
{
|
|
// Handled in FuncDeclaration::toObjFile
|
|
v_argptr = argptr;
|
|
v_argptr._init = new VoidInitializer(loc);
|
|
}
|
|
else
|
|
{
|
|
Type t = argptr.type;
|
|
if (global.params.is64bit && !global.params.isWindows)
|
|
{
|
|
// Initialize _argptr to point to v_argsave
|
|
Expression e1 = new VarExp(Loc(), argptr);
|
|
Expression e = new SymOffExp(Loc(), v_argsave, 6 * 8 + 8 * 16);
|
|
e.type = argptr.type;
|
|
e = new AssignExp(Loc(), e1, e);
|
|
e = e.semantic(sc2);
|
|
a.push(new ExpStatement(Loc(), e));
|
|
}
|
|
else
|
|
{
|
|
// Initialize _argptr to point past non-variadic arg
|
|
VarDeclaration p;
|
|
uint offset = 0;
|
|
Expression e;
|
|
Expression e1 = new VarExp(Loc(), argptr);
|
|
// Find the last non-ref parameter
|
|
if (parameters && parameters.dim)
|
|
{
|
|
size_t lastNonref = parameters.dim - 1;
|
|
p = (*parameters)[lastNonref];
|
|
/* The trouble with out and ref parameters is that taking
|
|
* the address of it doesn't work, because later processing
|
|
* adds in an extra level of indirection. So we skip over them.
|
|
*/
|
|
while (p.storage_class & (STCout | STCref))
|
|
{
|
|
offset += Target.ptrsize;
|
|
if (lastNonref-- == 0)
|
|
{
|
|
p = v_arguments;
|
|
break;
|
|
}
|
|
p = (*parameters)[lastNonref];
|
|
}
|
|
}
|
|
else
|
|
p = v_arguments; // last parameter is _arguments[]
|
|
if (global.params.is64bit && global.params.isWindows)
|
|
{
|
|
offset += Target.ptrsize;
|
|
if ((p.storage_class & STClazy) || p.type.size() > Target.ptrsize)
|
|
{
|
|
/* Necessary to offset the extra level of indirection the Win64
|
|
* ABI demands
|
|
*/
|
|
e = new SymOffExp(Loc(), p, 0);
|
|
e.type = Type.tvoidptr;
|
|
e = new AddrExp(Loc(), e);
|
|
e.type = Type.tvoidptr;
|
|
e = new AddExp(Loc(), e, new IntegerExp(offset));
|
|
e.type = Type.tvoidptr;
|
|
goto L1;
|
|
}
|
|
}
|
|
else if (p.storage_class & STClazy)
|
|
{
|
|
// If the last parameter is lazy, it's the size of a delegate
|
|
offset += Target.ptrsize * 2;
|
|
}
|
|
else
|
|
offset += p.type.size();
|
|
offset = (offset + Target.ptrsize - 1) & ~(Target.ptrsize - 1); // assume stack aligns on pointer size
|
|
e = new SymOffExp(Loc(), p, offset);
|
|
e.type = Type.tvoidptr;
|
|
//e = e->semantic(sc);
|
|
L1:
|
|
e = new AssignExp(Loc(), e1, e);
|
|
e.type = t;
|
|
a.push(new ExpStatement(Loc(), e));
|
|
p.isargptr = true;
|
|
}
|
|
}
|
|
}
|
|
if (_arguments)
|
|
{
|
|
/* Advance to elements[] member of TypeInfo_Tuple with:
|
|
* _arguments = v_arguments.elements;
|
|
*/
|
|
Expression e = new VarExp(Loc(), v_arguments);
|
|
e = new DotIdExp(Loc(), e, Id.elements);
|
|
e = new ConstructExp(Loc(), _arguments, e);
|
|
e = e.semantic(sc2);
|
|
_arguments._init = new ExpInitializer(Loc(), e);
|
|
auto de = new DeclarationExp(Loc(), _arguments);
|
|
a.push(new ExpStatement(Loc(), de));
|
|
}
|
|
} // !IN_LLVM
|
|
// Merge contracts together with body into one compound statement
|
|
if (freq || fpreinv)
|
|
{
|
|
if (!freq)
|
|
freq = fpreinv;
|
|
else if (fpreinv)
|
|
freq = new CompoundStatement(Loc(), freq, fpreinv);
|
|
a.push(freq);
|
|
}
|
|
if (fbody)
|
|
a.push(fbody);
|
|
if (fens || fpostinv)
|
|
{
|
|
if (!fens)
|
|
fens = fpostinv;
|
|
else if (fpostinv)
|
|
fens = new CompoundStatement(Loc(), fpostinv, fens);
|
|
auto ls = new LabelStatement(Loc(), Id.returnLabel, fens);
|
|
returnLabel.statement = ls;
|
|
a.push(returnLabel.statement);
|
|
if (f.next.ty != Tvoid && vresult)
|
|
{
|
|
version(IN_LLVM)
|
|
{
|
|
Expression e = null;
|
|
if (isCtorDeclaration())
|
|
{
|
|
ThisExp te = new ThisExp(Loc());
|
|
te.type = vthis.type;
|
|
te.var = vthis;
|
|
e = te;
|
|
}
|
|
else
|
|
{
|
|
e = new VarExp(Loc(), vresult);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Create: return vresult;
|
|
Expression e = new VarExp(Loc(), vresult);
|
|
}
|
|
if (tintro)
|
|
{
|
|
e = e.implicitCastTo(sc, tintro.nextOf());
|
|
e = e.semantic(sc);
|
|
}
|
|
auto s = new ReturnStatement(Loc(), e);
|
|
a.push(s);
|
|
}
|
|
}
|
|
if (isMain() && f.next.ty == Tvoid)
|
|
{
|
|
// Add a return 0; statement
|
|
Statement s = new ReturnStatement(Loc(), new IntegerExp(0));
|
|
a.push(s);
|
|
}
|
|
|
|
Statement sbody = new CompoundStatement(Loc(), a);
|
|
|
|
/* Append destructor calls for parameters as finally blocks.
|
|
*/
|
|
if (parameters)
|
|
{
|
|
foreach (v; *parameters)
|
|
{
|
|
if (v.storage_class & (STCref | STCout | STClazy))
|
|
continue;
|
|
if (v.needsScopeDtor())
|
|
{
|
|
// same with ExpStatement.scopeCode()
|
|
Statement s = new DtorExpStatement(Loc(), v.edtor, v);
|
|
v.noscope = true;
|
|
|
|
s = s.semantic(sc2);
|
|
|
|
uint nothrowErrors = global.errors;
|
|
bool isnothrow = f.isnothrow & !(flags & FUNCFLAGnothrowInprocess);
|
|
int blockexit = s.blockExit(this, isnothrow);
|
|
if (f.isnothrow && (global.errors != nothrowErrors))
|
|
.error(loc, "%s '%s' is nothrow yet may throw", kind(), toPrettyChars());
|
|
if (flags & FUNCFLAGnothrowInprocess && blockexit & BEthrow)
|
|
f.isnothrow = false;
|
|
|
|
if (sbody.blockExit(this, f.isnothrow) == BEfallthru)
|
|
sbody = new CompoundStatement(Loc(), sbody, s);
|
|
else
|
|
sbody = new TryFinallyStatement(Loc(), sbody, s);
|
|
}
|
|
}
|
|
}
|
|
// from this point on all possible 'throwers' are checked
|
|
flags &= ~FUNCFLAGnothrowInprocess;
|
|
|
|
if (isSynchronized())
|
|
{
|
|
/* Wrap the entire function body in a synchronized statement
|
|
*/
|
|
ClassDeclaration cd = isThis() ? isThis().isClassDeclaration() : parent.isClassDeclaration();
|
|
if (cd)
|
|
{
|
|
if (!global.params.is64bit && global.params.isWindows && !isStatic() && !sbody.usesEH() && !global.params.trace)
|
|
{
|
|
/* The back end uses the "jmonitor" hack for syncing;
|
|
* no need to do the sync at this level.
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
Expression vsync;
|
|
if (isStatic())
|
|
{
|
|
// The monitor is in the ClassInfo
|
|
vsync = new DotIdExp(loc, DsymbolExp.resolve(loc, sc2, cd, false), Id.classinfo);
|
|
}
|
|
else
|
|
{
|
|
// 'this' is the monitor
|
|
vsync = new VarExp(loc, vthis);
|
|
}
|
|
sbody = new PeelStatement(sbody); // don't redo semantic()
|
|
sbody = new SynchronizedStatement(loc, vsync, sbody);
|
|
sbody = sbody.semantic(sc2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error("synchronized function %s must be a member of a class", toChars());
|
|
}
|
|
}
|
|
// If declaration has no body, don't set sbody to prevent incorrect codegen.
|
|
InterfaceDeclaration id = parent.isInterfaceDeclaration();
|
|
if (fbody || id && (fdensure || fdrequire) && isVirtual())
|
|
fbody = sbody;
|
|
}
|
|
// Fix up forward-referenced gotos
|
|
if (gotos)
|
|
{
|
|
for (size_t i = 0; i < gotos.dim; ++i)
|
|
{
|
|
(*gotos)[i].checkLabel();
|
|
}
|
|
}
|
|
if (naked && (fensure || frequire))
|
|
error("naked assembly functions with contracts are not supported");
|
|
sc2.callSuper = 0;
|
|
sc2.pop();
|
|
}
|
|
|
|
if (checkClosure())
|
|
{
|
|
// We should be setting errors here instead of relying on the global error count.
|
|
//errors = true;
|
|
}
|
|
|
|
/* If function survived being marked as impure, then it is pure
|
|
*/
|
|
if (flags & FUNCFLAGpurityInprocess)
|
|
{
|
|
flags &= ~FUNCFLAGpurityInprocess;
|
|
if (type == f)
|
|
f = cast(TypeFunction)f.copy();
|
|
f.purity = PUREfwdref;
|
|
}
|
|
if (flags & FUNCFLAGsafetyInprocess)
|
|
{
|
|
flags &= ~FUNCFLAGsafetyInprocess;
|
|
if (type == f)
|
|
f = cast(TypeFunction)f.copy();
|
|
f.trust = TRUSTsafe;
|
|
}
|
|
if (flags & FUNCFLAGnogcInprocess)
|
|
{
|
|
flags &= ~FUNCFLAGnogcInprocess;
|
|
if (type == f)
|
|
f = cast(TypeFunction)f.copy();
|
|
f.isnogc = true;
|
|
}
|
|
flags &= ~FUNCFLAGreturnInprocess;
|
|
// reset deco to apply inference result to mangled name
|
|
if (f != type)
|
|
f.deco = null;
|
|
// Do semantic type AFTER pure/nothrow inference.
|
|
if (!f.deco && ident != Id.xopEquals && ident != Id.xopCmp)
|
|
{
|
|
sc = sc.push();
|
|
if (isCtorDeclaration()) // Bugzilla #15665
|
|
sc.flags |= SCOPEctor;
|
|
sc.stc = 0;
|
|
sc.linkage = linkage; // Bugzilla 8496
|
|
type = f.semantic(loc, sc);
|
|
sc = sc.pop();
|
|
}
|
|
/* If this function had instantiated with gagging, error reproduction will be
|
|
* done by TemplateInstance::semantic.
|
|
* Otherwise, error gagging should be temporarily ungagged by functionSemantic3.
|
|
*/
|
|
semanticRun = PASSsemantic3done;
|
|
semantic3Errors = (global.errors != oldErrors) || (fbody && fbody.isErrorStatement());
|
|
if (type.ty == Terror)
|
|
errors = true;
|
|
//printf("-FuncDeclaration::semantic3('%s.%s', sc = %p, loc = %s)\n", parent->toChars(), toChars(), sc, loc.toChars());
|
|
//fflush(stdout);
|
|
}
|
|
|
|
/****************************************************
|
|
* Resolve forward reference of function signature -
|
|
* parameter types, return type, and attributes.
|
|
* Returns false if any errors exist in the signature.
|
|
*/
|
|
final bool functionSemantic()
|
|
{
|
|
if (!_scope)
|
|
return !errors;
|
|
|
|
if (!originalType) // semantic not yet run
|
|
{
|
|
TemplateInstance spec = isSpeculative();
|
|
uint olderrs = global.errors;
|
|
uint oldgag = global.gag;
|
|
if (global.gag && !spec)
|
|
global.gag = 0;
|
|
semantic(_scope);
|
|
global.gag = oldgag;
|
|
if (spec && global.errors != olderrs)
|
|
spec.errors = (global.errors - olderrs != 0);
|
|
if (olderrs != global.errors) // if errors compiling this function
|
|
return false;
|
|
}
|
|
|
|
// if inferring return type, sematic3 needs to be run
|
|
// - When the function body contains any errors, we cannot assume
|
|
// the inferred return type is valid.
|
|
// So, the body errors should become the function signature error.
|
|
if (inferRetType && type && !type.nextOf())
|
|
return functionSemantic3();
|
|
|
|
TemplateInstance ti;
|
|
if (isInstantiated() && !isVirtualMethod() &&
|
|
!(ti = parent.isTemplateInstance(), ti && !ti.isTemplateMixin() && ti.tempdecl.ident != ident))
|
|
{
|
|
AggregateDeclaration ad = isMember2();
|
|
if (ad && ad.sizeok != SIZEOKdone)
|
|
{
|
|
/* Currently dmd cannot resolve forward references per methods,
|
|
* then setting SIZOKfwd is too conservative and would break existing code.
|
|
* So, just stop method attributes inference until ad->semantic() done.
|
|
*/
|
|
//ad->sizeok = SIZEOKfwd;
|
|
}
|
|
else
|
|
return functionSemantic3() || !errors;
|
|
}
|
|
|
|
if (storage_class & STCinference)
|
|
return functionSemantic3() || !errors;
|
|
|
|
return !errors;
|
|
}
|
|
|
|
/****************************************************
|
|
* Resolve forward reference of function body.
|
|
* Returns false if any errors exist in the body.
|
|
*/
|
|
final bool functionSemantic3()
|
|
{
|
|
if (semanticRun < PASSsemantic3 && _scope)
|
|
{
|
|
/* Forward reference - we need to run semantic3 on this function.
|
|
* If errors are gagged, and it's not part of a template instance,
|
|
* we need to temporarily ungag errors.
|
|
*/
|
|
TemplateInstance spec = isSpeculative();
|
|
uint olderrs = global.errors;
|
|
uint oldgag = global.gag;
|
|
version (IN_LLVM)
|
|
{
|
|
if (global.gag && !spec && !global.gaggedForInlining)
|
|
global.gag = 0;
|
|
}
|
|
else
|
|
{
|
|
if (global.gag && !spec)
|
|
global.gag = 0;
|
|
}
|
|
semantic3(_scope);
|
|
global.gag = oldgag;
|
|
|
|
// If it is a speculatively-instantiated template, and errors occur,
|
|
// we need to mark the template as having errors.
|
|
if (spec && global.errors != olderrs)
|
|
spec.errors = (global.errors - olderrs != 0);
|
|
if (olderrs != global.errors) // if errors compiling this function
|
|
return false;
|
|
}
|
|
|
|
return !errors && !semantic3Errors;
|
|
}
|
|
|
|
/****************************************************
|
|
* Check that this function type is properly resolved.
|
|
* If not, report "forward reference error" and return true.
|
|
*/
|
|
final bool checkForwardRef(Loc loc)
|
|
{
|
|
if (!functionSemantic())
|
|
return true;
|
|
|
|
/* No deco means the functionSemantic() call could not resolve
|
|
* forward referenes in the type of this function.
|
|
*/
|
|
if (!type.deco)
|
|
{
|
|
bool inSemantic3 = (inferRetType && semanticRun >= PASSsemantic3);
|
|
.error(loc, "forward reference to %s'%s'",
|
|
(inSemantic3 ? "inferred return type of function " : "").ptr,
|
|
toChars());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// called from semantic3
|
|
final VarDeclaration declareThis(Scope* sc, AggregateDeclaration ad)
|
|
{
|
|
if (ad && !isFuncLiteralDeclaration())
|
|
{
|
|
Type thandle = ad.handleType();
|
|
assert(thandle);
|
|
thandle = thandle.addMod(type.mod);
|
|
thandle = thandle.addStorageClass(storage_class);
|
|
VarDeclaration v = new ThisDeclaration(loc, thandle);
|
|
v.storage_class |= STCparameter;
|
|
if (thandle.ty == Tstruct)
|
|
{
|
|
v.storage_class |= STCref;
|
|
// if member function is marked 'inout', then 'this' is 'return ref'
|
|
if (type.ty == Tfunction && (cast(TypeFunction)type).iswild & 2)
|
|
v.storage_class |= STCreturn;
|
|
}
|
|
if (type.ty == Tfunction && (cast(TypeFunction)type).isreturn)
|
|
v.storage_class |= STCreturn;
|
|
|
|
v.semantic(sc);
|
|
if (!sc.insert(v))
|
|
assert(0);
|
|
v.parent = this;
|
|
return v;
|
|
}
|
|
if (isNested())
|
|
{
|
|
/* The 'this' for a nested function is the link to the
|
|
* enclosing function's stack frame.
|
|
* Note that nested functions and member functions are disjoint.
|
|
*/
|
|
VarDeclaration v = new ThisDeclaration(loc, Type.tvoid.pointerTo());
|
|
v.storage_class |= STCparameter;
|
|
v.semantic(sc);
|
|
if (!sc.insert(v))
|
|
assert(0);
|
|
v.parent = this;
|
|
return v;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
override final bool equals(RootObject o)
|
|
{
|
|
if (this == o)
|
|
return true;
|
|
Dsymbol s = isDsymbol(o);
|
|
if (s)
|
|
{
|
|
FuncDeclaration fd1 = this;
|
|
FuncDeclaration fd2 = s.isFuncDeclaration();
|
|
if (!fd2)
|
|
return false;
|
|
FuncAliasDeclaration fa1 = fd1.isFuncAliasDeclaration();
|
|
FuncAliasDeclaration fa2 = fd2.isFuncAliasDeclaration();
|
|
if (fa1 && fa2)
|
|
{
|
|
return fa1.toAliasFunc().equals(fa2.toAliasFunc()) && fa1.hasOverloads == fa2.hasOverloads;
|
|
}
|
|
if (fa1 && (fd1 = fa1.toAliasFunc()).isUnique() && !fa1.hasOverloads)
|
|
fa1 = null;
|
|
if (fa2 && (fd2 = fa2.toAliasFunc()).isUnique() && !fa2.hasOverloads)
|
|
fa2 = null;
|
|
if ((fa1 !is null) != (fa2 !is null))
|
|
return false;
|
|
return fd1.toParent().equals(fd2.toParent()) && fd1.ident.equals(fd2.ident) && fd1.type.equals(fd2.type);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/****************************************************
|
|
* Determine if 'this' overrides fd.
|
|
* Return !=0 if it does.
|
|
*/
|
|
final int overrides(FuncDeclaration fd)
|
|
{
|
|
int result = 0;
|
|
if (fd.ident == ident)
|
|
{
|
|
int cov = type.covariant(fd.type);
|
|
if (cov)
|
|
{
|
|
ClassDeclaration cd1 = toParent().isClassDeclaration();
|
|
ClassDeclaration cd2 = fd.toParent().isClassDeclaration();
|
|
if (cd1 && cd2 && cd2.isBaseOf(cd1, null))
|
|
result = 1;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*************************************************
|
|
* Find index of function in vtbl[0..dim] that
|
|
* this function overrides.
|
|
* Prefer an exact match to a covariant one.
|
|
* Returns:
|
|
* -1 didn't find one
|
|
* -2 can't determine because of forward references
|
|
*/
|
|
final int findVtblIndex(Dsymbols* vtbl, int dim)
|
|
{
|
|
FuncDeclaration mismatch = null;
|
|
StorageClass mismatchstc = 0;
|
|
int mismatchvi = -1;
|
|
int exactvi = -1;
|
|
int bestvi = -1;
|
|
for (int vi = 0; vi < dim; vi++)
|
|
{
|
|
FuncDeclaration fdv = (*vtbl)[vi].isFuncDeclaration();
|
|
if (fdv && fdv.ident == ident)
|
|
{
|
|
if (type.equals(fdv.type)) // if exact match
|
|
{
|
|
if (fdv.parent.isClassDeclaration())
|
|
return vi; // no need to look further
|
|
if (exactvi >= 0)
|
|
{
|
|
error("cannot determine overridden function");
|
|
return exactvi;
|
|
}
|
|
exactvi = vi;
|
|
bestvi = vi;
|
|
continue;
|
|
}
|
|
StorageClass stc = 0;
|
|
int cov = type.covariant(fdv.type, &stc);
|
|
//printf("\tbaseclass cov = %d\n", cov);
|
|
switch (cov)
|
|
{
|
|
case 0:
|
|
// types are distinct
|
|
break;
|
|
case 1:
|
|
bestvi = vi; // covariant, but not identical
|
|
break;
|
|
// keep looking for an exact match
|
|
case 2:
|
|
mismatchvi = vi;
|
|
mismatchstc = stc;
|
|
mismatch = fdv; // overrides, but is not covariant
|
|
break;
|
|
// keep looking for an exact match
|
|
case 3:
|
|
return -2; // forward references
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
}
|
|
if (bestvi == -1 && mismatch)
|
|
{
|
|
//type->print();
|
|
//mismatch->type->print();
|
|
//printf("%s %s\n", type->deco, mismatch->type->deco);
|
|
//printf("stc = %llx\n", mismatchstc);
|
|
if (mismatchstc)
|
|
{
|
|
// Fix it by modifying the type to add the storage classes
|
|
type = type.addStorageClass(mismatchstc);
|
|
bestvi = mismatchvi;
|
|
}
|
|
}
|
|
return bestvi;
|
|
}
|
|
|
|
/*********************************
|
|
* If function a function in a base class,
|
|
* return that base class.
|
|
* Params:
|
|
* cd = class that function is in
|
|
* Returns:
|
|
* base class if overriding, null if not
|
|
*/
|
|
final BaseClass* overrideInterface()
|
|
{
|
|
ClassDeclaration cd = parent.isClassDeclaration();
|
|
foreach (b; cd.interfaces)
|
|
{
|
|
auto v = findVtblIndex(&b.sym.vtbl, cast(int)b.sym.vtbl.dim);
|
|
if (v >= 0)
|
|
return b;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/****************************************************
|
|
* Overload this FuncDeclaration with the new one f.
|
|
* Return true if successful; i.e. no conflict.
|
|
*/
|
|
override bool overloadInsert(Dsymbol s)
|
|
{
|
|
//printf("FuncDeclaration::overloadInsert(s = %s) this = %s\n", s->toChars(), toChars());
|
|
assert(s != this);
|
|
AliasDeclaration ad = s.isAliasDeclaration();
|
|
if (ad)
|
|
{
|
|
if (overnext)
|
|
return overnext.overloadInsert(ad);
|
|
if (!ad.aliassym && ad.type.ty != Tident && ad.type.ty != Tinstance)
|
|
{
|
|
//printf("\tad = '%s'\n", ad->type->toChars());
|
|
return false;
|
|
}
|
|
overnext = ad;
|
|
//printf("\ttrue: no conflict\n");
|
|
return true;
|
|
}
|
|
TemplateDeclaration td = s.isTemplateDeclaration();
|
|
if (td)
|
|
{
|
|
if (!td.funcroot)
|
|
td.funcroot = this;
|
|
if (overnext)
|
|
return overnext.overloadInsert(td);
|
|
overnext = td;
|
|
return true;
|
|
}
|
|
FuncDeclaration fd = s.isFuncDeclaration();
|
|
if (!fd)
|
|
return false;
|
|
version (none)
|
|
{
|
|
/* Disable this check because:
|
|
* const void foo();
|
|
* semantic() isn't run yet on foo(), so the const hasn't been
|
|
* applied yet.
|
|
*/
|
|
if (type)
|
|
{
|
|
printf("type = %s\n", type.toChars());
|
|
printf("fd->type = %s\n", fd.type.toChars());
|
|
}
|
|
// fd->type can be NULL for overloaded constructors
|
|
if (type && fd.type && fd.type.covariant(type) && fd.type.mod == type.mod && !isFuncAliasDeclaration())
|
|
{
|
|
//printf("\tfalse: conflict %s\n", kind());
|
|
return false;
|
|
}
|
|
}
|
|
if (overnext)
|
|
{
|
|
td = overnext.isTemplateDeclaration();
|
|
if (td)
|
|
fd.overloadInsert(td);
|
|
else
|
|
return overnext.overloadInsert(fd);
|
|
}
|
|
overnext = fd;
|
|
//printf("\ttrue: no conflict\n");
|
|
return true;
|
|
}
|
|
|
|
/********************************************
|
|
* Find function in overload list that exactly matches t.
|
|
*/
|
|
final FuncDeclaration overloadExactMatch(Type t)
|
|
{
|
|
FuncDeclaration fd;
|
|
overloadApply(this, (Dsymbol s)
|
|
{
|
|
auto f = s.isFuncDeclaration();
|
|
if (!f)
|
|
return 0;
|
|
if (t.equals(f.type))
|
|
{
|
|
fd = f;
|
|
return 1;
|
|
}
|
|
|
|
/* Allow covariant matches, as long as the return type
|
|
* is just a const conversion.
|
|
* This allows things like pure functions to match with an impure function type.
|
|
*/
|
|
if (t.ty == Tfunction)
|
|
{
|
|
auto tf = cast(TypeFunction)f.type;
|
|
if (tf.covariant(t) == 1 &&
|
|
tf.nextOf().implicitConvTo(t.nextOf()) >= MATCHconst)
|
|
{
|
|
fd = f;
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
});
|
|
return fd;
|
|
}
|
|
|
|
/********************************************
|
|
* Find function in overload list that matches to the 'this' modifier.
|
|
* There's four result types.
|
|
*
|
|
* 1. If the 'tthis' matches only one candidate, it's an "exact match".
|
|
* Returns the function and 'hasOverloads' is set to false.
|
|
* eg. If 'tthis" is mutable and there's only one mutable method.
|
|
* 2. If there's two or more match candidates, but a candidate function will be
|
|
* a "better match".
|
|
* Returns the better match function but 'hasOverloads' is set to true.
|
|
* eg. If 'tthis' is mutable, and there's both mutable and const methods,
|
|
* the mutable method will be a better match.
|
|
* 3. If there's two or more match candidates, but there's no better match,
|
|
* Returns null and 'hasOverloads' is set to true to represent "ambiguous match".
|
|
* eg. If 'tthis' is mutable, and there's two or more mutable methods.
|
|
* 4. If there's no candidates, it's "no match" and returns null with error report.
|
|
* e.g. If 'tthis' is const but there's no const methods.
|
|
*/
|
|
final FuncDeclaration overloadModMatch(Loc loc, Type tthis, ref bool hasOverloads)
|
|
{
|
|
//printf("FuncDeclaration::overloadModMatch('%s')\n", toChars());
|
|
Match m;
|
|
m.last = MATCHnomatch;
|
|
overloadApply(this, (Dsymbol s)
|
|
{
|
|
auto f = s.isFuncDeclaration();
|
|
if (!f || f == m.lastf) // skip duplicates
|
|
return 0;
|
|
m.anyf = f;
|
|
|
|
auto tf = cast(TypeFunction)f.type;
|
|
//printf("tf = %s\n", tf->toChars());
|
|
|
|
MATCH match;
|
|
if (tthis) // non-static functions are preferred than static ones
|
|
{
|
|
if (f.needThis())
|
|
match = f.isCtorDeclaration() ? MATCHexact : MODmethodConv(tthis.mod, tf.mod);
|
|
else
|
|
match = MATCHconst; // keep static funciton in overload candidates
|
|
}
|
|
else // static functions are preferred than non-static ones
|
|
{
|
|
if (f.needThis())
|
|
match = MATCHconvert;
|
|
else
|
|
match = MATCHexact;
|
|
}
|
|
if (match == MATCHnomatch)
|
|
return 0;
|
|
|
|
if (match > m.last) goto LcurrIsBetter;
|
|
if (match < m.last) goto LlastIsBetter;
|
|
|
|
// See if one of the matches overrides the other.
|
|
if (m.lastf.overrides(f)) goto LlastIsBetter;
|
|
if (f.overrides(m.lastf)) goto LcurrIsBetter;
|
|
|
|
Lambiguous:
|
|
//printf("\tambiguous\n");
|
|
m.nextf = f;
|
|
m.count++;
|
|
return 0;
|
|
|
|
LlastIsBetter:
|
|
//printf("\tlastbetter\n");
|
|
m.count++; // count up
|
|
return 0;
|
|
|
|
LcurrIsBetter:
|
|
//printf("\tisbetter\n");
|
|
if (m.last <= MATCHconvert)
|
|
{
|
|
// clear last secondary matching
|
|
m.nextf = null;
|
|
m.count = 0;
|
|
}
|
|
m.last = match;
|
|
m.lastf = f;
|
|
m.count++; // count up
|
|
return 0;
|
|
});
|
|
|
|
if (m.count == 1) // exact match
|
|
{
|
|
hasOverloads = false;
|
|
}
|
|
else if (m.count > 1) // better or ambiguous match
|
|
{
|
|
hasOverloads = true;
|
|
}
|
|
else // no match
|
|
{
|
|
hasOverloads = true;
|
|
auto tf = cast(TypeFunction)this.type;
|
|
assert(tthis);
|
|
assert(!MODimplicitConv(tthis.mod, tf.mod)); // modifier mismatch
|
|
{
|
|
OutBuffer thisBuf, funcBuf;
|
|
MODMatchToBuffer(&thisBuf, tthis.mod, tf.mod);
|
|
MODMatchToBuffer(&funcBuf, tf.mod, tthis.mod);
|
|
.error(loc, "%smethod %s is not callable using a %sobject",
|
|
funcBuf.peekString(), this.toPrettyChars(), thisBuf.peekString());
|
|
}
|
|
}
|
|
return m.lastf;
|
|
}
|
|
|
|
/********************************************
|
|
* find function template root in overload list
|
|
*/
|
|
final TemplateDeclaration findTemplateDeclRoot()
|
|
{
|
|
FuncDeclaration f = this;
|
|
while (f && f.overnext)
|
|
{
|
|
//printf("f->overnext = %p %s\n", f->overnext, f->overnext->toChars());
|
|
TemplateDeclaration td = f.overnext.isTemplateDeclaration();
|
|
if (td)
|
|
return td;
|
|
f = f.overnext.isFuncDeclaration();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/********************************************
|
|
* Returns true if function was declared
|
|
* directly or indirectly in a unittest block
|
|
*/
|
|
final bool inUnittest()
|
|
{
|
|
Dsymbol f = this;
|
|
do
|
|
{
|
|
if (f.isUnitTestDeclaration())
|
|
return true;
|
|
f = f.toParent();
|
|
}
|
|
while (f);
|
|
return false;
|
|
}
|
|
|
|
/*************************************
|
|
* Determine partial specialization order of 'this' vs g.
|
|
* This is very similar to TemplateDeclaration::leastAsSpecialized().
|
|
* Returns:
|
|
* match 'this' is at least as specialized as g
|
|
* 0 g is more specialized than 'this'
|
|
*/
|
|
final MATCH leastAsSpecialized(FuncDeclaration g)
|
|
{
|
|
enum LOG_LEASTAS = 0;
|
|
static if (LOG_LEASTAS)
|
|
{
|
|
printf("%s.leastAsSpecialized(%s)\n", toChars(), g.toChars());
|
|
printf("%s, %s\n", type.toChars(), g.type.toChars());
|
|
}
|
|
/* This works by calling g() with f()'s parameters, and
|
|
* if that is possible, then f() is at least as specialized
|
|
* as g() is.
|
|
*/
|
|
TypeFunction tf = cast(TypeFunction)type;
|
|
TypeFunction tg = cast(TypeFunction)g.type;
|
|
size_t nfparams = Parameter.dim(tf.parameters);
|
|
/* If both functions have a 'this' pointer, and the mods are not
|
|
* the same and g's is not const, then this is less specialized.
|
|
*/
|
|
if (needThis() && g.needThis() && tf.mod != tg.mod)
|
|
{
|
|
if (isCtorDeclaration())
|
|
{
|
|
if (!MODimplicitConv(tg.mod, tf.mod))
|
|
return MATCHnomatch;
|
|
}
|
|
else
|
|
{
|
|
if (!MODimplicitConv(tf.mod, tg.mod))
|
|
return MATCHnomatch;
|
|
}
|
|
}
|
|
/* Create a dummy array of arguments out of the parameters to f()
|
|
*/
|
|
Expressions args;
|
|
args.setDim(nfparams);
|
|
for (size_t u = 0; u < nfparams; u++)
|
|
{
|
|
Parameter p = Parameter.getNth(tf.parameters, u);
|
|
Expression e;
|
|
if (p.storageClass & (STCref | STCout))
|
|
{
|
|
e = new IdentifierExp(Loc(), p.ident);
|
|
e.type = p.type;
|
|
}
|
|
else
|
|
e = p.type.defaultInitLiteral(Loc());
|
|
args[u] = e;
|
|
}
|
|
MATCH m = tg.callMatch(null, &args, 1);
|
|
if (m > MATCHnomatch)
|
|
{
|
|
/* A variadic parameter list is less specialized than a
|
|
* non-variadic one.
|
|
*/
|
|
if (tf.varargs && !tg.varargs)
|
|
goto L1;
|
|
// less specialized
|
|
static if (LOG_LEASTAS)
|
|
{
|
|
printf(" matches %d, so is least as specialized\n", m);
|
|
}
|
|
return m;
|
|
}
|
|
L1:
|
|
static if (LOG_LEASTAS)
|
|
{
|
|
printf(" doesn't match, so is not as specialized\n");
|
|
}
|
|
return MATCHnomatch;
|
|
}
|
|
|
|
/********************************
|
|
* Labels are in a separate scope, one per function.
|
|
*/
|
|
final LabelDsymbol searchLabel(Identifier ident)
|
|
{
|
|
Dsymbol s;
|
|
if (!labtab)
|
|
labtab = new DsymbolTable(); // guess we need one
|
|
s = labtab.lookup(ident);
|
|
if (!s)
|
|
{
|
|
s = new LabelDsymbol(ident);
|
|
labtab.insert(s);
|
|
}
|
|
return cast(LabelDsymbol)s;
|
|
}
|
|
|
|
/****************************************
|
|
* If non-static member function that has a 'this' pointer,
|
|
* return the aggregate it is a member of.
|
|
* Otherwise, return NULL.
|
|
*/
|
|
override AggregateDeclaration isThis()
|
|
{
|
|
//printf("+FuncDeclaration::isThis() '%s'\n", toChars());
|
|
AggregateDeclaration ad = null;
|
|
if ((storage_class & STCstatic) == 0 && !isFuncLiteralDeclaration())
|
|
{
|
|
ad = isMember2();
|
|
}
|
|
//printf("-FuncDeclaration::isThis() %p\n", ad);
|
|
return ad;
|
|
}
|
|
|
|
final AggregateDeclaration isMember2()
|
|
{
|
|
//printf("+FuncDeclaration::isMember2() '%s'\n", toChars());
|
|
AggregateDeclaration ad = null;
|
|
for (Dsymbol s = this; s; s = s.parent)
|
|
{
|
|
//printf("\ts = '%s', parent = '%s', kind = %s\n", s->toChars(), s->parent->toChars(), s->parent->kind());
|
|
ad = s.isMember();
|
|
if (ad)
|
|
{
|
|
break;
|
|
}
|
|
if (!s.parent || (!s.parent.isTemplateInstance()))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
//printf("-FuncDeclaration::isMember2() %p\n", ad);
|
|
return ad;
|
|
}
|
|
|
|
/*****************************************
|
|
* Determine lexical level difference from 'this' to nested function 'fd'.
|
|
* Error if this cannot call fd.
|
|
* Returns:
|
|
* 0 same level
|
|
* >0 decrease nesting by number
|
|
* -1 increase nesting by 1 (fd is nested within 'this')
|
|
* -2 error
|
|
*/
|
|
final int getLevel(Loc loc, Scope* sc, FuncDeclaration fd)
|
|
{
|
|
int level;
|
|
Dsymbol s;
|
|
Dsymbol fdparent;
|
|
//printf("FuncDeclaration::getLevel(fd = '%s')\n", fd.toChars());
|
|
fdparent = fd.toParent2();
|
|
if (fdparent == this)
|
|
return -1;
|
|
s = this;
|
|
level = 0;
|
|
while (fd != s && fdparent != s.toParent2())
|
|
{
|
|
//printf("\ts = %s, '%s'\n", s.kind(), s.toChars());
|
|
FuncDeclaration thisfd = s.isFuncDeclaration();
|
|
if (thisfd)
|
|
{
|
|
if (!thisfd.isNested() && !thisfd.vthis && !sc.intypeof)
|
|
goto Lerr;
|
|
}
|
|
else
|
|
{
|
|
AggregateDeclaration thiscd = s.isAggregateDeclaration();
|
|
if (thiscd)
|
|
{
|
|
/* AggregateDeclaration::isNested returns true only when
|
|
* it has a hidden pointer.
|
|
* But, calling the function belongs unrelated lexical scope
|
|
* is still allowed inside typeof.
|
|
*
|
|
* struct Map(alias fun) {
|
|
* typeof({ return fun(); }) RetType;
|
|
* // No member function makes Map struct 'not nested'.
|
|
* }
|
|
*/
|
|
if (!thiscd.isNested() && !sc.intypeof)
|
|
goto Lerr;
|
|
}
|
|
else
|
|
goto Lerr;
|
|
}
|
|
s = s.toParent2();
|
|
assert(s);
|
|
level++;
|
|
}
|
|
return level;
|
|
Lerr:
|
|
// Don't give error if in template constraint
|
|
if (!(sc.flags & SCOPEconstraint))
|
|
{
|
|
const(char)* xstatic = isStatic() ? "static " : "";
|
|
// better diagnostics for static functions
|
|
.error(loc, "%s%s %s cannot access frame of function %s", xstatic, kind(), toPrettyChars(), fd.toPrettyChars());
|
|
return -2;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
override const(char)* toPrettyChars(bool QualifyTypes = false)
|
|
{
|
|
if (isMain())
|
|
return "D main";
|
|
else
|
|
return Dsymbol.toPrettyChars(QualifyTypes);
|
|
}
|
|
|
|
/** for diagnostics, e.g. 'int foo(int x, int y) pure' */
|
|
final const(char)* toFullSignature()
|
|
{
|
|
OutBuffer buf;
|
|
functionToBufferWithIdent(cast(TypeFunction)type, &buf, toChars());
|
|
return buf.extractString();
|
|
}
|
|
|
|
final bool isMain()
|
|
{
|
|
return ident == Id.main && linkage != LINKc && !isMember() && !isNested();
|
|
}
|
|
|
|
final bool isCMain()
|
|
{
|
|
return ident == Id.main && linkage == LINKc && !isMember() && !isNested();
|
|
}
|
|
|
|
final bool isWinMain()
|
|
{
|
|
//printf("FuncDeclaration::isWinMain() %s\n", toChars());
|
|
version (none)
|
|
{
|
|
bool x = ident == Id.WinMain && linkage != LINKc && !isMember();
|
|
printf("%s\n", x ? "yes" : "no");
|
|
return x;
|
|
}
|
|
else
|
|
{
|
|
return ident == Id.WinMain && linkage != LINKc && !isMember();
|
|
}
|
|
}
|
|
|
|
final bool isDllMain()
|
|
{
|
|
return ident == Id.DllMain && linkage != LINKc && !isMember();
|
|
}
|
|
|
|
override final bool isExport()
|
|
{
|
|
return protection.kind == PROTexport;
|
|
}
|
|
|
|
override final bool isImportedSymbol()
|
|
{
|
|
//printf("isImportedSymbol()\n");
|
|
//printf("protection = %d\n", protection);
|
|
return (protection.kind == PROTexport) && !fbody;
|
|
}
|
|
|
|
override final bool isCodeseg()
|
|
{
|
|
return true; // functions are always in the code segment
|
|
}
|
|
|
|
override final bool isOverloadable()
|
|
{
|
|
return true; // functions can be overloaded
|
|
}
|
|
|
|
final PURE isPure()
|
|
{
|
|
//printf("FuncDeclaration::isPure() '%s'\n", toChars());
|
|
assert(type.ty == Tfunction);
|
|
TypeFunction tf = cast(TypeFunction)type;
|
|
if (flags & FUNCFLAGpurityInprocess)
|
|
setImpure();
|
|
if (tf.purity == PUREfwdref)
|
|
tf.purityLevel();
|
|
PURE purity = tf.purity;
|
|
if (purity > PUREweak && isNested())
|
|
purity = PUREweak;
|
|
if (purity > PUREweak && needThis())
|
|
{
|
|
// The attribute of the 'this' reference affects purity strength
|
|
if (type.mod & MODimmutable)
|
|
{
|
|
}
|
|
else if (type.mod & (MODconst | MODwild) && purity >= PUREconst)
|
|
purity = PUREconst;
|
|
else
|
|
purity = PUREweak;
|
|
}
|
|
tf.purity = purity;
|
|
// ^ This rely on the current situation that every FuncDeclaration has a
|
|
// unique TypeFunction.
|
|
return purity;
|
|
}
|
|
|
|
final PURE isPureBypassingInference()
|
|
{
|
|
if (flags & FUNCFLAGpurityInprocess)
|
|
return PUREfwdref;
|
|
else
|
|
return isPure();
|
|
}
|
|
|
|
/**************************************
|
|
* The function is doing something impure,
|
|
* so mark it as impure.
|
|
* If there's a purity error, return true.
|
|
*/
|
|
final bool setImpure()
|
|
{
|
|
if (flags & FUNCFLAGpurityInprocess)
|
|
{
|
|
flags &= ~FUNCFLAGpurityInprocess;
|
|
if (fes)
|
|
fes.func.setImpure();
|
|
}
|
|
else if (isPure())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
final bool isSafe()
|
|
{
|
|
assert(type.ty == Tfunction);
|
|
if (flags & FUNCFLAGsafetyInprocess)
|
|
setUnsafe();
|
|
return (cast(TypeFunction)type).trust == TRUSTsafe;
|
|
}
|
|
|
|
final bool isSafeBypassingInference()
|
|
{
|
|
return !(flags & FUNCFLAGsafetyInprocess) && isSafe();
|
|
}
|
|
|
|
final bool isTrusted()
|
|
{
|
|
assert(type.ty == Tfunction);
|
|
if (flags & FUNCFLAGsafetyInprocess)
|
|
setUnsafe();
|
|
return (cast(TypeFunction)type).trust == TRUSTtrusted;
|
|
}
|
|
|
|
/**************************************
|
|
* The function is doing something unsave,
|
|
* so mark it as unsafe.
|
|
* If there's a safe error, return true.
|
|
*/
|
|
final bool setUnsafe()
|
|
{
|
|
if (flags & FUNCFLAGsafetyInprocess)
|
|
{
|
|
flags &= ~FUNCFLAGsafetyInprocess;
|
|
(cast(TypeFunction)type).trust = TRUSTsystem;
|
|
if (fes)
|
|
fes.func.setUnsafe();
|
|
}
|
|
else if (isSafe())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
final bool isNogc()
|
|
{
|
|
assert(type.ty == Tfunction);
|
|
if (flags & FUNCFLAGnogcInprocess)
|
|
setGC();
|
|
return (cast(TypeFunction)type).isnogc;
|
|
}
|
|
|
|
final bool isNogcBypassingInference()
|
|
{
|
|
return !(flags & FUNCFLAGnogcInprocess) && isNogc();
|
|
}
|
|
|
|
/**************************************
|
|
* The function is doing something that may allocate with the GC,
|
|
* so mark it as not nogc (not no-how).
|
|
* Returns:
|
|
* true if function is marked as @nogc, meaning a user error occurred
|
|
*/
|
|
final bool setGC()
|
|
{
|
|
if (flags & FUNCFLAGnogcInprocess)
|
|
{
|
|
flags &= ~FUNCFLAGnogcInprocess;
|
|
(cast(TypeFunction)type).isnogc = false;
|
|
if (fes)
|
|
fes.func.setGC();
|
|
}
|
|
else if (isNogc())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
final void printGCUsage(Loc loc, const(char)* warn)
|
|
{
|
|
if (!global.params.vgc)
|
|
return;
|
|
Module m = getModule();
|
|
if (m && m.isRoot() && !inUnittest())
|
|
{
|
|
fprintf(global.stdmsg, "%s: vgc: %s\n", loc.toChars(), warn);
|
|
}
|
|
}
|
|
|
|
/********************************************
|
|
* Returns true if the function return value has no indirection
|
|
* which comes from the parameters.
|
|
*/
|
|
final bool isolateReturn()
|
|
{
|
|
assert(type.ty == Tfunction);
|
|
TypeFunction tf = cast(TypeFunction)type;
|
|
assert(tf.next);
|
|
Type treti = tf.next;
|
|
treti = tf.isref ? treti : getIndirection(treti);
|
|
if (!treti)
|
|
return true; // target has no mutable indirection
|
|
return parametersIntersect(treti);
|
|
}
|
|
|
|
/********************************************
|
|
* Returns true if an object typed t can have indirections
|
|
* which come from the parameters.
|
|
*/
|
|
final bool parametersIntersect(Type t)
|
|
{
|
|
assert(t);
|
|
if (!isPureBypassingInference() || isNested())
|
|
return false;
|
|
assert(type.ty == Tfunction);
|
|
TypeFunction tf = cast(TypeFunction)type;
|
|
//printf("parametersIntersect(%s) t = %s\n", tf->toChars(), t->toChars());
|
|
size_t dim = Parameter.dim(tf.parameters);
|
|
for (size_t i = 0; i < dim; i++)
|
|
{
|
|
Parameter fparam = Parameter.getNth(tf.parameters, i);
|
|
if (!fparam.type)
|
|
continue;
|
|
Type tprmi = (fparam.storageClass & (STClazy | STCout | STCref)) ? fparam.type : getIndirection(fparam.type);
|
|
if (!tprmi)
|
|
continue;
|
|
// there is no mutable indirection
|
|
//printf("\t[%d] tprmi = %d %s\n", i, tprmi->ty, tprmi->toChars());
|
|
if (traverseIndirections(tprmi, t))
|
|
return false;
|
|
}
|
|
if (AggregateDeclaration ad = isCtorDeclaration() ? null : isThis())
|
|
{
|
|
Type tthis = ad.getType().addMod(tf.mod);
|
|
//printf("\ttthis = %s\n", tthis->toChars());
|
|
if (traverseIndirections(tthis, t))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Determine if function needs
|
|
// a static frame pointer to its lexically enclosing function
|
|
bool isNested()
|
|
{
|
|
FuncDeclaration f = toAliasFunc();
|
|
//printf("\ttoParent2() = '%s'\n", f->toParent2()->toChars());
|
|
return ((f.storage_class & STCstatic) == 0) && (f.linkage == LINKd) && (f.toParent2().isFuncDeclaration() !is null);
|
|
}
|
|
|
|
override final bool needThis()
|
|
{
|
|
//printf("FuncDeclaration::needThis() '%s'\n", toChars());
|
|
return toAliasFunc().isThis() !is null;
|
|
}
|
|
|
|
// Determine if a function is pedantically virtual
|
|
final bool isVirtualMethod()
|
|
{
|
|
if (toAliasFunc() != this)
|
|
return toAliasFunc().isVirtualMethod();
|
|
//printf("FuncDeclaration::isVirtualMethod() %s\n", toChars());
|
|
if (!isVirtual())
|
|
return false;
|
|
// If it's a final method, and does not override anything, then it is not virtual
|
|
if (isFinalFunc() && foverrides.dim == 0)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Determine if function goes into virtual function pointer table
|
|
bool isVirtual()
|
|
{
|
|
if (toAliasFunc() != this)
|
|
return toAliasFunc().isVirtual();
|
|
Dsymbol p = toParent();
|
|
version (none)
|
|
{
|
|
printf("FuncDeclaration::isVirtual(%s)\n", toChars());
|
|
printf("isMember:%p isStatic:%d private:%d ctor:%d !Dlinkage:%d\n", isMember(), isStatic(), protection == PROTprivate, isCtorDeclaration(), linkage != LINKd);
|
|
printf("result is %d\n", isMember() && !(isStatic() || protection == PROTprivate || protection == PROTpackage) && p.isClassDeclaration() && !(p.isInterfaceDeclaration() && isFinalFunc()));
|
|
}
|
|
return isMember() && !(isStatic() || protection.kind == PROTprivate || protection.kind == PROTpackage) && p.isClassDeclaration() && !(p.isInterfaceDeclaration() && isFinalFunc());
|
|
}
|
|
|
|
bool isFinalFunc()
|
|
{
|
|
if (toAliasFunc() != this)
|
|
return toAliasFunc().isFinalFunc();
|
|
ClassDeclaration cd;
|
|
version (none)
|
|
{
|
|
printf("FuncDeclaration::isFinalFunc(%s), %x\n", toChars(), Declaration.isFinal());
|
|
printf("%p %d %d %d\n", isMember(), isStatic(), Declaration.isFinal(), ((cd = toParent().isClassDeclaration()) !is null && cd.storage_class & STCfinal));
|
|
printf("result is %d\n", isMember() && (Declaration.isFinal() || ((cd = toParent().isClassDeclaration()) !is null && cd.storage_class & STCfinal)));
|
|
if (cd)
|
|
printf("\tmember of %s\n", cd.toChars());
|
|
}
|
|
return isMember() && (Declaration.isFinal() || ((cd = toParent().isClassDeclaration()) !is null && cd.storage_class & STCfinal));
|
|
}
|
|
|
|
bool addPreInvariant()
|
|
{
|
|
AggregateDeclaration ad = isThis();
|
|
ClassDeclaration cd = ad ? ad.isClassDeclaration() : null;
|
|
return (ad && !(cd && cd.isCPPclass()) && global.params.useInvariants && (protection.kind == PROTprotected || protection.kind == PROTpublic || protection.kind == PROTexport) && !naked);
|
|
}
|
|
|
|
bool addPostInvariant()
|
|
{
|
|
AggregateDeclaration ad = isThis();
|
|
ClassDeclaration cd = ad ? ad.isClassDeclaration() : null;
|
|
return (ad && !(cd && cd.isCPPclass()) && ad.inv && global.params.useInvariants && (protection.kind == PROTprotected || protection.kind == PROTpublic || protection.kind == PROTexport) && !naked);
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "function";
|
|
}
|
|
|
|
/********************************************
|
|
* If there are no overloads of function f, return that function,
|
|
* otherwise return NULL.
|
|
*/
|
|
final FuncDeclaration isUnique()
|
|
{
|
|
FuncDeclaration result = null;
|
|
overloadApply(this, (Dsymbol s)
|
|
{
|
|
auto f = s.isFuncDeclaration();
|
|
if (!f)
|
|
return 0;
|
|
if (result)
|
|
{
|
|
result = null;
|
|
return 1; // ambiguous, done
|
|
}
|
|
else
|
|
{
|
|
result = f;
|
|
return 0;
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
|
|
/*********************************************
|
|
* In the current function, we are calling 'this' function.
|
|
* 1. Check to see if the current function can call 'this' function, issue error if not.
|
|
* 2. If the current function is not the parent of 'this' function, then add
|
|
* the current function to the list of siblings of 'this' function.
|
|
* 3. If the current function is a literal, and it's accessing an uplevel scope,
|
|
* then mark it as a delegate.
|
|
* Returns true if error occurs.
|
|
*/
|
|
final bool checkNestedReference(Scope* sc, Loc loc)
|
|
{
|
|
//printf("FuncDeclaration::checkNestedReference() %s\n", toPrettyChars());
|
|
|
|
if (auto fld = this.isFuncLiteralDeclaration())
|
|
{
|
|
if (fld.tok == TOKreserved)
|
|
{
|
|
fld.tok = TOKfunction;
|
|
fld.vthis = null;
|
|
}
|
|
}
|
|
|
|
if (!parent || parent == sc.parent)
|
|
return false;
|
|
if (ident == Id.require || ident == Id.ensure)
|
|
return false;
|
|
if (!isThis() && !isNested())
|
|
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);
|
|
|
|
if (isNested())
|
|
{
|
|
// The function that this function is in
|
|
FuncDeclaration fdv = p.isFuncDeclaration();
|
|
if (!fdv)
|
|
return false;
|
|
if (fdv == fdthis)
|
|
return false;
|
|
|
|
//printf("this = %s in [%s]\n", this.toChars(), this.loc.toChars());
|
|
//printf("fdv = %s in [%s]\n", fdv .toChars(), fdv .loc.toChars());
|
|
//printf("fdthis = %s in [%s]\n", fdthis.toChars(), fdthis.loc.toChars());
|
|
|
|
// Add this function to the list of those which called us
|
|
if (fdthis != this)
|
|
{
|
|
bool found = false;
|
|
for (size_t i = 0; i < siblingCallers.dim; ++i)
|
|
{
|
|
if (siblingCallers[i] == fdthis)
|
|
found = true;
|
|
}
|
|
if (!found)
|
|
{
|
|
//printf("\tadding sibling %s\n", fdthis.toPrettyChars());
|
|
if (!sc.intypeof && !(sc.flags & SCOPEcompile))
|
|
siblingCallers.push(fdthis);
|
|
}
|
|
}
|
|
|
|
int lv = fdthis.getLevel(loc, sc, fdv);
|
|
if (lv == -2)
|
|
return true; // error
|
|
if (lv == -1)
|
|
return false; // downlevel call
|
|
if (lv == 0)
|
|
return false; // same level call
|
|
// Uplevel call
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*******************************
|
|
* Look at all the variables in this function that are referenced
|
|
* by nested functions, and determine if a closure needs to be
|
|
* created for them.
|
|
*/
|
|
final bool needsClosure()
|
|
{
|
|
/* Need a closure for all the closureVars[] if any of the
|
|
* closureVars[] are accessed by a
|
|
* function that escapes the scope of this function.
|
|
* We take the conservative approach and decide that a function needs
|
|
* a closure if it:
|
|
* 1) is a virtual function
|
|
* 2) has its address taken
|
|
* 3) has a parent that escapes
|
|
* 4) calls another nested function that needs a closure
|
|
* -or-
|
|
* 5) this function returns a local struct/class
|
|
*
|
|
* Note that since a non-virtual function can be called by
|
|
* a virtual one, if that non-virtual function accesses a closure
|
|
* var, the closure still has to be taken. Hence, we check for isThis()
|
|
* instead of isVirtual(). (thanks to David Friedman)
|
|
*/
|
|
//printf("FuncDeclaration::needsClosure() %s\n", toChars());
|
|
if (requiresClosure)
|
|
goto Lyes;
|
|
for (size_t i = 0; i < closureVars.dim; i++)
|
|
{
|
|
VarDeclaration v = closureVars[i];
|
|
//printf("\tv = %s\n", v->toChars());
|
|
for (size_t j = 0; j < v.nestedrefs.dim; j++)
|
|
{
|
|
FuncDeclaration f = v.nestedrefs[j];
|
|
assert(f != this);
|
|
//printf("\t\tf = %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", f->toChars(), f->isVirtual(), f->isThis(), f->tookAddressOf);
|
|
/* Look to see if f escapes. We consider all parents of f within
|
|
* this, and also all siblings which call f; if any of them escape,
|
|
* so does f.
|
|
* Mark all affected functions as requiring closures.
|
|
*/
|
|
for (Dsymbol s = f; s && s != this; s = s.parent)
|
|
{
|
|
FuncDeclaration fx = s.isFuncDeclaration();
|
|
if (!fx)
|
|
continue;
|
|
if (fx.isThis() || fx.tookAddressOf)
|
|
{
|
|
//printf("\t\tfx = %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", fx->toChars(), fx->isVirtual(), fx->isThis(), fx->tookAddressOf);
|
|
/* Mark as needing closure any functions between this and f
|
|
*/
|
|
markAsNeedingClosure((fx == f) ? fx.parent : fx, this);
|
|
requiresClosure = true;
|
|
}
|
|
/* We also need to check if any sibling functions that
|
|
* called us, have escaped. This is recursive: we need
|
|
* to check the callers of our siblings.
|
|
*/
|
|
if (checkEscapingSiblings(fx, this))
|
|
requiresClosure = true;
|
|
/* Bugzilla 12406: Iterate all closureVars to mark all descendant
|
|
* nested functions that access to the closing context of this funciton.
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
if (requiresClosure)
|
|
goto Lyes;
|
|
/* Look for case (5)
|
|
*/
|
|
if (closureVars.dim)
|
|
{
|
|
assert(type.ty == Tfunction);
|
|
Type tret = (cast(TypeFunction)type).next;
|
|
assert(tret);
|
|
tret = tret.toBasetype();
|
|
//printf("\t\treturning %s\n", tret->toChars());
|
|
if (tret.ty == Tclass || tret.ty == Tstruct)
|
|
{
|
|
Dsymbol st = tret.toDsymbol(null);
|
|
//printf("\t\treturning class/struct %s\n", tret->toChars());
|
|
for (Dsymbol s = st.parent; s; s = s.parent)
|
|
{
|
|
//printf("\t\t\tparent = %s %s\n", s->kind(), s->toChars());
|
|
if (s == this)
|
|
{
|
|
//printf("\t\treturning local %s\n", st->toChars());
|
|
goto Lyes;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
Lyes:
|
|
//printf("\tneeds closure\n");
|
|
return true;
|
|
}
|
|
|
|
/***********************************************
|
|
* Check that the function contains any closure.
|
|
* If it's @nogc, report suitable errors.
|
|
* This is mostly consistent with FuncDeclaration::needsClosure().
|
|
*
|
|
* Returns:
|
|
* true if any errors occur.
|
|
*/
|
|
final bool checkClosure()
|
|
{
|
|
if (!needsClosure())
|
|
return false;
|
|
|
|
if (setGC())
|
|
{
|
|
error("is @nogc yet allocates closures with the GC");
|
|
if (global.gag) // need not report supplemental errors
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
printGCUsage(loc, "using closure causes GC allocation");
|
|
return false;
|
|
}
|
|
|
|
FuncDeclarations a;
|
|
foreach (v; closureVars)
|
|
{
|
|
foreach (f; v.nestedrefs)
|
|
{
|
|
assert(f !is this);
|
|
|
|
LcheckAncestorsOfANestedRef:
|
|
for (Dsymbol s = f; s && s !is this; s = s.parent)
|
|
{
|
|
auto fx = s.isFuncDeclaration();
|
|
if (!fx)
|
|
continue;
|
|
if (fx.isThis() ||
|
|
fx.tookAddressOf ||
|
|
checkEscapingSiblings(fx, this))
|
|
{
|
|
foreach (f2; a)
|
|
{
|
|
if (f2 == f)
|
|
break LcheckAncestorsOfANestedRef;
|
|
}
|
|
a.push(f);
|
|
.errorSupplemental(f.loc, "%s closes over variable %s at %s",
|
|
f.toPrettyChars(), v.toChars(), v.loc.toChars());
|
|
break LcheckAncestorsOfANestedRef;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/***********************************************
|
|
* Determine if function's variables are referenced by a function
|
|
* nested within it.
|
|
*/
|
|
final bool hasNestedFrameRefs()
|
|
{
|
|
if (closureVars.dim)
|
|
return true;
|
|
/* If a virtual function has contracts, assume its variables are referenced
|
|
* by those contracts, even if they aren't. Because they might be referenced
|
|
* by the overridden or overriding function's contracts.
|
|
* This can happen because frequire and fensure are implemented as nested functions,
|
|
* and they can be called directly by an overriding function and the overriding function's
|
|
* context had better match, or Bugzilla 7335 will bite.
|
|
*/
|
|
if (fdrequire || fdensure)
|
|
return true;
|
|
if (foverrides.dim && isVirtualMethod())
|
|
{
|
|
for (size_t i = 0; i < foverrides.dim; i++)
|
|
{
|
|
FuncDeclaration fdv = foverrides[i];
|
|
if (fdv.hasNestedFrameRefs())
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/****************************************************
|
|
* Declare result variable lazily.
|
|
*/
|
|
final void buildResultVar(Scope* sc, Type tret)
|
|
{
|
|
if (!vresult)
|
|
{
|
|
Loc loc = fensure ? fensure.loc : this.loc;
|
|
/* If inferRetType is true, tret may not be a correct return type yet.
|
|
* So, in here it may be a temporary type for vresult, and after
|
|
* fbody->semantic() running, vresult->type might be modified.
|
|
*/
|
|
vresult = new VarDeclaration(loc, tret, outId ? outId : Id.result, null);
|
|
vresult.noscope = true;
|
|
if (outId == Id.result)
|
|
vresult.storage_class |= STCtemp;
|
|
if (!isVirtual())
|
|
vresult.storage_class |= STCconst;
|
|
vresult.storage_class |= STCresult;
|
|
// set before the semantic() for checkNestedReference()
|
|
vresult.parent = this;
|
|
}
|
|
if (sc && vresult.sem == SemanticStart)
|
|
{
|
|
assert(type.ty == Tfunction);
|
|
TypeFunction tf = cast(TypeFunction)type;
|
|
if (tf.isref)
|
|
vresult.storage_class |= STCref | STCforeach;
|
|
vresult.type = tret;
|
|
vresult.semantic(sc);
|
|
if (!sc.insert(vresult))
|
|
error("out result %s is already defined", vresult.toChars());
|
|
assert(vresult.parent == this);
|
|
}
|
|
}
|
|
|
|
/****************************************************
|
|
* Merge into this function the 'in' contracts of all it overrides.
|
|
* 'in's are OR'd together, i.e. only one of them needs to pass.
|
|
*/
|
|
// IN_LLVM replaced: final Statement mergeFrequire(Statement sf)
|
|
final Statement mergeFrequire(Statement sf, Expressions *params = null)
|
|
{
|
|
if (params is null)
|
|
params = fdrequireParams;
|
|
|
|
/* If a base function and its override both have an IN contract, then
|
|
* only one of them needs to succeed. This is done by generating:
|
|
*
|
|
* void derived.in() {
|
|
* try {
|
|
* base.in();
|
|
* }
|
|
* catch () {
|
|
* ... body of derived.in() ...
|
|
* }
|
|
* }
|
|
*
|
|
* So if base.in() doesn't throw, derived.in() need not be executed, and the contract is valid.
|
|
* If base.in() throws, then derived.in()'s body is executed.
|
|
*/
|
|
version(IN_LLVM)
|
|
{
|
|
/* In LDC, we can't rely on these codegen hacks - we explicitly pass
|
|
* parameters on to the contract functions.
|
|
*/
|
|
} else {
|
|
/* Implementing this is done by having the overriding function call
|
|
* nested functions (the fdrequire functions) nested inside the overridden
|
|
* function. This requires that the stack layout of the calling function's
|
|
* parameters and 'this' pointer be in the same place (as the nested
|
|
* function refers to them).
|
|
* This is easy for the parameters, as they are all on the stack in the same
|
|
* place by definition, since it's an overriding function. The problem is
|
|
* getting the 'this' pointer in the same place, since it is a local variable.
|
|
* We did some hacks in the code generator to make this happen:
|
|
* 1. always generate exception handler frame, or at least leave space for it
|
|
* in the frame (Windows 32 SEH only)
|
|
* 2. always generate an EBP style frame
|
|
* 3. since 'this' is passed in a register that is subsequently copied into
|
|
* a stack local, allocate that local immediately following the exception
|
|
* handler block, so it is always at the same offset from EBP.
|
|
*/
|
|
}
|
|
for (size_t i = 0; i < foverrides.dim; i++)
|
|
{
|
|
FuncDeclaration fdv = foverrides[i];
|
|
/* The semantic pass on the contracts of the overridden functions must
|
|
* be completed before code generation occurs (bug 3602).
|
|
*/
|
|
if (fdv.fdrequire && fdv.fdrequire.semanticRun != PASSsemantic3done)
|
|
{
|
|
assert(fdv._scope);
|
|
Scope* sc = fdv._scope.push();
|
|
sc.stc &= ~STCoverride;
|
|
fdv.semantic3(sc);
|
|
sc.pop();
|
|
}
|
|
version(IN_LLVM)
|
|
sf = fdv.mergeFrequire(sf, params);
|
|
else
|
|
sf = fdv.mergeFrequire(sf);
|
|
if (sf && fdv.fdrequire)
|
|
{
|
|
//printf("fdv->frequire: %s\n", fdv->frequire->toChars());
|
|
/* Make the call:
|
|
* try { __require(); }
|
|
* catch { frequire; }
|
|
*/
|
|
version(IN_LLVM)
|
|
Expression e = new CallExp(loc, new VarExp(loc, fdv.fdrequire, false), params);
|
|
else
|
|
{
|
|
Expression eresult = null;
|
|
Expression e = new CallExp(loc, new VarExp(loc, fdv.fdrequire, false), eresult);
|
|
}
|
|
Statement s2 = new ExpStatement(loc, e);
|
|
auto c = new Catch(loc, null, null, sf);
|
|
c.internalCatch = true;
|
|
auto catches = new Catches();
|
|
catches.push(c);
|
|
sf = new TryCatchStatement(loc, s2, catches);
|
|
}
|
|
else
|
|
return null;
|
|
}
|
|
return sf;
|
|
}
|
|
|
|
/****************************************************
|
|
* Merge into this function the 'out' contracts of all it overrides.
|
|
* 'out's are AND'd together, i.e. all of them need to pass.
|
|
*/
|
|
// IN_LLVM replaced: final Statement mergeFensure(Statement sf, Identifier oid)
|
|
final Statement mergeFensure(Statement sf, Identifier oid, Expressions *params = null)
|
|
{
|
|
version(IN_LLVM)
|
|
{
|
|
if (params is null)
|
|
params = fdensureParams;
|
|
}
|
|
|
|
/* Same comments as for mergeFrequire(), except that we take care
|
|
* of generating a consistent reference to the 'result' local by
|
|
* explicitly passing 'result' to the nested function as a reference
|
|
* argument.
|
|
* This won't work for the 'this' parameter as it would require changing
|
|
* the semantic code for the nested function so that it looks on the parameter
|
|
* list for the 'this' pointer, something that would need an unknown amount
|
|
* of tweaking of various parts of the compiler that I'd rather leave alone.
|
|
*/
|
|
for (size_t i = 0; i < foverrides.dim; i++)
|
|
{
|
|
FuncDeclaration fdv = foverrides[i];
|
|
/* The semantic pass on the contracts of the overridden functions must
|
|
* be completed before code generation occurs (bug 3602 and 5230).
|
|
*/
|
|
if (fdv.fdensure && fdv.fdensure.semanticRun != PASSsemantic3done)
|
|
{
|
|
assert(fdv._scope);
|
|
Scope* sc = fdv._scope.push();
|
|
sc.stc &= ~STCoverride;
|
|
fdv.semantic3(sc);
|
|
sc.pop();
|
|
}
|
|
version(IN_LLVM)
|
|
sf = fdv.mergeFensure(sf, oid, params);
|
|
else
|
|
sf = fdv.mergeFensure(sf, oid);
|
|
if (fdv.fdensure)
|
|
{
|
|
//printf("fdv->fensure: %s\n", fdv->fensure->toChars());
|
|
// Make the call: __ensure(result)
|
|
Expression eresult = null;
|
|
if (outId)
|
|
{
|
|
version(IN_LLVM)
|
|
eresult = (*params)[0];
|
|
else
|
|
eresult = new IdentifierExp(loc, oid);
|
|
Type t1 = fdv.type.nextOf().toBasetype();
|
|
version(IN_LLVM)
|
|
{
|
|
// We actually check for matching types in CommaExp::toElem,
|
|
// 'testcontract' breaks without this.
|
|
t1 = t1.constOf();
|
|
}
|
|
Type t2 = this.type.nextOf().toBasetype();
|
|
if (t1.isBaseOf(t2, null))
|
|
{
|
|
/* Making temporary reference variable is necessary
|
|
* in covariant return.
|
|
* See bugzilla 5204 and 10479.
|
|
*/
|
|
auto ei = new ExpInitializer(Loc(), eresult);
|
|
auto v = new VarDeclaration(Loc(), t1, Identifier.generateId("__covres"), ei);
|
|
v.storage_class |= STCtemp;
|
|
auto de = new DeclarationExp(Loc(), v);
|
|
auto ve = new VarExp(Loc(), v);
|
|
eresult = new CommaExp(Loc(), de, ve);
|
|
}
|
|
}
|
|
version(IN_LLVM)
|
|
{
|
|
if (eresult !is null)
|
|
(*params)[0] = eresult;
|
|
Expression e = new CallExp(loc, new VarExp(loc, fdv.fdensure, false), params);
|
|
}
|
|
else
|
|
{
|
|
Expression e = new CallExp(loc, new VarExp(loc, fdv.fdensure, false), eresult);
|
|
}
|
|
Statement s2 = new ExpStatement(loc, e);
|
|
if (sf)
|
|
{
|
|
sf = new CompoundStatement(sf.loc, s2, sf);
|
|
}
|
|
else
|
|
sf = s2;
|
|
}
|
|
}
|
|
return sf;
|
|
}
|
|
|
|
/*********************************************
|
|
* Return the function's parameter list, and whether
|
|
* it is variadic or not.
|
|
*/
|
|
final Parameters* getParameters(int* pvarargs)
|
|
{
|
|
Parameters* fparameters = null;
|
|
int fvarargs = 0;
|
|
if (type)
|
|
{
|
|
assert(type.ty == Tfunction);
|
|
TypeFunction fdtype = cast(TypeFunction)type;
|
|
fparameters = fdtype.parameters;
|
|
fvarargs = fdtype.varargs;
|
|
}
|
|
if (pvarargs)
|
|
*pvarargs = fvarargs;
|
|
return fparameters;
|
|
}
|
|
|
|
/**********************************
|
|
* Generate a FuncDeclaration for a runtime library function.
|
|
*/
|
|
final static FuncDeclaration genCfunc(Parameters* fparams, Type treturn, const(char)* name, StorageClass stc = 0)
|
|
{
|
|
return genCfunc(fparams, treturn, Identifier.idPool(name, strlen(name)), stc);
|
|
}
|
|
|
|
final static FuncDeclaration genCfunc(Parameters* fparams, Type treturn, Identifier id, StorageClass stc = 0)
|
|
{
|
|
FuncDeclaration fd;
|
|
TypeFunction tf;
|
|
Dsymbol s;
|
|
static __gshared DsymbolTable st = null;
|
|
//printf("genCfunc(name = '%s')\n", id->toChars());
|
|
//printf("treturn\n\t"); treturn->print();
|
|
// See if already in table
|
|
if (!st)
|
|
st = new DsymbolTable();
|
|
s = st.lookup(id);
|
|
if (s)
|
|
{
|
|
fd = s.isFuncDeclaration();
|
|
assert(fd);
|
|
assert(fd.type.nextOf().equals(treturn));
|
|
}
|
|
else
|
|
{
|
|
tf = new TypeFunction(fparams, treturn, 0, LINKc, stc);
|
|
fd = new FuncDeclaration(Loc(), Loc(), id, STCstatic, tf);
|
|
fd.protection = Prot(PROTpublic);
|
|
fd.linkage = LINKc;
|
|
st.insert(fd);
|
|
}
|
|
return fd;
|
|
}
|
|
|
|
override final inout(FuncDeclaration) isFuncDeclaration() inout
|
|
{
|
|
return this;
|
|
}
|
|
|
|
FuncDeclaration toAliasFunc()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/********************************************************
|
|
* Generate Expression to call the invariant.
|
|
* Input:
|
|
* ad aggregate with the invariant
|
|
* vthis variable with 'this'
|
|
* direct call invariant directly
|
|
* Returns:
|
|
* void expression that calls the invariant
|
|
*/
|
|
extern (C++) Expression addInvariant(Loc loc, Scope* sc, AggregateDeclaration ad, VarDeclaration vthis, bool direct)
|
|
{
|
|
Expression e = null;
|
|
if (direct)
|
|
{
|
|
// Call invariant directly only if it exists
|
|
FuncDeclaration inv = ad.inv;
|
|
ClassDeclaration cd = ad.isClassDeclaration();
|
|
while (!inv && cd)
|
|
{
|
|
cd = cd.baseClass;
|
|
if (!cd)
|
|
break;
|
|
inv = cd.inv;
|
|
}
|
|
if (inv)
|
|
{
|
|
version (all)
|
|
{
|
|
// Workaround for bugzilla 13394: For the correct mangling,
|
|
// run attribute inference on inv if needed.
|
|
inv.functionSemantic();
|
|
}
|
|
//e = new DsymbolExp(Loc(), inv);
|
|
//e = new CallExp(Loc(), e);
|
|
//e = e->semantic(sc2);
|
|
/* Bugzilla 13113: Currently virtual invariant calls completely
|
|
* bypass attribute enforcement.
|
|
* Change the behavior of pre-invariant call by following it.
|
|
*/
|
|
e = new ThisExp(Loc());
|
|
e.type = vthis.type;
|
|
e = new DotVarExp(Loc(), e, inv, false);
|
|
e.type = inv.type;
|
|
e = new CallExp(Loc(), e);
|
|
e.type = Type.tvoid;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
version (all)
|
|
{
|
|
// Workaround for bugzilla 13394: For the correct mangling,
|
|
// run attribute inference on inv if needed.
|
|
if (ad.isStructDeclaration() && ad.inv)
|
|
ad.inv.functionSemantic();
|
|
}
|
|
// Call invariant virtually
|
|
version(IN_LLVM)
|
|
{
|
|
// We actually need a valid 'var' for codegen.
|
|
auto tv = new ThisExp(Loc());
|
|
tv.var = vthis;
|
|
Expression v = tv;
|
|
}
|
|
else
|
|
Expression v = new ThisExp(Loc());
|
|
v.type = vthis.type;
|
|
if (ad.isStructDeclaration())
|
|
v = v.addressOf();
|
|
e = new StringExp(Loc(), cast(char*)"null this");
|
|
e = new AssertExp(loc, v, e);
|
|
e = e.semantic(sc);
|
|
}
|
|
return e;
|
|
}
|
|
|
|
/***************************************************
|
|
* Visit each overloaded function/template in turn, and call dg(s) on it.
|
|
* Exit when no more, or dg(s) returns nonzero.
|
|
* Returns:
|
|
* ==0 continue
|
|
* !=0 done
|
|
*/
|
|
extern (D) int overloadApply(Dsymbol fstart, scope int delegate(Dsymbol) dg)
|
|
{
|
|
Dsymbol next;
|
|
for (Dsymbol d = fstart; d; d = next)
|
|
{
|
|
if (auto od = d.isOverDeclaration())
|
|
{
|
|
if (od.hasOverloads)
|
|
{
|
|
if (int r = overloadApply(od.aliassym, dg))
|
|
return r;
|
|
}
|
|
else
|
|
{
|
|
if (int r = dg(od.aliassym))
|
|
return r;
|
|
}
|
|
next = od.overnext;
|
|
}
|
|
else if (auto fa = d.isFuncAliasDeclaration())
|
|
{
|
|
if (fa.hasOverloads)
|
|
{
|
|
if (int r = overloadApply(fa.funcalias, dg))
|
|
return r;
|
|
}
|
|
else if (auto fd = fa.toAliasFunc())
|
|
{
|
|
if (int r = dg(fd))
|
|
return r;
|
|
}
|
|
else
|
|
{
|
|
d.error("is aliased to a function");
|
|
break;
|
|
}
|
|
next = fa.overnext;
|
|
}
|
|
else if (auto ad = d.isAliasDeclaration())
|
|
{
|
|
next = ad.toAlias();
|
|
if (next == ad)
|
|
break;
|
|
if (next == fstart)
|
|
break;
|
|
}
|
|
else if (auto td = d.isTemplateDeclaration())
|
|
{
|
|
if (int r = dg(td))
|
|
return r;
|
|
next = td.overnext;
|
|
}
|
|
else if (auto fd = d.isFuncDeclaration())
|
|
{
|
|
if (int r = dg(fd))
|
|
return r;
|
|
next = fd.overnext;
|
|
}
|
|
else
|
|
{
|
|
d.error("is aliased to a function");
|
|
break;
|
|
// BUG: should print error message?
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
extern (C++) int overloadApply(Dsymbol fstart, void* param, int function(void*, Dsymbol) fp)
|
|
{
|
|
return overloadApply(fstart, s => (*fp)(param, s));
|
|
}
|
|
|
|
extern (C++) static void MODMatchToBuffer(OutBuffer* buf, ubyte lhsMod, ubyte rhsMod)
|
|
{
|
|
bool bothMutable = ((lhsMod & rhsMod) == 0);
|
|
bool sharedMismatch = ((lhsMod ^ rhsMod) & MODshared) != 0;
|
|
bool sharedMismatchOnly = ((lhsMod ^ rhsMod) == MODshared);
|
|
if (lhsMod & MODshared)
|
|
buf.writestring("shared ");
|
|
else if (sharedMismatch && !(lhsMod & MODimmutable))
|
|
buf.writestring("non-shared ");
|
|
if (bothMutable && sharedMismatchOnly)
|
|
{
|
|
}
|
|
else if (lhsMod & MODimmutable)
|
|
buf.writestring("immutable ");
|
|
else if (lhsMod & MODconst)
|
|
buf.writestring("const ");
|
|
else if (lhsMod & MODwild)
|
|
buf.writestring("inout ");
|
|
else
|
|
buf.writestring("mutable ");
|
|
}
|
|
|
|
/*******************************************
|
|
* Given a symbol that could be either a FuncDeclaration or
|
|
* a function template, resolve it to a function symbol.
|
|
* loc instantiation location
|
|
* sc instantiation scope
|
|
* tiargs initial list of template arguments
|
|
* tthis if !NULL, the 'this' pointer argument
|
|
* fargs arguments to function
|
|
* flags 1: do not issue error message on no match, just return NULL
|
|
* 2: overloadResolve only
|
|
*/
|
|
extern (C++) FuncDeclaration resolveFuncCall(Loc loc, Scope* sc, Dsymbol s,
|
|
Objects* tiargs, Type tthis, Expressions* fargs, int flags = 0)
|
|
{
|
|
if (!s)
|
|
return null; // no match
|
|
|
|
version (none)
|
|
{
|
|
printf("resolveFuncCall('%s')\n", s.toChars());
|
|
if (fargs)
|
|
{
|
|
for (size_t i = 0; i < fargs.dim; i++)
|
|
{
|
|
Expression arg = (*fargs)[i];
|
|
assert(arg.type);
|
|
printf("\t%s: ", arg.toChars());
|
|
arg.type.print();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (tiargs && arrayObjectIsError(tiargs) ||
|
|
fargs && arrayObjectIsError(cast(Objects*)fargs))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
Match m;
|
|
m.last = MATCHnomatch;
|
|
|
|
functionResolve(&m, s, loc, sc, tiargs, tthis, fargs);
|
|
|
|
if (m.last > MATCHnomatch && m.lastf)
|
|
{
|
|
if (m.count == 1) // exactly one match
|
|
{
|
|
if (!(flags & 1))
|
|
m.lastf.functionSemantic();
|
|
return m.lastf;
|
|
}
|
|
if ((flags & 2) && !tthis && m.lastf.needThis())
|
|
{
|
|
return m.lastf;
|
|
}
|
|
}
|
|
|
|
/* Failed to find a best match.
|
|
* Do nothing or print error.
|
|
*/
|
|
if (m.last <= MATCHnomatch)
|
|
{
|
|
// error was caused on matched function
|
|
if (m.count == 1)
|
|
return m.lastf;
|
|
|
|
// if do not print error messages
|
|
if (flags & 1)
|
|
return null; // no match
|
|
}
|
|
|
|
auto fd = s.isFuncDeclaration();
|
|
auto od = s.isOverDeclaration();
|
|
auto td = s.isTemplateDeclaration();
|
|
if (td && td.funcroot)
|
|
s = fd = td.funcroot;
|
|
|
|
OutBuffer tiargsBuf;
|
|
arrayObjectsToBuffer(&tiargsBuf, tiargs);
|
|
|
|
OutBuffer fargsBuf;
|
|
fargsBuf.writeByte('(');
|
|
argExpTypesToCBuffer(&fargsBuf, fargs);
|
|
fargsBuf.writeByte(')');
|
|
if (tthis)
|
|
tthis.modToBuffer(&fargsBuf);
|
|
|
|
// max num of overloads to print (-v overrides this).
|
|
enum int numOverloadsDisplay = 5;
|
|
|
|
if (!m.lastf && !(flags & 1)) // no match
|
|
{
|
|
if (td && !fd) // all of overloads are templates
|
|
{
|
|
.error(loc, "%s %s.%s cannot deduce function from argument types !(%s)%s, candidates are:",
|
|
td.kind(), td.parent.toPrettyChars(), td.ident.toChars(),
|
|
tiargsBuf.peekString(), fargsBuf.peekString());
|
|
|
|
// Display candidate templates (even if there are no multiple overloads)
|
|
int numToDisplay = numOverloadsDisplay;
|
|
overloadApply(td, (Dsymbol s)
|
|
{
|
|
auto td = s.isTemplateDeclaration();
|
|
if (!td)
|
|
return 0;
|
|
.errorSupplemental(td.loc, "%s", td.toPrettyChars());
|
|
if (global.params.verbose || --numToDisplay != 0 || !td.overnext)
|
|
return 0;
|
|
|
|
// Too many overloads to sensibly display.
|
|
// Just show count of remaining overloads.
|
|
int num = 0;
|
|
overloadApply(td.overnext, (s) { ++num; return 0; });
|
|
if (num > 0)
|
|
.errorSupplemental(loc, "... (%d more, -v to show) ...", num);
|
|
return 1; // stop iterating
|
|
});
|
|
}
|
|
else if (od)
|
|
{
|
|
.error(loc, "none of the overloads of '%s' are callable using argument types !(%s)%s",
|
|
od.ident.toChars(), tiargsBuf.peekString(), fargsBuf.peekString());
|
|
}
|
|
else
|
|
{
|
|
assert(fd);
|
|
|
|
bool hasOverloads = fd.overnext !is null;
|
|
auto tf = cast(TypeFunction)fd.type;
|
|
if (tthis && !MODimplicitConv(tthis.mod, tf.mod)) // modifier mismatch
|
|
{
|
|
OutBuffer thisBuf, funcBuf;
|
|
MODMatchToBuffer(&thisBuf, tthis.mod, tf.mod);
|
|
MODMatchToBuffer(&funcBuf, tf.mod, tthis.mod);
|
|
if (hasOverloads)
|
|
{
|
|
.error(loc, "none of the overloads of '%s' are callable using a %sobject, candidates are:",
|
|
fd.ident.toChars(), thisBuf.peekString());
|
|
}
|
|
else
|
|
{
|
|
.error(loc, "%smethod %s is not callable using a %sobject",
|
|
funcBuf.peekString(), fd.toPrettyChars(),
|
|
thisBuf.peekString());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//printf("tf = %s, args = %s\n", tf->deco, (*fargs)[0]->type->deco);
|
|
if (hasOverloads)
|
|
{
|
|
.error(loc, "none of the overloads of '%s' are callable using argument types %s, candidates are:",
|
|
fd.ident.toChars(), fargsBuf.peekString());
|
|
}
|
|
else
|
|
{
|
|
fd.error(loc, "%s%s is not callable using argument types %s",
|
|
parametersTypeToChars(tf.parameters, tf.varargs),
|
|
tf.modToChars(), fargsBuf.peekString());
|
|
}
|
|
}
|
|
|
|
// Display candidate functions
|
|
int numToDisplay = numOverloadsDisplay;
|
|
overloadApply(hasOverloads ? fd : null, (Dsymbol s)
|
|
{
|
|
auto fd = s.isFuncDeclaration();
|
|
if (!fd || fd.errors || fd.type.ty == Terror)
|
|
return 0;
|
|
auto tf = cast(TypeFunction)fd.type;
|
|
.errorSupplemental(fd.loc, "%s%s", fd.toPrettyChars(),
|
|
parametersTypeToChars(tf.parameters, tf.varargs));
|
|
if (global.params.verbose || --numToDisplay != 0 || !fd.overnext)
|
|
return 0;
|
|
|
|
// Too many overloads to sensibly display.
|
|
int num = 0;
|
|
overloadApply(fd.overnext, (s){ num += !!s.isFuncDeclaration(); return 0; });
|
|
if (num > 0)
|
|
.errorSupplemental(loc, "... (%d more, -v to show) ...", num);
|
|
return 1; // stop iterating
|
|
});
|
|
}
|
|
}
|
|
else if (m.nextf)
|
|
{
|
|
TypeFunction tf1 = cast(TypeFunction)m.lastf.type;
|
|
TypeFunction tf2 = cast(TypeFunction)m.nextf.type;
|
|
const(char)* lastprms = parametersTypeToChars(tf1.parameters, tf1.varargs);
|
|
const(char)* nextprms = parametersTypeToChars(tf2.parameters, tf2.varargs);
|
|
.error(loc, "%s.%s called with argument types %s matches both:\n%s: %s%s\nand:\n%s: %s%s",
|
|
s.parent.toPrettyChars(), s.ident.toChars(),
|
|
fargsBuf.peekString(),
|
|
m.lastf.loc.toChars(), m.lastf.toPrettyChars(), lastprms,
|
|
m.nextf.loc.toChars(), m.nextf.toPrettyChars(), nextprms);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**************************************
|
|
* Returns an indirect type one step from t.
|
|
*/
|
|
extern (C++) Type getIndirection(Type t)
|
|
{
|
|
t = t.baseElemOf();
|
|
if (t.ty == Tarray || t.ty == Tpointer)
|
|
return t.nextOf().toBasetype();
|
|
if (t.ty == Taarray || t.ty == Tclass)
|
|
return t;
|
|
if (t.ty == Tstruct)
|
|
return t.hasPointers() ? t : null; // TODO
|
|
// should consider TypeDelegate?
|
|
return null;
|
|
}
|
|
|
|
/**************************************
|
|
* Returns true if memory reachable through a reference B to a value of type tb,
|
|
* which has been constructed with a reference A to a value of type ta
|
|
* available, can alias memory reachable from A based on the types involved
|
|
* (either directly or via any number of indirections).
|
|
*
|
|
* Note that this relation is not symmetric in the two arguments. For example,
|
|
* a const(int) reference can point to a pre-existing int, but not the other
|
|
* way round.
|
|
*/
|
|
extern (C++) bool traverseIndirections(Type ta, Type tb, void* p = null, bool reversePass = false)
|
|
{
|
|
Type source = ta;
|
|
Type target = tb;
|
|
if (reversePass)
|
|
{
|
|
source = tb;
|
|
target = ta;
|
|
}
|
|
if (source.constConv(target))
|
|
return true;
|
|
else if (target.ty == Tvoid && MODimplicitConv(source.mod, target.mod))
|
|
return true;
|
|
// No direct match, so try breaking up one of the types (starting with tb).
|
|
Type tbb = tb.toBasetype().baseElemOf();
|
|
if (tbb != tb)
|
|
return traverseIndirections(ta, tbb, p, reversePass);
|
|
// context date to detect circular look up
|
|
struct Ctxt
|
|
{
|
|
Ctxt* prev;
|
|
Type type;
|
|
}
|
|
|
|
Ctxt* ctxt = cast(Ctxt*)p;
|
|
if (tb.ty == Tclass || tb.ty == Tstruct)
|
|
{
|
|
for (Ctxt* c = ctxt; c; c = c.prev)
|
|
if (tb == c.type)
|
|
return false;
|
|
Ctxt c;
|
|
c.prev = ctxt;
|
|
c.type = tb;
|
|
AggregateDeclaration sym = tb.toDsymbol(null).isAggregateDeclaration();
|
|
for (size_t i = 0; i < sym.fields.dim; i++)
|
|
{
|
|
VarDeclaration v = sym.fields[i];
|
|
Type tprmi = v.type.addMod(tb.mod);
|
|
//printf("\ttb = %s, tprmi = %s\n", tb->toChars(), tprmi->toChars());
|
|
if (traverseIndirections(ta, tprmi, &c, reversePass))
|
|
return true;
|
|
}
|
|
}
|
|
else if (tb.ty == Tarray || tb.ty == Taarray || tb.ty == Tpointer)
|
|
{
|
|
Type tind = tb.nextOf();
|
|
if (traverseIndirections(ta, tind, ctxt, reversePass))
|
|
return true;
|
|
}
|
|
else if (tb.hasPointers())
|
|
{
|
|
// FIXME: function pointer/delegate types should be considered.
|
|
return true;
|
|
}
|
|
// Still no match, so try breaking up ta if we have note done so yet.
|
|
if (!reversePass)
|
|
return traverseIndirections(tb, ta, ctxt, true);
|
|
return false;
|
|
}
|
|
|
|
/* For all functions between outerFunc and f, mark them as needing
|
|
* a closure.
|
|
*/
|
|
extern (C++) void markAsNeedingClosure(Dsymbol f, FuncDeclaration outerFunc)
|
|
{
|
|
for (Dsymbol sx = f; sx && sx != outerFunc; sx = sx.parent)
|
|
{
|
|
FuncDeclaration fy = sx.isFuncDeclaration();
|
|
if (fy && fy.closureVars.dim)
|
|
{
|
|
/* fy needs a closure if it has closureVars[],
|
|
* because the frame pointer in the closure will be accessed.
|
|
*/
|
|
fy.requiresClosure = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Given a nested function f inside a function outerFunc, check
|
|
* if any sibling callers of f have escaped. If so, mark
|
|
* all the enclosing functions as needing closures.
|
|
* Return true if any closures were detected.
|
|
* This is recursive: we need to check the callers of our siblings.
|
|
* Note that nested functions can only call lexically earlier nested
|
|
* functions, so loops are impossible.
|
|
*/
|
|
extern (C++) bool checkEscapingSiblings(FuncDeclaration f, FuncDeclaration outerFunc, void* p = null)
|
|
{
|
|
struct PrevSibling
|
|
{
|
|
PrevSibling* p;
|
|
FuncDeclaration f;
|
|
}
|
|
|
|
PrevSibling ps;
|
|
ps.p = cast(PrevSibling*)p;
|
|
ps.f = f;
|
|
//printf("checkEscapingSiblings(f = %s, outerfunc = %s)\n", f->toChars(), outerFunc->toChars());
|
|
bool bAnyClosures = false;
|
|
for (size_t i = 0; i < f.siblingCallers.dim; ++i)
|
|
{
|
|
FuncDeclaration g = f.siblingCallers[i];
|
|
if (g.isThis() || g.tookAddressOf)
|
|
{
|
|
markAsNeedingClosure(g, outerFunc);
|
|
bAnyClosures = true;
|
|
}
|
|
PrevSibling* prev = cast(PrevSibling*)p;
|
|
while (1)
|
|
{
|
|
if (!prev)
|
|
{
|
|
bAnyClosures |= checkEscapingSiblings(g, outerFunc, &ps);
|
|
break;
|
|
}
|
|
if (prev.f == g)
|
|
break;
|
|
prev = prev.p;
|
|
}
|
|
}
|
|
//printf("\t%d\n", bAnyClosures);
|
|
return bAnyClosures;
|
|
}
|
|
|
|
/***********************************************************
|
|
* Used as a way to import a set of functions from another scope into this one.
|
|
*/
|
|
extern (C++) final class FuncAliasDeclaration : FuncDeclaration
|
|
{
|
|
public:
|
|
FuncDeclaration funcalias;
|
|
bool hasOverloads;
|
|
|
|
extern (D) this(Identifier ident, FuncDeclaration funcalias, bool hasOverloads = true)
|
|
{
|
|
super(funcalias.loc, funcalias.endloc, ident, funcalias.storage_class, funcalias.type);
|
|
assert(funcalias != this);
|
|
this.funcalias = funcalias;
|
|
this.hasOverloads = hasOverloads;
|
|
if (hasOverloads)
|
|
{
|
|
if (FuncAliasDeclaration fad = funcalias.isFuncAliasDeclaration())
|
|
this.hasOverloads = fad.hasOverloads;
|
|
}
|
|
else
|
|
{
|
|
// for internal use
|
|
assert(!funcalias.isFuncAliasDeclaration());
|
|
this.hasOverloads = false;
|
|
}
|
|
userAttribDecl = funcalias.userAttribDecl;
|
|
}
|
|
|
|
override inout(FuncAliasDeclaration) isFuncAliasDeclaration() inout
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "function alias";
|
|
}
|
|
|
|
override FuncDeclaration toAliasFunc()
|
|
{
|
|
return funcalias.toAliasFunc();
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class FuncLiteralDeclaration : FuncDeclaration
|
|
{
|
|
public:
|
|
TOK tok; // TOKfunction or TOKdelegate
|
|
Type treq; // target of return type inference
|
|
|
|
// backend
|
|
bool deferToObj;
|
|
|
|
extern (D) this(Loc loc, Loc endloc, Type type, TOK tok, ForeachStatement fes, Identifier id = null)
|
|
{
|
|
super(loc, endloc, null, STCundefined, type);
|
|
this.ident = id ? id : Id.empty;
|
|
this.tok = tok;
|
|
this.fes = fes;
|
|
//printf("FuncLiteralDeclaration() id = '%s', type = '%s'\n", this->ident->toChars(), type->toChars());
|
|
}
|
|
|
|
override Dsymbol syntaxCopy(Dsymbol s)
|
|
{
|
|
//printf("FuncLiteralDeclaration::syntaxCopy('%s')\n", toChars());
|
|
assert(!s);
|
|
auto f = new FuncLiteralDeclaration(loc, endloc, type.syntaxCopy(), tok, fes, ident);
|
|
f.treq = treq; // don't need to copy
|
|
return FuncDeclaration.syntaxCopy(f);
|
|
}
|
|
|
|
override bool isNested()
|
|
{
|
|
//printf("FuncLiteralDeclaration::isNested() '%s'\n", toChars());
|
|
return (tok != TOKfunction);
|
|
}
|
|
|
|
override bool isVirtual()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override bool addPreInvariant()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override bool addPostInvariant()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/*******************************
|
|
* Modify all expression type of return statements to tret.
|
|
*
|
|
* On function literals, return type may be modified based on the context type
|
|
* after its semantic3 is done, in FuncExp::implicitCastTo.
|
|
*
|
|
* A function() dg = (){ return new B(); } // OK if is(B : A) == true
|
|
*
|
|
* If B to A conversion is convariant that requires offseet adjusting,
|
|
* all return statements should be adjusted to return expressions typed A.
|
|
*/
|
|
void modifyReturns(Scope* sc, Type tret)
|
|
{
|
|
extern (C++) final class RetWalker : StatementRewriteWalker
|
|
{
|
|
alias visit = super.visit;
|
|
public:
|
|
Scope* sc;
|
|
Type tret;
|
|
FuncLiteralDeclaration fld;
|
|
|
|
override void visit(ReturnStatement s)
|
|
{
|
|
Expression exp = s.exp;
|
|
if (exp && !exp.type.equals(tret))
|
|
{
|
|
s.exp = exp.castTo(sc, tret);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (semanticRun < PASSsemantic3done)
|
|
return;
|
|
if (fes)
|
|
return;
|
|
scope RetWalker w = new RetWalker();
|
|
w.sc = sc;
|
|
w.tret = tret;
|
|
w.fld = this;
|
|
fbody.accept(w);
|
|
// Also update the inferred function type to match the new return type.
|
|
// This is required so the code generator does not try to cast the
|
|
// modified returns back to the original type.
|
|
if (inferRetType && type.nextOf() != tret)
|
|
(cast(TypeFunction)type).next = tret;
|
|
}
|
|
|
|
override inout(FuncLiteralDeclaration) isFuncLiteralDeclaration() inout
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
// GCC requires the (char*) casts
|
|
return (tok != TOKfunction) ? cast(char*)"delegate" : cast(char*)"function";
|
|
}
|
|
|
|
override const(char)* toPrettyChars(bool QualifyTypes = false)
|
|
{
|
|
if (parent)
|
|
{
|
|
TemplateInstance ti = parent.isTemplateInstance();
|
|
if (ti)
|
|
return ti.tempdecl.toPrettyChars(QualifyTypes);
|
|
}
|
|
return Dsymbol.toPrettyChars(QualifyTypes);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class CtorDeclaration : FuncDeclaration
|
|
{
|
|
public:
|
|
extern (D) this(Loc loc, Loc endloc, StorageClass stc, Type type)
|
|
{
|
|
super(loc, endloc, Id.ctor, stc, type);
|
|
//printf("CtorDeclaration(loc = %s) %s\n", loc.toChars(), toChars());
|
|
}
|
|
|
|
override Dsymbol syntaxCopy(Dsymbol s)
|
|
{
|
|
assert(!s);
|
|
auto f = new CtorDeclaration(loc, endloc, storage_class, type.syntaxCopy());
|
|
return FuncDeclaration.syntaxCopy(f);
|
|
}
|
|
|
|
override void semantic(Scope* sc)
|
|
{
|
|
//printf("CtorDeclaration::semantic() %s\n", toChars());
|
|
if (semanticRun >= PASSsemanticdone)
|
|
return;
|
|
if (_scope)
|
|
{
|
|
sc = _scope;
|
|
_scope = null;
|
|
}
|
|
parent = sc.parent;
|
|
Dsymbol p = toParent2();
|
|
AggregateDeclaration ad = p.isAggregateDeclaration();
|
|
if (!ad)
|
|
{
|
|
.error(loc, "constructor can only be a member of aggregate, not %s %s", p.kind(), p.toChars());
|
|
type = Type.terror;
|
|
errors = true;
|
|
return;
|
|
}
|
|
sc = sc.push();
|
|
sc.stc &= ~STCstatic; // not a static constructor
|
|
sc.flags |= SCOPEctor;
|
|
FuncDeclaration.semantic(sc);
|
|
sc.pop();
|
|
if (errors)
|
|
return;
|
|
TypeFunction tf = cast(TypeFunction)type;
|
|
assert(tf && tf.ty == Tfunction);
|
|
/* See if it's the default constructor
|
|
* But, template constructor should not become a default constructor.
|
|
*/
|
|
if (ad && (!parent.isTemplateInstance() || parent.isTemplateMixin()))
|
|
{
|
|
immutable dim = Parameter.dim(tf.parameters);
|
|
|
|
if (auto sd = ad.isStructDeclaration())
|
|
{
|
|
if (dim == 0 && tf.varargs == 0) // empty default ctor w/o any varargs
|
|
{
|
|
if (fbody || !(storage_class & STCdisable))
|
|
{
|
|
error("default constructor for structs only allowed " ~
|
|
"with @disable, no body, and no parameters");
|
|
storage_class |= STCdisable;
|
|
fbody = null;
|
|
}
|
|
sd.noDefaultCtor = true;
|
|
}
|
|
else if (dim == 0 && tf.varargs) // allow varargs only ctor
|
|
{
|
|
}
|
|
else if (dim && Parameter.getNth(tf.parameters, 0).defaultArg)
|
|
{
|
|
// if the first parameter has a default argument, then the rest does as well
|
|
if (storage_class & STCdisable)
|
|
{
|
|
deprecation("@disable'd constructor cannot have default "~
|
|
"arguments for all parameters.");
|
|
deprecationSupplemental(loc, "Use @disable this(); if you want to disable default initialization.");
|
|
}
|
|
else
|
|
deprecation("all parameters have default arguments, "~
|
|
"but structs cannot have default constructors.");
|
|
}
|
|
}
|
|
else if (dim == 0 && tf.varargs == 0)
|
|
{
|
|
ad.defaultCtor = this;
|
|
}
|
|
}
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "constructor";
|
|
}
|
|
|
|
override const(char)* toChars() const
|
|
{
|
|
return "this";
|
|
}
|
|
|
|
override bool isVirtual()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override bool addPreInvariant()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override bool addPostInvariant()
|
|
{
|
|
return (isThis() && vthis && global.params.useInvariants);
|
|
}
|
|
|
|
override inout(CtorDeclaration) isCtorDeclaration() inout
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class PostBlitDeclaration : FuncDeclaration
|
|
{
|
|
public:
|
|
extern (D) this(Loc loc, Loc endloc, StorageClass stc, Identifier id)
|
|
{
|
|
super(loc, endloc, id, stc, null);
|
|
}
|
|
|
|
override Dsymbol syntaxCopy(Dsymbol s)
|
|
{
|
|
assert(!s);
|
|
auto dd = new PostBlitDeclaration(loc, endloc, storage_class, ident);
|
|
return FuncDeclaration.syntaxCopy(dd);
|
|
}
|
|
|
|
override void semantic(Scope* sc)
|
|
{
|
|
//printf("PostBlitDeclaration::semantic() %s\n", toChars());
|
|
//printf("ident: %s, %s, %p, %p\n", ident->toChars(), Id::dtor->toChars(), ident, Id::dtor);
|
|
//printf("stc = x%llx\n", sc->stc);
|
|
if (semanticRun >= PASSsemanticdone)
|
|
return;
|
|
if (_scope)
|
|
{
|
|
sc = _scope;
|
|
_scope = null;
|
|
}
|
|
parent = sc.parent;
|
|
Dsymbol p = toParent2();
|
|
StructDeclaration ad = p.isStructDeclaration();
|
|
if (!ad)
|
|
{
|
|
.error(loc, "postblit can only be a member of struct/union, not %s %s", p.kind(), p.toChars());
|
|
type = Type.terror;
|
|
errors = true;
|
|
return;
|
|
}
|
|
if (ident == Id.postblit && semanticRun < PASSsemantic)
|
|
ad.postblits.push(this);
|
|
if (!type)
|
|
type = new TypeFunction(null, Type.tvoid, false, LINKd, storage_class);
|
|
sc = sc.push();
|
|
sc.stc &= ~STCstatic; // not static
|
|
sc.linkage = LINKd;
|
|
FuncDeclaration.semantic(sc);
|
|
sc.pop();
|
|
}
|
|
|
|
override bool isVirtual()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override bool addPreInvariant()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override bool addPostInvariant()
|
|
{
|
|
return (isThis() && vthis && global.params.useInvariants);
|
|
}
|
|
|
|
override bool overloadInsert(Dsymbol s)
|
|
{
|
|
return false; // cannot overload postblits
|
|
}
|
|
|
|
override inout(PostBlitDeclaration) isPostBlitDeclaration() inout
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class DtorDeclaration : FuncDeclaration
|
|
{
|
|
public:
|
|
extern (D) this(Loc loc, Loc endloc)
|
|
{
|
|
super(loc, endloc, Id.dtor, STCundefined, null);
|
|
}
|
|
|
|
extern (D) this(Loc loc, Loc endloc, StorageClass stc, Identifier id)
|
|
{
|
|
super(loc, endloc, id, stc, null);
|
|
}
|
|
|
|
override Dsymbol syntaxCopy(Dsymbol s)
|
|
{
|
|
assert(!s);
|
|
auto dd = new DtorDeclaration(loc, endloc, storage_class, ident);
|
|
return FuncDeclaration.syntaxCopy(dd);
|
|
}
|
|
|
|
override void semantic(Scope* sc)
|
|
{
|
|
//printf("DtorDeclaration::semantic() %s\n", toChars());
|
|
//printf("ident: %s, %s, %p, %p\n", ident->toChars(), Id::dtor->toChars(), ident, Id::dtor);
|
|
if (semanticRun >= PASSsemanticdone)
|
|
return;
|
|
if (_scope)
|
|
{
|
|
sc = _scope;
|
|
_scope = null;
|
|
}
|
|
parent = sc.parent;
|
|
Dsymbol p = toParent2();
|
|
AggregateDeclaration ad = p.isAggregateDeclaration();
|
|
if (!ad)
|
|
{
|
|
.error(loc, "destructor can only be a member of aggregate, not %s %s", p.kind(), p.toChars());
|
|
type = Type.terror;
|
|
errors = true;
|
|
return;
|
|
}
|
|
if (ident == Id.dtor && semanticRun < PASSsemantic)
|
|
ad.dtors.push(this);
|
|
if (!type)
|
|
type = new TypeFunction(null, Type.tvoid, false, LINKd, storage_class);
|
|
sc = sc.push();
|
|
sc.stc &= ~STCstatic; // not a static destructor
|
|
if (sc.linkage != LINKcpp)
|
|
sc.linkage = LINKd;
|
|
FuncDeclaration.semantic(sc);
|
|
sc.pop();
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "destructor";
|
|
}
|
|
|
|
override const(char)* toChars() const
|
|
{
|
|
return "~this";
|
|
}
|
|
|
|
override bool isVirtual()
|
|
{
|
|
// false so that dtor's don't get put into the vtbl[]
|
|
return false;
|
|
}
|
|
|
|
override bool addPreInvariant()
|
|
{
|
|
return (isThis() && vthis && global.params.useInvariants);
|
|
}
|
|
|
|
override bool addPostInvariant()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override bool overloadInsert(Dsymbol s)
|
|
{
|
|
return false; // cannot overload destructors
|
|
}
|
|
|
|
override inout(DtorDeclaration) isDtorDeclaration() inout
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) class StaticCtorDeclaration : FuncDeclaration
|
|
{
|
|
public:
|
|
final extern (D) this(Loc loc, Loc endloc, StorageClass stc)
|
|
{
|
|
super(loc, endloc, Identifier.generateId("_staticCtor"), STCstatic | stc, null);
|
|
}
|
|
|
|
final extern (D) this(Loc loc, Loc endloc, const(char)* name, StorageClass stc)
|
|
{
|
|
super(loc, endloc, Identifier.generateId(name), STCstatic | stc, null);
|
|
}
|
|
|
|
override Dsymbol syntaxCopy(Dsymbol s)
|
|
{
|
|
assert(!s);
|
|
auto scd = new StaticCtorDeclaration(loc, endloc, storage_class);
|
|
return FuncDeclaration.syntaxCopy(scd);
|
|
}
|
|
|
|
override final void semantic(Scope* sc)
|
|
{
|
|
//printf("StaticCtorDeclaration::semantic()\n");
|
|
if (semanticRun >= PASSsemanticdone)
|
|
return;
|
|
if (_scope)
|
|
{
|
|
sc = _scope;
|
|
_scope = null;
|
|
}
|
|
parent = sc.parent;
|
|
Dsymbol p = parent.pastMixin();
|
|
if (!p.isScopeDsymbol())
|
|
{
|
|
const(char)* s = (isSharedStaticCtorDeclaration() ? "shared " : "");
|
|
.error(loc, "%sstatic constructor can only be member of module/aggregate/template, not %s %s", s, p.kind(), p.toChars());
|
|
type = Type.terror;
|
|
errors = true;
|
|
return;
|
|
}
|
|
if (!type)
|
|
type = new TypeFunction(null, Type.tvoid, false, LINKd, storage_class);
|
|
/* If the static ctor appears within a template instantiation,
|
|
* it could get called multiple times by the module constructors
|
|
* for different modules. Thus, protect it with a gate.
|
|
*/
|
|
if (isInstantiated() && semanticRun < PASSsemantic)
|
|
{
|
|
/* Add this prefix to the function:
|
|
* static int gate;
|
|
* if (++gate != 1) return;
|
|
* Note that this is not thread safe; should not have threads
|
|
* during static construction.
|
|
*/
|
|
auto v = new VarDeclaration(Loc(), Type.tint32, Id.gate, null);
|
|
v.storage_class = STCtemp | (isSharedStaticCtorDeclaration() ? STCstatic : STCtls);
|
|
auto sa = new Statements();
|
|
Statement s = new ExpStatement(Loc(), v);
|
|
sa.push(s);
|
|
Expression e = new IdentifierExp(Loc(), v.ident);
|
|
e = new AddAssignExp(Loc(), e, new IntegerExp(1));
|
|
e = new EqualExp(TOKnotequal, Loc(), e, new IntegerExp(1));
|
|
s = new IfStatement(Loc(), null, e, new ReturnStatement(Loc(), null), null);
|
|
sa.push(s);
|
|
if (fbody)
|
|
sa.push(fbody);
|
|
fbody = new CompoundStatement(Loc(), sa);
|
|
}
|
|
FuncDeclaration.semantic(sc);
|
|
// We're going to need ModuleInfo
|
|
Module m = getModule();
|
|
if (!m)
|
|
m = sc._module;
|
|
if (m)
|
|
{
|
|
m.needmoduleinfo = 1;
|
|
//printf("module1 %s needs moduleinfo\n", m->toChars());
|
|
}
|
|
}
|
|
|
|
override final AggregateDeclaration isThis()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
override final bool isVirtual()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override final bool addPreInvariant()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override final bool addPostInvariant()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override final bool hasStaticCtorOrDtor()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override final inout(StaticCtorDeclaration) isStaticCtorDeclaration() inout
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class SharedStaticCtorDeclaration : StaticCtorDeclaration
|
|
{
|
|
public:
|
|
extern (D) this(Loc loc, Loc endloc, StorageClass stc)
|
|
{
|
|
super(loc, endloc, "_sharedStaticCtor", stc);
|
|
}
|
|
|
|
override Dsymbol syntaxCopy(Dsymbol s)
|
|
{
|
|
assert(!s);
|
|
auto scd = new SharedStaticCtorDeclaration(loc, endloc, storage_class);
|
|
return FuncDeclaration.syntaxCopy(scd);
|
|
}
|
|
|
|
override inout(SharedStaticCtorDeclaration) isSharedStaticCtorDeclaration() inout
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) class StaticDtorDeclaration : FuncDeclaration
|
|
{
|
|
public:
|
|
VarDeclaration vgate; // 'gate' variable
|
|
|
|
final extern (D) this(Loc loc, Loc endloc, StorageClass stc)
|
|
{
|
|
super(loc, endloc, Identifier.generateId("_staticDtor"), STCstatic | stc, null);
|
|
}
|
|
|
|
final extern (D) this(Loc loc, Loc endloc, const(char)* name, StorageClass stc)
|
|
{
|
|
super(loc, endloc, Identifier.generateId(name), STCstatic | stc, null);
|
|
}
|
|
|
|
override Dsymbol syntaxCopy(Dsymbol s)
|
|
{
|
|
assert(!s);
|
|
auto sdd = new StaticDtorDeclaration(loc, endloc, storage_class);
|
|
return FuncDeclaration.syntaxCopy(sdd);
|
|
}
|
|
|
|
override final void semantic(Scope* sc)
|
|
{
|
|
if (semanticRun >= PASSsemanticdone)
|
|
return;
|
|
if (_scope)
|
|
{
|
|
sc = _scope;
|
|
_scope = null;
|
|
}
|
|
parent = sc.parent;
|
|
Dsymbol p = parent.pastMixin();
|
|
if (!p.isScopeDsymbol())
|
|
{
|
|
const(char)* s = (isSharedStaticDtorDeclaration() ? "shared " : "");
|
|
.error(loc, "%sstatic destructor can only be member of module/aggregate/template, not %s %s", s, p.kind(), p.toChars());
|
|
type = Type.terror;
|
|
errors = true;
|
|
return;
|
|
}
|
|
if (!type)
|
|
type = new TypeFunction(null, Type.tvoid, false, LINKd, storage_class);
|
|
/* If the static ctor appears within a template instantiation,
|
|
* it could get called multiple times by the module constructors
|
|
* for different modules. Thus, protect it with a gate.
|
|
*/
|
|
if (isInstantiated() && semanticRun < PASSsemantic)
|
|
{
|
|
/* Add this prefix to the function:
|
|
* static int gate;
|
|
* if (--gate != 0) return;
|
|
* Increment gate during constructor execution.
|
|
* Note that this is not thread safe; should not have threads
|
|
* during static destruction.
|
|
*/
|
|
auto v = new VarDeclaration(Loc(), Type.tint32, Id.gate, null);
|
|
v.storage_class = STCtemp | (isSharedStaticDtorDeclaration() ? STCstatic : STCtls);
|
|
auto sa = new Statements();
|
|
Statement s = new ExpStatement(Loc(), v);
|
|
sa.push(s);
|
|
Expression e = new IdentifierExp(Loc(), v.ident);
|
|
e = new AddAssignExp(Loc(), e, new IntegerExp(-1)); // LDC_FIXME: Previously had (uint64_t)-1, double-check this.
|
|
e = new EqualExp(TOKnotequal, Loc(), e, new IntegerExp(0));
|
|
s = new IfStatement(Loc(), null, e, new ReturnStatement(Loc(), null), null);
|
|
sa.push(s);
|
|
if (fbody)
|
|
sa.push(fbody);
|
|
fbody = new CompoundStatement(Loc(), sa);
|
|
vgate = v;
|
|
}
|
|
FuncDeclaration.semantic(sc);
|
|
// We're going to need ModuleInfo
|
|
Module m = getModule();
|
|
if (!m)
|
|
m = sc._module;
|
|
if (m)
|
|
{
|
|
m.needmoduleinfo = 1;
|
|
//printf("module2 %s needs moduleinfo\n", m->toChars());
|
|
}
|
|
}
|
|
|
|
override final AggregateDeclaration isThis()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
override final bool isVirtual()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override final bool hasStaticCtorOrDtor()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override final bool addPreInvariant()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override final bool addPostInvariant()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override final inout(StaticDtorDeclaration) isStaticDtorDeclaration() inout
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class SharedStaticDtorDeclaration : StaticDtorDeclaration
|
|
{
|
|
public:
|
|
extern (D) this(Loc loc, Loc endloc, StorageClass stc)
|
|
{
|
|
super(loc, endloc, "_sharedStaticDtor", stc);
|
|
}
|
|
|
|
override Dsymbol syntaxCopy(Dsymbol s)
|
|
{
|
|
assert(!s);
|
|
auto sdd = new SharedStaticDtorDeclaration(loc, endloc, storage_class);
|
|
return FuncDeclaration.syntaxCopy(sdd);
|
|
}
|
|
|
|
override inout(SharedStaticDtorDeclaration) isSharedStaticDtorDeclaration() inout
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class InvariantDeclaration : FuncDeclaration
|
|
{
|
|
public:
|
|
extern (D) this(Loc loc, Loc endloc, StorageClass stc, Identifier id = null)
|
|
{
|
|
super(loc, endloc, id ? id : Identifier.generateId("__invariant"), stc, null);
|
|
}
|
|
|
|
override Dsymbol syntaxCopy(Dsymbol s)
|
|
{
|
|
assert(!s);
|
|
auto id = new InvariantDeclaration(loc, endloc, storage_class);
|
|
return FuncDeclaration.syntaxCopy(id);
|
|
}
|
|
|
|
override void semantic(Scope* sc)
|
|
{
|
|
if (semanticRun >= PASSsemanticdone)
|
|
return;
|
|
if (_scope)
|
|
{
|
|
sc = _scope;
|
|
_scope = null;
|
|
}
|
|
parent = sc.parent;
|
|
Dsymbol p = parent.pastMixin();
|
|
AggregateDeclaration ad = p.isAggregateDeclaration();
|
|
if (!ad)
|
|
{
|
|
.error(loc, "invariant can only be a member of aggregate, not %s %s", p.kind(), p.toChars());
|
|
type = Type.terror;
|
|
errors = true;
|
|
return;
|
|
}
|
|
if (ident != Id.classInvariant && semanticRun < PASSsemantic)
|
|
ad.invs.push(this);
|
|
if (!type)
|
|
type = new TypeFunction(null, Type.tvoid, false, LINKd, storage_class);
|
|
sc = sc.push();
|
|
sc.stc &= ~STCstatic; // not a static invariant
|
|
sc.stc |= STCconst; // invariant() is always const
|
|
sc.flags = (sc.flags & ~SCOPEcontract) | SCOPEinvariant;
|
|
sc.linkage = LINKd;
|
|
FuncDeclaration.semantic(sc);
|
|
sc.pop();
|
|
}
|
|
|
|
override bool isVirtual()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override bool addPreInvariant()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override bool addPostInvariant()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override inout(InvariantDeclaration) isInvariantDeclaration() inout
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
* Generate unique unittest function Id so we can have multiple
|
|
* instances per module.
|
|
*/
|
|
extern (C++) static Identifier unitTestId(Loc loc)
|
|
{
|
|
OutBuffer buf;
|
|
buf.printf("__unittestL%u_", loc.linnum);
|
|
return Identifier.generateId(buf.peekString());
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class UnitTestDeclaration : FuncDeclaration
|
|
{
|
|
public:
|
|
char* codedoc; // for documented unittest
|
|
|
|
// toObjFile() these nested functions after this one
|
|
FuncDeclarations deferredNested;
|
|
|
|
extern (D) this(Loc loc, Loc endloc, StorageClass stc, char* codedoc)
|
|
{
|
|
super(loc, endloc, unitTestId(loc), stc, null);
|
|
this.codedoc = codedoc;
|
|
}
|
|
|
|
override Dsymbol syntaxCopy(Dsymbol s)
|
|
{
|
|
assert(!s);
|
|
auto utd = new UnitTestDeclaration(loc, endloc, storage_class, codedoc);
|
|
return FuncDeclaration.syntaxCopy(utd);
|
|
}
|
|
|
|
override void semantic(Scope* sc)
|
|
{
|
|
if (semanticRun >= PASSsemanticdone)
|
|
return;
|
|
if (_scope)
|
|
{
|
|
sc = _scope;
|
|
_scope = null;
|
|
}
|
|
protection = sc.protection;
|
|
parent = sc.parent;
|
|
Dsymbol p = parent.pastMixin();
|
|
if (!p.isScopeDsymbol())
|
|
{
|
|
.error(loc, "unittest can only be a member of module/aggregate/template, not %s %s", p.kind(), p.toChars());
|
|
type = Type.terror;
|
|
errors = true;
|
|
return;
|
|
}
|
|
if (global.params.useUnitTests)
|
|
{
|
|
if (!type)
|
|
type = new TypeFunction(null, Type.tvoid, false, LINKd, storage_class);
|
|
Scope* sc2 = sc.push();
|
|
sc2.linkage = LINKd;
|
|
FuncDeclaration.semantic(sc2);
|
|
sc2.pop();
|
|
}
|
|
version (none)
|
|
{
|
|
// We're going to need ModuleInfo even if the unit tests are not
|
|
// compiled in, because other modules may import this module and refer
|
|
// to this ModuleInfo.
|
|
// (This doesn't make sense to me?)
|
|
Module m = getModule();
|
|
if (!m)
|
|
m = sc._module;
|
|
if (m)
|
|
{
|
|
//printf("module3 %s needs moduleinfo\n", m->toChars());
|
|
m.needmoduleinfo = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
override AggregateDeclaration isThis()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
override bool isVirtual()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override bool addPreInvariant()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override bool addPostInvariant()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override inout(UnitTestDeclaration) isUnitTestDeclaration() inout
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class NewDeclaration : FuncDeclaration
|
|
{
|
|
public:
|
|
Parameters* parameters;
|
|
int varargs;
|
|
|
|
extern (D) this(Loc loc, Loc endloc, StorageClass stc, Parameters* fparams, int varargs)
|
|
{
|
|
super(loc, endloc, Id.classNew, STCstatic | stc, null);
|
|
this.parameters = fparams;
|
|
this.varargs = varargs;
|
|
}
|
|
|
|
override Dsymbol syntaxCopy(Dsymbol s)
|
|
{
|
|
assert(!s);
|
|
auto f = new NewDeclaration(loc, endloc, storage_class, Parameter.arraySyntaxCopy(parameters), varargs);
|
|
return FuncDeclaration.syntaxCopy(f);
|
|
}
|
|
|
|
override void semantic(Scope* sc)
|
|
{
|
|
//printf("NewDeclaration::semantic()\n");
|
|
if (semanticRun >= PASSsemanticdone)
|
|
return;
|
|
if (_scope)
|
|
{
|
|
sc = _scope;
|
|
_scope = null;
|
|
}
|
|
parent = sc.parent;
|
|
Dsymbol p = parent.pastMixin();
|
|
if (!p.isAggregateDeclaration())
|
|
{
|
|
.error(loc, "allocator can only be a member of aggregate, not %s %s", p.kind(), p.toChars());
|
|
type = Type.terror;
|
|
errors = true;
|
|
return;
|
|
}
|
|
Type tret = Type.tvoid.pointerTo();
|
|
if (!type)
|
|
type = new TypeFunction(parameters, tret, varargs, LINKd, storage_class);
|
|
type = type.semantic(loc, sc);
|
|
assert(type.ty == Tfunction);
|
|
// Check that there is at least one argument of type size_t
|
|
TypeFunction tf = cast(TypeFunction)type;
|
|
if (Parameter.dim(tf.parameters) < 1)
|
|
{
|
|
error("at least one argument of type size_t expected");
|
|
}
|
|
else
|
|
{
|
|
Parameter fparam = Parameter.getNth(tf.parameters, 0);
|
|
if (!fparam.type.equals(Type.tsize_t))
|
|
error("first argument must be type size_t, not %s", fparam.type.toChars());
|
|
}
|
|
FuncDeclaration.semantic(sc);
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "allocator";
|
|
}
|
|
|
|
override bool isVirtual()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override bool addPreInvariant()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override bool addPostInvariant()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override inout(NewDeclaration) isNewDeclaration() inout
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class DeleteDeclaration : FuncDeclaration
|
|
{
|
|
public:
|
|
Parameters* parameters;
|
|
|
|
extern (D) this(Loc loc, Loc endloc, StorageClass stc, Parameters* fparams)
|
|
{
|
|
super(loc, endloc, Id.classDelete, STCstatic | stc, null);
|
|
this.parameters = fparams;
|
|
}
|
|
|
|
override Dsymbol syntaxCopy(Dsymbol s)
|
|
{
|
|
assert(!s);
|
|
auto f = new DeleteDeclaration(loc, endloc, storage_class, Parameter.arraySyntaxCopy(parameters));
|
|
return FuncDeclaration.syntaxCopy(f);
|
|
}
|
|
|
|
override void semantic(Scope* sc)
|
|
{
|
|
//printf("DeleteDeclaration::semantic()\n");
|
|
if (semanticRun >= PASSsemanticdone)
|
|
return;
|
|
if (_scope)
|
|
{
|
|
sc = _scope;
|
|
_scope = null;
|
|
}
|
|
parent = sc.parent;
|
|
Dsymbol p = parent.pastMixin();
|
|
if (!p.isAggregateDeclaration())
|
|
{
|
|
.error(loc, "deallocator can only be a member of aggregate, not %s %s", p.kind(), p.toChars());
|
|
type = Type.terror;
|
|
errors = true;
|
|
return;
|
|
}
|
|
if (!type)
|
|
type = new TypeFunction(parameters, Type.tvoid, 0, LINKd, storage_class);
|
|
type = type.semantic(loc, sc);
|
|
assert(type.ty == Tfunction);
|
|
// Check that there is only one argument of type void*
|
|
TypeFunction tf = cast(TypeFunction)type;
|
|
if (Parameter.dim(tf.parameters) != 1)
|
|
{
|
|
error("one argument of type void* expected");
|
|
}
|
|
else
|
|
{
|
|
Parameter fparam = Parameter.getNth(tf.parameters, 0);
|
|
if (!fparam.type.equals(Type.tvoid.pointerTo()))
|
|
error("one argument of type void* expected, not %s", fparam.type.toChars());
|
|
}
|
|
FuncDeclaration.semantic(sc);
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "deallocator";
|
|
}
|
|
|
|
override bool isDelete()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override bool isVirtual()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override bool addPreInvariant()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override bool addPostInvariant()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override inout(DeleteDeclaration) isDeleteDeclaration() inout
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|