mirror of
https://github.com/dlang/dmd.git
synced 2025-04-26 13:10:12 +03:00
Move functions from func.d to funcsem.d. (#16286)
These require `import dscope` that we want to remove from func.d in pursuit of https://github.com/orgs/dlang/projects/41. Other functions that still need to be moved include `overloadApply()`, `isUnique()` and `equals()`, the latter needing a visitor akin #15782.
This commit is contained in:
parent
5bb882c8bc
commit
cd6a32f24e
11 changed files with 820 additions and 866 deletions
|
@ -26,6 +26,7 @@ import dmd.dsymbol;
|
|||
import dmd.dsymbolsem;
|
||||
import dmd.errors;
|
||||
import dmd.func;
|
||||
import dmd.funcsem;
|
||||
import dmd.id;
|
||||
import dmd.identifier;
|
||||
import dmd.location;
|
||||
|
|
|
@ -27,6 +27,7 @@ import dmd.dtemplate;
|
|||
import dmd.errors;
|
||||
import dmd.expression;
|
||||
import dmd.func;
|
||||
import dmd.funcsem;
|
||||
import dmd.globals;
|
||||
import dmd.gluelayer;
|
||||
import dmd.id;
|
||||
|
|
|
@ -147,6 +147,7 @@ import dmd.dtemplate;
|
|||
import dmd.errors;
|
||||
import dmd.expression;
|
||||
import dmd.func;
|
||||
import dmd.funcsem;
|
||||
import dmd.globals;
|
||||
import dmd.id;
|
||||
import dmd.identifier;
|
||||
|
|
|
@ -28,6 +28,7 @@ import dmd.dtemplate;
|
|||
import dmd.errors;
|
||||
import dmd.expression;
|
||||
import dmd.func;
|
||||
import dmd.funcsem;
|
||||
import dmd.globals;
|
||||
import dmd.id;
|
||||
import dmd.identifier;
|
||||
|
|
|
@ -25,6 +25,7 @@ import dmd.dsymbol;
|
|||
import dmd.errors;
|
||||
import dmd.expression;
|
||||
import dmd.func;
|
||||
import dmd.funcsem;
|
||||
import dmd.globals : FeatureState;
|
||||
import dmd.id;
|
||||
import dmd.identifier;
|
||||
|
|
|
@ -3786,7 +3786,6 @@ public:
|
|||
bool isCodeseg() const final override;
|
||||
bool isOverloadable() const final override;
|
||||
bool isAbstract() final override;
|
||||
bool canInferAttributes(Scope* sc);
|
||||
void initInferAttributes();
|
||||
PURE isPure();
|
||||
bool isSafe();
|
||||
|
@ -3806,7 +3805,6 @@ public:
|
|||
bool checkClosure();
|
||||
bool hasNestedFrameRefs();
|
||||
static bool needsFensure(FuncDeclaration* fd);
|
||||
void buildEnsureRequire();
|
||||
ParameterList getParameterList();
|
||||
static FuncDeclaration* genCfunc(Array<Parameter* >* fparams, Type* treturn, const char* name, StorageClass stc = 0);
|
||||
static FuncDeclaration* genCfunc(Array<Parameter* >* fparams, Type* treturn, Identifier* id, StorageClass stc = 0);
|
||||
|
|
|
@ -512,149 +512,6 @@ extern (C++) class FuncDeclaration : Declaration
|
|||
return true;
|
||||
}
|
||||
|
||||
/********************************************
|
||||
* Find function in overload list that exactly matches t.
|
||||
*/
|
||||
extern (D) final FuncDeclaration overloadExactMatch(Type t)
|
||||
{
|
||||
FuncDeclaration fd;
|
||||
overloadApply(this, (Dsymbol s)
|
||||
{
|
||||
auto f = s.isFuncDeclaration();
|
||||
if (!f)
|
||||
return 0;
|
||||
if (f.storage_class & STC.disable)
|
||||
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) == Covariant.yes &&
|
||||
tf.nextOf().implicitConvTo(t.nextOf()) >= MATCH.constant)
|
||||
{
|
||||
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.
|
||||
*/
|
||||
extern (D) final FuncDeclaration overloadModMatch(const ref Loc loc, Type tthis, ref bool hasOverloads)
|
||||
{
|
||||
//printf("FuncDeclaration::overloadModMatch('%s')\n", toChars());
|
||||
MatchAccumulator m;
|
||||
overloadApply(this, (Dsymbol s)
|
||||
{
|
||||
auto f = s.isFuncDeclaration();
|
||||
if (!f || f == m.lastf) // skip duplicates
|
||||
return 0;
|
||||
|
||||
auto tf = f.type.toTypeFunction();
|
||||
//printf("tf = %s\n", tf.toChars());
|
||||
|
||||
MATCH match;
|
||||
if (tthis) // non-static functions are preferred than static ones
|
||||
{
|
||||
if (f.needThis())
|
||||
match = f.isCtorDeclaration() ? MATCH.exact : MODmethodConv(tthis.mod, tf.mod);
|
||||
else
|
||||
match = MATCH.constant; // keep static function in overload candidates
|
||||
}
|
||||
else // static functions are preferred than non-static ones
|
||||
{
|
||||
if (f.needThis())
|
||||
match = MATCH.convert;
|
||||
else
|
||||
match = MATCH.exact;
|
||||
}
|
||||
if (match == MATCH.nomatch)
|
||||
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;
|
||||
|
||||
//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 <= MATCH.convert)
|
||||
{
|
||||
// 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 = this.type.toTypeFunction();
|
||||
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", kind, toPrettyChars,
|
||||
funcBuf.peekChars(), this.toPrettyChars(), thisBuf.peekChars());
|
||||
}
|
||||
}
|
||||
return m.lastf;
|
||||
}
|
||||
|
||||
/********************************************
|
||||
* find function template root in overload list
|
||||
*/
|
||||
|
@ -855,43 +712,6 @@ extern (C++) class FuncDeclaration : Declaration
|
|||
return level;
|
||||
}
|
||||
|
||||
/***********************************
|
||||
* Determine lexical level difference from `this` to nested function `fd`.
|
||||
* Issue error if `this` cannot call `fd`.
|
||||
*
|
||||
* Params:
|
||||
* loc = location for error messages
|
||||
* sc = context
|
||||
* fd = target of call
|
||||
* decl = The `Declaration` that triggered this check.
|
||||
* Used to provide a better error message only.
|
||||
* Returns:
|
||||
* 0 same level
|
||||
* >0 decrease nesting by number
|
||||
* -1 increase nesting by 1 (`fd` is nested within 'this')
|
||||
* LevelError error
|
||||
*/
|
||||
extern (D) final int getLevelAndCheck(const ref Loc loc, Scope* sc, FuncDeclaration fd,
|
||||
Declaration decl)
|
||||
{
|
||||
int level = getLevel(fd, sc.intypeof);
|
||||
if (level != LevelError)
|
||||
return level;
|
||||
|
||||
// Don't give error if in template constraint
|
||||
if (!(sc.flags & SCOPE.constraint))
|
||||
{
|
||||
const(char)* xstatic = isStatic() ? "`static` " : "";
|
||||
// better diagnostics for static functions
|
||||
.error(loc, "%s%s `%s` cannot access %s `%s` in frame of function `%s`",
|
||||
xstatic, kind(), toPrettyChars(), decl.kind(), decl.toChars(),
|
||||
fd.toPrettyChars());
|
||||
.errorSupplemental(decl.loc, "`%s` declared here", decl.toChars());
|
||||
return LevelError;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
enum LevelError = -2;
|
||||
|
||||
override const(char)* toPrettyChars(bool QualifyTypes = false)
|
||||
|
@ -990,47 +810,6 @@ extern (C++) class FuncDeclaration : Declaration
|
|||
return false;
|
||||
}
|
||||
|
||||
/**********************************
|
||||
* Decide if attributes for this function can be inferred from examining
|
||||
* the function body.
|
||||
* Returns:
|
||||
* true if can
|
||||
*/
|
||||
final bool canInferAttributes(Scope* sc)
|
||||
{
|
||||
if (!fbody)
|
||||
return false;
|
||||
|
||||
if (isVirtualMethod() &&
|
||||
/*
|
||||
* https://issues.dlang.org/show_bug.cgi?id=21719
|
||||
*
|
||||
* If we have an auto virtual function we can infer
|
||||
* the attributes.
|
||||
*/
|
||||
!(inferRetType && !isCtorDeclaration()))
|
||||
return false; // since they may be overridden
|
||||
|
||||
if (sc.func &&
|
||||
/********** this is for backwards compatibility for the moment ********/
|
||||
(!isMember() || sc.func.isSafeBypassingInference() && !isInstantiated()))
|
||||
return true;
|
||||
|
||||
if (isFuncLiteralDeclaration() || // externs are not possible with literals
|
||||
(storage_class & STC.inference) || // do attribute inference
|
||||
(inferRetType && !isCtorDeclaration()))
|
||||
return true;
|
||||
|
||||
if (isInstantiated())
|
||||
{
|
||||
auto ti = parent.isTemplateInstance();
|
||||
if (ti is null || ti.isTemplateMixin() || ti.tempdecl.ident == ident)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*****************************************
|
||||
* Initialize for inferring the attributes of this function.
|
||||
*/
|
||||
|
@ -1633,101 +1412,6 @@ extern (C++) class FuncDeclaration : Declaration
|
|||
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.
|
||||
*/
|
||||
extern (D) final bool checkNestedReference(Scope* sc, const ref Loc loc)
|
||||
{
|
||||
//printf("FuncDeclaration::checkNestedReference() %s\n", toPrettyChars());
|
||||
|
||||
if (auto fld = this.isFuncLiteralDeclaration())
|
||||
{
|
||||
if (fld.tok == TOK.reserved)
|
||||
{
|
||||
fld.tok = TOK.function_;
|
||||
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 = toParentLocal();
|
||||
Dsymbol p2 = toParent2();
|
||||
|
||||
// Function literals from fdthis to p must be delegates
|
||||
ensureStaticLinkTo(fdthis, p);
|
||||
if (p != p2)
|
||||
ensureStaticLinkTo(fdthis, p2);
|
||||
|
||||
if (isNested())
|
||||
{
|
||||
// The function that this function is in
|
||||
bool checkEnclosing(FuncDeclaration fdv)
|
||||
{
|
||||
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.length; ++i)
|
||||
{
|
||||
if (siblingCallers[i] == fdthis)
|
||||
found = true;
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
//printf("\tadding sibling %s to %s\n", fdthis.toPrettyChars(), toPrettyChars());
|
||||
if (!sc.intypeof && !(sc.flags & SCOPE.compile))
|
||||
{
|
||||
siblingCallers.push(fdthis);
|
||||
computedEscapingSiblings = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const lv = fdthis.getLevelAndCheck(loc, sc, fdv, this);
|
||||
if (lv == LevelError)
|
||||
return true; // error
|
||||
if (lv == -1)
|
||||
return false; // downlevel call
|
||||
if (lv == 0)
|
||||
return false; // same level call
|
||||
|
||||
return false; // Uplevel call
|
||||
}
|
||||
|
||||
if (checkEnclosing(p.isFuncDeclaration()))
|
||||
return true;
|
||||
if (checkEnclosing(p == p2 ? null : p2.isFuncDeclaration()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*******************************
|
||||
* Look at all the variables in this function that are referenced
|
||||
* by nested functions, and determine if a closure needs to be
|
||||
|
@ -1922,147 +1606,6 @@ extern (C++) class FuncDeclaration : Declaration
|
|||
return false;
|
||||
}
|
||||
|
||||
/****************************************************
|
||||
* Check whether result variable can be built.
|
||||
* Returns:
|
||||
* `true` if the function has a return type that
|
||||
* is different from `void`.
|
||||
*/
|
||||
extern (D) private bool canBuildResultVar()
|
||||
{
|
||||
auto f = cast(TypeFunction)type;
|
||||
return f && f.nextOf() && f.nextOf().toBasetype().ty != Tvoid;
|
||||
}
|
||||
|
||||
/****************************************************
|
||||
* 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.
|
||||
*/
|
||||
extern (D) final Statement mergeFrequire(Statement sf, Expressions* params)
|
||||
{
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
foreach (fdv; foverrides)
|
||||
{
|
||||
/* The semantic pass on the contracts of the overridden functions must
|
||||
* be completed before code generation occurs.
|
||||
* https://issues.dlang.org/show_bug.cgi?id=3602
|
||||
*/
|
||||
if (fdv.frequires && fdv.semanticRun != PASS.semantic3done)
|
||||
{
|
||||
assert(fdv._scope);
|
||||
Scope* sc = fdv._scope.push();
|
||||
sc.stc &= ~STC.override_;
|
||||
fdv.semantic3(sc);
|
||||
sc.pop();
|
||||
}
|
||||
|
||||
sf = fdv.mergeFrequire(sf, params);
|
||||
if (!sf || !fdv.fdrequire)
|
||||
return null;
|
||||
//printf("fdv.frequire: %s\n", fdv.frequire.toChars());
|
||||
/* Make the call:
|
||||
* try { __require(params); }
|
||||
* catch (Throwable) { frequire; }
|
||||
*/
|
||||
params = Expression.arraySyntaxCopy(params);
|
||||
Expression e = new CallExp(loc, new VarExp(loc, fdv.fdrequire, false), params);
|
||||
Statement s2 = new ExpStatement(loc, e);
|
||||
|
||||
auto c = new Catch(loc, getThrowable(), null, sf);
|
||||
c.internalCatch = true;
|
||||
auto catches = new Catches();
|
||||
catches.push(c);
|
||||
sf = new TryCatchStatement(loc, s2, catches);
|
||||
}
|
||||
return sf;
|
||||
}
|
||||
|
||||
/****************************************************
|
||||
* Merge into this function the 'in' contracts of all it overrides.
|
||||
*/
|
||||
extern (D) final Statement mergeFrequireInclusivePreview(Statement sf, Expressions* params)
|
||||
{
|
||||
/* If a base function and its override both have an IN contract, then
|
||||
* the override in contract must widen the guarantee of the base contract.
|
||||
* This is checked by generating:
|
||||
*
|
||||
* void derived.in() {
|
||||
* try {
|
||||
* ... body of derived.in() ...
|
||||
* }
|
||||
* catch () {
|
||||
* // derived in rejected this argument. so parent must also reject it, or we've tightened the contract.
|
||||
* base.in();
|
||||
* assert(false, "Logic error: " ~ thr.msg);
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
|
||||
foreach (fdv; foverrides)
|
||||
{
|
||||
/* The semantic pass on the contracts of the overridden functions must
|
||||
* be completed before code generation occurs.
|
||||
* https://issues.dlang.org/show_bug.cgi?id=3602
|
||||
*/
|
||||
if (fdv.frequires && fdv.semanticRun != PASS.semantic3done)
|
||||
{
|
||||
assert(fdv._scope);
|
||||
Scope* sc = fdv._scope.push();
|
||||
sc.stc &= ~STC.override_;
|
||||
fdv.semantic3(sc);
|
||||
sc.pop();
|
||||
}
|
||||
|
||||
sf = fdv.mergeFrequireInclusivePreview(sf, params);
|
||||
if (sf && fdv.fdrequire)
|
||||
{
|
||||
const loc = this.fdrequire.loc;
|
||||
|
||||
//printf("fdv.frequire: %s\n", fdv.frequire.toChars());
|
||||
/* Make the call:
|
||||
* try { frequire; }
|
||||
* catch (Throwable thr) { __require(params); assert(false, "Logic error: " ~ thr.msg); }
|
||||
*/
|
||||
Identifier id = Identifier.generateId("thr");
|
||||
params = Expression.arraySyntaxCopy(params);
|
||||
Expression e = new CallExp(loc, new VarExp(loc, fdv.fdrequire, false), params);
|
||||
Statement s2 = new ExpStatement(loc, e);
|
||||
// assert(false, ...)
|
||||
// TODO make this a runtime helper to allow:
|
||||
// - chaining the original expression
|
||||
// - nogc concatenation
|
||||
Expression msg = new StringExp(loc, "Logic error: in-contract was tighter than parent in-contract");
|
||||
Statement fail = new ExpStatement(loc, new AssertExp(loc, IntegerExp.literal!0, msg));
|
||||
|
||||
Statement s3 = new CompoundStatement(loc, s2, fail);
|
||||
|
||||
auto c = new Catch(loc, getThrowable(), id, s3);
|
||||
c.internalCatch = true;
|
||||
auto catches = new Catches();
|
||||
catches.push(c);
|
||||
sf = new TryCatchStatement(loc, sf, catches);
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
return sf;
|
||||
}
|
||||
|
||||
/****************************************************
|
||||
* Determine whether an 'out' contract is declared inside
|
||||
* the given function or any of its overrides.
|
||||
|
@ -2084,232 +1627,6 @@ extern (C++) class FuncDeclaration : Declaration
|
|||
return false;
|
||||
}
|
||||
|
||||
/****************************************************
|
||||
* Rewrite contracts as statements.
|
||||
*/
|
||||
final void buildEnsureRequire()
|
||||
{
|
||||
|
||||
if (frequires)
|
||||
{
|
||||
/* in { statements1... }
|
||||
* in { statements2... }
|
||||
* ...
|
||||
* becomes:
|
||||
* in { { statements1... } { statements2... } ... }
|
||||
*/
|
||||
assert(frequires.length);
|
||||
auto loc = (*frequires)[0].loc;
|
||||
auto s = new Statements;
|
||||
foreach (r; *frequires)
|
||||
{
|
||||
s.push(new ScopeStatement(r.loc, r, r.loc));
|
||||
}
|
||||
frequire = new CompoundStatement(loc, s);
|
||||
}
|
||||
|
||||
if (fensures)
|
||||
{
|
||||
/* out(id1) { statements1... }
|
||||
* out(id2) { statements2... }
|
||||
* ...
|
||||
* becomes:
|
||||
* out(__result) { { ref id1 = __result; { statements1... } }
|
||||
* { ref id2 = __result; { statements2... } } ... }
|
||||
*/
|
||||
assert(fensures.length);
|
||||
auto loc = (*fensures)[0].ensure.loc;
|
||||
auto s = new Statements;
|
||||
foreach (r; *fensures)
|
||||
{
|
||||
if (r.id && canBuildResultVar())
|
||||
{
|
||||
auto rloc = r.ensure.loc;
|
||||
auto resultId = new IdentifierExp(rloc, Id.result);
|
||||
auto init = new ExpInitializer(rloc, resultId);
|
||||
auto stc = STC.ref_ | STC.temp | STC.result;
|
||||
auto decl = new VarDeclaration(rloc, null, r.id, init, stc);
|
||||
auto sdecl = new ExpStatement(rloc, decl);
|
||||
s.push(new ScopeStatement(rloc, new CompoundStatement(rloc, sdecl, r.ensure), rloc));
|
||||
}
|
||||
else
|
||||
{
|
||||
s.push(r.ensure);
|
||||
}
|
||||
}
|
||||
fensure = new CompoundStatement(loc, s);
|
||||
}
|
||||
|
||||
if (!isVirtual())
|
||||
return;
|
||||
|
||||
/* Rewrite contracts as nested functions, then call them. Doing it as nested
|
||||
* functions means that overriding functions can call them.
|
||||
*/
|
||||
TypeFunction f = cast(TypeFunction) type;
|
||||
|
||||
/* Make a copy of the parameters and make them all ref */
|
||||
static Parameters* toRefCopy(ParameterList parameterList)
|
||||
{
|
||||
auto result = new Parameters();
|
||||
|
||||
foreach (n, p; parameterList)
|
||||
{
|
||||
p = p.syntaxCopy();
|
||||
if (!p.isLazy())
|
||||
p.storageClass = (p.storageClass | STC.ref_) & ~STC.out_;
|
||||
p.defaultArg = null; // won't be the same with ref
|
||||
result.push(p);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (frequire)
|
||||
{
|
||||
/* in { ... }
|
||||
* becomes:
|
||||
* void __require(ref params) { ... }
|
||||
* __require(params);
|
||||
*/
|
||||
Loc loc = frequire.loc;
|
||||
fdrequireParams = new Expressions();
|
||||
if (parameters)
|
||||
{
|
||||
foreach (vd; *parameters)
|
||||
fdrequireParams.push(new VarExp(loc, vd));
|
||||
}
|
||||
auto fo = cast(TypeFunction)(originalType ? originalType : f);
|
||||
auto fparams = toRefCopy(fo.parameterList);
|
||||
auto tf = new TypeFunction(ParameterList(fparams), Type.tvoid, LINK.d);
|
||||
tf.isnothrow = f.isnothrow;
|
||||
tf.isnogc = f.isnogc;
|
||||
tf.purity = f.purity;
|
||||
tf.trust = f.trust;
|
||||
auto fd = new FuncDeclaration(loc, loc, Id.require, STC.undefined_, tf);
|
||||
fd.fbody = frequire;
|
||||
Statement s1 = new ExpStatement(loc, fd);
|
||||
Expression e = new CallExp(loc, new VarExp(loc, fd, false), fdrequireParams);
|
||||
Statement s2 = new ExpStatement(loc, e);
|
||||
frequire = new CompoundStatement(loc, s1, s2);
|
||||
fdrequire = fd;
|
||||
}
|
||||
|
||||
/* We need to set fdensureParams here and not in the block below to
|
||||
* have the parameters available when calling a base class ensure(),
|
||||
* even if this function doesn't have an out contract.
|
||||
*/
|
||||
fdensureParams = new Expressions();
|
||||
if (canBuildResultVar())
|
||||
fdensureParams.push(new IdentifierExp(loc, Id.result));
|
||||
if (parameters)
|
||||
{
|
||||
foreach (vd; *parameters)
|
||||
fdensureParams.push(new VarExp(loc, vd));
|
||||
}
|
||||
|
||||
if (fensure)
|
||||
{
|
||||
/* out (result) { ... }
|
||||
* becomes:
|
||||
* void __ensure(ref tret result, ref params) { ... }
|
||||
* __ensure(result, params);
|
||||
*/
|
||||
Loc loc = fensure.loc;
|
||||
auto fparams = new Parameters();
|
||||
if (canBuildResultVar())
|
||||
{
|
||||
Parameter p = new Parameter(loc, STC.ref_ | STC.const_, f.nextOf(), Id.result, null, null);
|
||||
fparams.push(p);
|
||||
}
|
||||
auto fo = cast(TypeFunction)(originalType ? originalType : f);
|
||||
fparams.pushSlice((*toRefCopy(fo.parameterList))[]);
|
||||
auto tf = new TypeFunction(ParameterList(fparams), Type.tvoid, LINK.d);
|
||||
tf.isnothrow = f.isnothrow;
|
||||
tf.isnogc = f.isnogc;
|
||||
tf.purity = f.purity;
|
||||
tf.trust = f.trust;
|
||||
auto fd = new FuncDeclaration(loc, loc, Id.ensure, STC.undefined_, tf);
|
||||
fd.fbody = fensure;
|
||||
Statement s1 = new ExpStatement(loc, fd);
|
||||
Expression e = new CallExp(loc, new VarExp(loc, fd, false), fdensureParams);
|
||||
Statement s2 = new ExpStatement(loc, e);
|
||||
fensure = new CompoundStatement(loc, s1, s2);
|
||||
fdensure = fd;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************
|
||||
* 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.
|
||||
*/
|
||||
extern (D) final Statement mergeFensure(Statement sf, Identifier oid, Expressions* params)
|
||||
{
|
||||
/* 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.
|
||||
*/
|
||||
foreach (fdv; foverrides)
|
||||
{
|
||||
/* The semantic pass on the contracts of the overridden functions must
|
||||
* be completed before code generation occurs.
|
||||
* https://issues.dlang.org/show_bug.cgi?id=3602 and
|
||||
* https://issues.dlang.org/show_bug.cgi?id=5230
|
||||
*/
|
||||
if (needsFensure(fdv) && fdv.semanticRun != PASS.semantic3done)
|
||||
{
|
||||
assert(fdv._scope);
|
||||
Scope* sc = fdv._scope.push();
|
||||
sc.stc &= ~STC.override_;
|
||||
fdv.semantic3(sc);
|
||||
sc.pop();
|
||||
}
|
||||
|
||||
sf = fdv.mergeFensure(sf, oid, params);
|
||||
if (fdv.fdensure)
|
||||
{
|
||||
//printf("fdv.fensure: %s\n", fdv.fensure.toChars());
|
||||
// Make the call: __ensure(result, params)
|
||||
params = Expression.arraySyntaxCopy(params);
|
||||
if (canBuildResultVar())
|
||||
{
|
||||
Type t1 = fdv.type.nextOf().toBasetype();
|
||||
Type t2 = this.type.nextOf().toBasetype();
|
||||
if (t1.isBaseOf(t2, null))
|
||||
{
|
||||
/* Making temporary reference variable is necessary
|
||||
* in covariant return.
|
||||
* https://issues.dlang.org/show_bug.cgi?id=5204
|
||||
* https://issues.dlang.org/show_bug.cgi?id=10479
|
||||
*/
|
||||
Expression* eresult = &(*params)[0];
|
||||
auto ei = new ExpInitializer(Loc.initial, *eresult);
|
||||
auto v = new VarDeclaration(Loc.initial, t1, Identifier.generateId("__covres"), ei);
|
||||
v.storage_class |= STC.temp;
|
||||
auto de = new DeclarationExp(Loc.initial, v);
|
||||
auto ve = new VarExp(Loc.initial, v);
|
||||
*eresult = new CommaExp(Loc.initial, de, ve);
|
||||
}
|
||||
}
|
||||
Expression e = new CallExp(loc, new VarExp(loc, fdv.fdensure, false), params);
|
||||
Statement s2 = new ExpStatement(loc, e);
|
||||
|
||||
if (sf)
|
||||
{
|
||||
sf = new CompoundStatement(sf.loc, s2, sf);
|
||||
}
|
||||
else
|
||||
sf = s2;
|
||||
}
|
||||
}
|
||||
return sf;
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
* Returns: the function's parameter list, and whether
|
||||
* it is variadic or not.
|
||||
|
@ -3073,56 +2390,6 @@ extern (C++) final class FuncLiteralDeclaration : FuncDeclaration
|
|||
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.
|
||||
*/
|
||||
extern (D) void modifyReturns(Scope* sc, Type tret)
|
||||
{
|
||||
import dmd.statement_rewrite_walker;
|
||||
|
||||
extern (C++) final class RetWalker : StatementRewriteWalker
|
||||
{
|
||||
alias visit = typeof(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.implicitCastTo(sc, tret);
|
||||
}
|
||||
}
|
||||
|
||||
if (semanticRun < PASS.semantic3done)
|
||||
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)
|
||||
type.toTypeFunction().next = tret;
|
||||
}
|
||||
|
||||
override inout(FuncLiteralDeclaration) isFuncLiteralDeclaration() inout
|
||||
{
|
||||
return this;
|
||||
|
@ -3654,136 +2921,6 @@ extern (C++) final class NewDeclaration : FuncDeclaration
|
|||
}
|
||||
}
|
||||
|
||||
/**************************************
|
||||
* When a traits(compiles) is used on a function literal call
|
||||
* we need to take into account if the body of the function
|
||||
* violates any attributes, however, we must not affect the
|
||||
* attribute inference on the outer function. The attributes
|
||||
* of the function literal still need to be inferred, therefore
|
||||
* we need a way to check for the scope that the traits compiles
|
||||
* introduces.
|
||||
*
|
||||
* Params:
|
||||
* sc = scope to be checked for
|
||||
*
|
||||
* Returns: `true` if the provided scope is the root
|
||||
* of the traits compiles list of scopes.
|
||||
*/
|
||||
bool isRootTraitsCompilesScope(Scope* sc)
|
||||
{
|
||||
return (sc.flags & SCOPE.compile) && !(sc.func.flags & SCOPE.compile);
|
||||
}
|
||||
|
||||
/**************************************
|
||||
* A statement / expression in this scope is not `@safe`,
|
||||
* so mark the enclosing function as `@system`
|
||||
*
|
||||
* Params:
|
||||
* sc = scope that the unsafe statement / expression is in
|
||||
* gag = surpress error message (used in escape.d)
|
||||
* loc = location of error
|
||||
* fmt = printf-style format string
|
||||
* arg0 = (optional) argument for first %s format specifier
|
||||
* arg1 = (optional) argument for second %s format specifier
|
||||
* arg2 = (optional) argument for third %s format specifier
|
||||
* Returns: whether there's a safe error
|
||||
*/
|
||||
bool setUnsafe(Scope* sc,
|
||||
bool gag = false, Loc loc = Loc.init, const(char)* fmt = null,
|
||||
RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null)
|
||||
{
|
||||
if (sc.intypeof)
|
||||
return false; // typeof(cast(int*)0) is safe
|
||||
|
||||
if (sc.flags & SCOPE.debug_) // debug {} scopes are permissive
|
||||
return false;
|
||||
|
||||
if (!sc.func)
|
||||
{
|
||||
if (sc.varDecl)
|
||||
{
|
||||
if (sc.varDecl.storage_class & STC.safe)
|
||||
{
|
||||
.error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : "");
|
||||
return true;
|
||||
}
|
||||
else if (!(sc.varDecl.storage_class & STC.trusted))
|
||||
{
|
||||
sc.varDecl.storage_class |= STC.system;
|
||||
sc.varDecl.systemInferred = true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (isRootTraitsCompilesScope(sc)) // __traits(compiles, x)
|
||||
{
|
||||
if (sc.func.isSafeBypassingInference())
|
||||
{
|
||||
// Message wil be gagged, but still call error() to update global.errors and for
|
||||
// -verrors=spec
|
||||
.error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : "");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return sc.func.setUnsafe(gag, loc, fmt, arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
/***************************************
|
||||
* Like `setUnsafe`, but for safety errors still behind preview switches
|
||||
*
|
||||
* Given a `FeatureState fs`, for example dip1000 / dip25 / systemVariables,
|
||||
* the behavior changes based on the setting:
|
||||
*
|
||||
* - In case of `-revert=fs`, it does nothing.
|
||||
* - In case of `-preview=fs`, it's the same as `setUnsafe`
|
||||
* - By default, print a deprecation in `@safe` functions, or store an attribute violation in inferred functions.
|
||||
*
|
||||
* Params:
|
||||
* sc = used to find affected function/variable, and for checking whether we are in a deprecated / speculative scope
|
||||
* fs = feature state from the preview flag
|
||||
* gag = surpress error message
|
||||
* loc = location of error
|
||||
* msg = printf-style format string
|
||||
* arg0 = (optional) argument for first %s format specifier
|
||||
* arg1 = (optional) argument for second %s format specifier
|
||||
* arg2 = (optional) argument for third %s format specifier
|
||||
* Returns: whether an actual safe error (not deprecation) occured
|
||||
*/
|
||||
bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char)* msg,
|
||||
RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null)
|
||||
{
|
||||
//printf("setUnsafePreview() fs:%d %s\n", fs, msg);
|
||||
with (FeatureState) final switch (fs)
|
||||
{
|
||||
case disabled:
|
||||
return false;
|
||||
|
||||
case enabled:
|
||||
return sc.setUnsafe(gag, loc, msg, arg0, arg1, arg2);
|
||||
|
||||
case default_:
|
||||
if (!sc.func)
|
||||
return false;
|
||||
if (sc.func.isSafeBypassingInference())
|
||||
{
|
||||
if (!gag && !sc.isDeprecated())
|
||||
{
|
||||
deprecation(loc, msg, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : "");
|
||||
}
|
||||
}
|
||||
else if (!sc.func.safetyViolation)
|
||||
{
|
||||
import dmd.func : AttributeViolation;
|
||||
sc.func.safetyViolation = new AttributeViolation(loc, msg, arg0, arg1, arg2);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores a reason why a function failed to infer a function attribute like `@safe` or `pure`
|
||||
///
|
||||
/// Has two modes:
|
||||
|
|
|
@ -1891,6 +1891,309 @@ Expression addInvariant(AggregateDeclaration ad, VarDeclaration vthis)
|
|||
return e;
|
||||
}
|
||||
|
||||
/********************************************
|
||||
* Find function in overload list that exactly matches t.
|
||||
*/
|
||||
FuncDeclaration overloadExactMatch(FuncDeclaration thisfd, Type t)
|
||||
{
|
||||
FuncDeclaration fd;
|
||||
overloadApply(thisfd, (Dsymbol s)
|
||||
{
|
||||
auto f = s.isFuncDeclaration();
|
||||
if (!f)
|
||||
return 0;
|
||||
if (f.storage_class & STC.disable)
|
||||
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) == Covariant.yes &&
|
||||
tf.nextOf().implicitConvTo(t.nextOf()) >= MATCH.constant)
|
||||
{
|
||||
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.
|
||||
*/
|
||||
FuncDeclaration overloadModMatch(FuncDeclaration thisfd, const ref Loc loc, Type tthis, ref bool hasOverloads)
|
||||
{
|
||||
//printf("FuncDeclaration::overloadModMatch('%s')\n", toChars());
|
||||
MatchAccumulator m;
|
||||
overloadApply(thisfd, (Dsymbol s)
|
||||
{
|
||||
auto f = s.isFuncDeclaration();
|
||||
if (!f || f == m.lastf) // skip duplicates
|
||||
return 0;
|
||||
auto tf = f.type.toTypeFunction();
|
||||
//printf("tf = %s\n", tf.toChars());
|
||||
MATCH match;
|
||||
if (tthis) // non-static functions are preferred than static ones
|
||||
{
|
||||
if (f.needThis())
|
||||
match = f.isCtorDeclaration() ? MATCH.exact : MODmethodConv(tthis.mod, tf.mod);
|
||||
else
|
||||
match = MATCH.constant; // keep static function in overload candidates
|
||||
}
|
||||
else // static functions are preferred than non-static ones
|
||||
{
|
||||
if (f.needThis())
|
||||
match = MATCH.convert;
|
||||
else
|
||||
match = MATCH.exact;
|
||||
}
|
||||
if (match == MATCH.nomatch)
|
||||
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;
|
||||
//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 <= MATCH.convert)
|
||||
{
|
||||
// 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 = thisfd.type.toTypeFunction();
|
||||
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", thisfd.kind, thisfd.toPrettyChars,
|
||||
funcBuf.peekChars(), thisfd.toPrettyChars(), thisBuf.peekChars());
|
||||
}
|
||||
}
|
||||
return m.lastf;
|
||||
}
|
||||
|
||||
/***********************************
|
||||
* Determine lexical level difference from `fd` to nested function `target`.
|
||||
* Issue error if `fd` cannot call `target`.
|
||||
*
|
||||
* Params:
|
||||
* fd = function
|
||||
* loc = location for error messages
|
||||
* sc = context
|
||||
* target = target of call
|
||||
* decl = The `Declaration` that triggered this check.
|
||||
* Used to provide a better error message only.
|
||||
* Returns:
|
||||
* 0 same level
|
||||
* >0 decrease nesting by number
|
||||
* -1 increase nesting by 1 (`target` is nested within 'fd')
|
||||
* LevelError error
|
||||
*/
|
||||
int getLevelAndCheck(FuncDeclaration fd, const ref Loc loc, Scope* sc, FuncDeclaration target,
|
||||
Declaration decl)
|
||||
{
|
||||
int level = fd.getLevel(target, sc.intypeof);
|
||||
if (level != fd.LevelError)
|
||||
return level;
|
||||
// Don't give error if in template constraint
|
||||
if (!(sc.flags & SCOPE.constraint))
|
||||
{
|
||||
const(char)* xstatic = fd.isStatic() ? "`static` " : "";
|
||||
// better diagnostics for static functions
|
||||
.error(loc, "%s%s `%s` cannot access %s `%s` in frame of function `%s`",
|
||||
xstatic, fd.kind(), fd.toPrettyChars(), decl.kind(), decl.toChars(),
|
||||
target.toPrettyChars());
|
||||
.errorSupplemental(decl.loc, "`%s` declared here", decl.toChars());
|
||||
return fd.LevelError;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**********************************
|
||||
* Decide if attributes for this function can be inferred from examining
|
||||
* the function body.
|
||||
* Returns:
|
||||
* true if can
|
||||
*/
|
||||
bool canInferAttributes(FuncDeclaration fd, Scope* sc)
|
||||
{
|
||||
if (!fd.fbody)
|
||||
return false;
|
||||
if (fd.isVirtualMethod() &&
|
||||
/*
|
||||
* https://issues.dlang.org/show_bug.cgi?id=21719
|
||||
*
|
||||
* If we have an auto virtual function we can infer
|
||||
* the attributes.
|
||||
*/
|
||||
!(fd.inferRetType && !fd.isCtorDeclaration()))
|
||||
return false; // since they may be overridden
|
||||
if (sc.func &&
|
||||
/********** this is for backwards compatibility for the moment ********/
|
||||
(!fd.isMember() || sc.func.isSafeBypassingInference() && !fd.isInstantiated()))
|
||||
return true;
|
||||
if (fd.isFuncLiteralDeclaration() || // externs are not possible with literals
|
||||
(fd.storage_class & STC.inference) || // do attribute inference
|
||||
(fd.inferRetType && !fd.isCtorDeclaration()))
|
||||
return true;
|
||||
if (fd.isInstantiated())
|
||||
{
|
||||
auto ti = fd.parent.isTemplateInstance();
|
||||
if (ti is null || ti.isTemplateMixin() || ti.tempdecl.ident == fd.ident)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
* 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.
|
||||
*/
|
||||
bool checkNestedReference(FuncDeclaration fd, Scope* sc, const ref Loc loc)
|
||||
{
|
||||
//printf("FuncDeclaration::checkNestedReference() %s\n", toPrettyChars());
|
||||
if (auto fld = fd.isFuncLiteralDeclaration())
|
||||
{
|
||||
if (fld.tok == TOK.reserved)
|
||||
{
|
||||
fld.tok = TOK.function_;
|
||||
fld.vthis = null;
|
||||
}
|
||||
}
|
||||
if (!fd.parent || fd.parent == sc.parent)
|
||||
return false;
|
||||
if (fd.ident == Id.require || fd.ident == Id.ensure)
|
||||
return false;
|
||||
if (!fd.isThis() && !fd.isNested())
|
||||
return false;
|
||||
// The current function
|
||||
FuncDeclaration fdthis = sc.parent.isFuncDeclaration();
|
||||
if (!fdthis)
|
||||
return false; // out of function scope
|
||||
Dsymbol p = fd.toParentLocal();
|
||||
Dsymbol p2 = fd.toParent2();
|
||||
// Function literals from fdthis to p must be delegates
|
||||
ensureStaticLinkTo(fdthis, p);
|
||||
if (p != p2)
|
||||
ensureStaticLinkTo(fdthis, p2);
|
||||
if (fd.isNested())
|
||||
{
|
||||
// The function that this function is in
|
||||
bool checkEnclosing(FuncDeclaration fdv)
|
||||
{
|
||||
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 != fd)
|
||||
{
|
||||
bool found = false;
|
||||
for (size_t i = 0; i < fd.siblingCallers.length; ++i)
|
||||
{
|
||||
if (fd.siblingCallers[i] == fdthis)
|
||||
found = true;
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
//printf("\tadding sibling %s to %s\n", fdthis.toPrettyChars(), toPrettyChars());
|
||||
if (!sc.intypeof && !(sc.flags & SCOPE.compile))
|
||||
{
|
||||
fd.siblingCallers.push(fdthis);
|
||||
fd.computedEscapingSiblings = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
const lv = fdthis.getLevelAndCheck(loc, sc, fdv, fd);
|
||||
if (lv == fd.LevelError)
|
||||
return true; // error
|
||||
if (lv == -1)
|
||||
return false; // downlevel call
|
||||
if (lv == 0)
|
||||
return false; // same level call
|
||||
return false; // Uplevel call
|
||||
}
|
||||
if (checkEnclosing(p.isFuncDeclaration()))
|
||||
return true;
|
||||
if (checkEnclosing(p == p2 ? null : p2.isFuncDeclaration()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/****************************************************
|
||||
* Check whether result variable can be built.
|
||||
* Returns:
|
||||
* `true` if the function has a return type that
|
||||
* is different from `void`.
|
||||
*/
|
||||
private bool canBuildResultVar(FuncDeclaration fd)
|
||||
{
|
||||
auto f = cast(TypeFunction)fd.type;
|
||||
return f && f.nextOf() && f.nextOf().toBasetype().ty != Tvoid;
|
||||
}
|
||||
|
||||
/****************************************************
|
||||
* Declare result variable lazily.
|
||||
*/
|
||||
|
@ -1923,3 +2226,512 @@ void buildResultVar(FuncDeclaration fd, Scope* sc, Type tret)
|
|||
assert(fd.vresult.parent == fd);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************
|
||||
* 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.
|
||||
*/
|
||||
Statement mergeFrequire(FuncDeclaration fd, Statement sf, Expressions* params)
|
||||
{
|
||||
/* 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.
|
||||
*/
|
||||
foreach (fdv; fd.foverrides)
|
||||
{
|
||||
/* The semantic pass on the contracts of the overridden functions must
|
||||
* be completed before code generation occurs.
|
||||
* https://issues.dlang.org/show_bug.cgi?id=3602
|
||||
*/
|
||||
if (fdv.frequires && fdv.semanticRun != PASS.semantic3done)
|
||||
{
|
||||
assert(fdv._scope);
|
||||
Scope* sc = fdv._scope.push();
|
||||
sc.stc &= ~STC.override_;
|
||||
fdv.semantic3(sc);
|
||||
sc.pop();
|
||||
}
|
||||
sf = fdv.mergeFrequire(sf, params);
|
||||
if (!sf || !fdv.fdrequire)
|
||||
return null;
|
||||
//printf("fdv.frequire: %s\n", fdv.frequire.toChars());
|
||||
/* Make the call:
|
||||
* try { __require(params); }
|
||||
* catch (Throwable) { frequire; }
|
||||
*/
|
||||
params = Expression.arraySyntaxCopy(params);
|
||||
Expression e = new CallExp(fd.loc, new VarExp(fd.loc, fdv.fdrequire, false), params);
|
||||
Statement s2 = new ExpStatement(fd.loc, e);
|
||||
auto c = new Catch(fd.loc, getThrowable(), null, sf);
|
||||
c.internalCatch = true;
|
||||
auto catches = new Catches();
|
||||
catches.push(c);
|
||||
sf = new TryCatchStatement(fd.loc, s2, catches);
|
||||
}
|
||||
return sf;
|
||||
}
|
||||
|
||||
/****************************************************
|
||||
* Merge into this function the 'in' contracts of all it overrides.
|
||||
*/
|
||||
Statement mergeFrequireInclusivePreview(FuncDeclaration fd, Statement sf, Expressions* params)
|
||||
{
|
||||
/* If a base function and its override both have an IN contract, then
|
||||
* the override in contract must widen the guarantee of the base contract.
|
||||
* This is checked by generating:
|
||||
*
|
||||
* void derived.in() {
|
||||
* try {
|
||||
* ... body of derived.in() ...
|
||||
* }
|
||||
* catch () {
|
||||
* // derived in rejected this argument. so parent must also reject it, or we've tightened the contract.
|
||||
* base.in();
|
||||
* assert(false, "Logic error: " ~ thr.msg);
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
foreach (fdv; fd.foverrides)
|
||||
{
|
||||
/* The semantic pass on the contracts of the overridden functions must
|
||||
* be completed before code generation occurs.
|
||||
* https://issues.dlang.org/show_bug.cgi?id=3602
|
||||
*/
|
||||
if (fdv.frequires && fdv.semanticRun != PASS.semantic3done)
|
||||
{
|
||||
assert(fdv._scope);
|
||||
Scope* sc = fdv._scope.push();
|
||||
sc.stc &= ~STC.override_;
|
||||
fdv.semantic3(sc);
|
||||
sc.pop();
|
||||
}
|
||||
sf = fdv.mergeFrequireInclusivePreview(sf, params);
|
||||
if (sf && fdv.fdrequire)
|
||||
{
|
||||
const loc = fd.fdrequire.loc;
|
||||
//printf("fdv.frequire: %s\n", fdv.frequire.toChars());
|
||||
/* Make the call:
|
||||
* try { frequire; }
|
||||
* catch (Throwable thr) { __require(params); assert(false, "Logic error: " ~ thr.msg); }
|
||||
*/
|
||||
Identifier id = Identifier.generateId("thr");
|
||||
params = Expression.arraySyntaxCopy(params);
|
||||
Expression e = new CallExp(loc, new VarExp(loc, fdv.fdrequire, false), params);
|
||||
Statement s2 = new ExpStatement(loc, e);
|
||||
// assert(false, ...)
|
||||
// TODO make this a runtime helper to allow:
|
||||
// - chaining the original expression
|
||||
// - nogc concatenation
|
||||
Expression msg = new StringExp(loc, "Logic error: in-contract was tighter than parent in-contract");
|
||||
Statement fail = new ExpStatement(loc, new AssertExp(loc, IntegerExp.literal!0, msg));
|
||||
Statement s3 = new CompoundStatement(loc, s2, fail);
|
||||
auto c = new Catch(loc, getThrowable(), id, s3);
|
||||
c.internalCatch = true;
|
||||
auto catches = new Catches();
|
||||
catches.push(c);
|
||||
sf = new TryCatchStatement(loc, sf, catches);
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
return sf;
|
||||
}
|
||||
|
||||
/****************************************************
|
||||
* Rewrite contracts as statements.
|
||||
*/
|
||||
void buildEnsureRequire(FuncDeclaration thisfd)
|
||||
{
|
||||
if (thisfd.frequires)
|
||||
{
|
||||
/* in { statements1... }
|
||||
* in { statements2... }
|
||||
* ...
|
||||
* becomes:
|
||||
* in { { statements1... } { statements2... } ... }
|
||||
*/
|
||||
assert(thisfd.frequires.length);
|
||||
auto loc = (*thisfd.frequires)[0].loc;
|
||||
auto s = new Statements;
|
||||
foreach (r; *thisfd.frequires)
|
||||
{
|
||||
s.push(new ScopeStatement(r.loc, r, r.loc));
|
||||
}
|
||||
thisfd.frequire = new CompoundStatement(loc, s);
|
||||
}
|
||||
if (thisfd.fensures)
|
||||
{
|
||||
/* out(id1) { statements1... }
|
||||
* out(id2) { statements2... }
|
||||
* ...
|
||||
* becomes:
|
||||
* out(__result) { { ref id1 = __result; { statements1... } }
|
||||
* { ref id2 = __result; { statements2... } } ... }
|
||||
*/
|
||||
assert(thisfd.fensures.length);
|
||||
auto loc = (*thisfd.fensures)[0].ensure.loc;
|
||||
auto s = new Statements;
|
||||
foreach (r; *thisfd.fensures)
|
||||
{
|
||||
if (r.id && thisfd.canBuildResultVar())
|
||||
{
|
||||
auto rloc = r.ensure.loc;
|
||||
auto resultId = new IdentifierExp(rloc, Id.result);
|
||||
auto init = new ExpInitializer(rloc, resultId);
|
||||
auto stc = STC.ref_ | STC.temp | STC.result;
|
||||
auto decl = new VarDeclaration(rloc, null, r.id, init, stc);
|
||||
auto sdecl = new ExpStatement(rloc, decl);
|
||||
s.push(new ScopeStatement(rloc, new CompoundStatement(rloc, sdecl, r.ensure), rloc));
|
||||
}
|
||||
else
|
||||
{
|
||||
s.push(r.ensure);
|
||||
}
|
||||
}
|
||||
thisfd.fensure = new CompoundStatement(loc, s);
|
||||
}
|
||||
if (!thisfd.isVirtual())
|
||||
return;
|
||||
/* Rewrite contracts as nested functions, then call them. Doing it as nested
|
||||
* functions means that overriding functions can call them.
|
||||
*/
|
||||
TypeFunction f = cast(TypeFunction) thisfd.type;
|
||||
/* Make a copy of the parameters and make them all ref */
|
||||
static Parameters* toRefCopy(ParameterList parameterList)
|
||||
{
|
||||
auto result = new Parameters();
|
||||
foreach (n, p; parameterList)
|
||||
{
|
||||
p = p.syntaxCopy();
|
||||
if (!p.isLazy())
|
||||
p.storageClass = (p.storageClass | STC.ref_) & ~STC.out_;
|
||||
p.defaultArg = null; // won't be the same with ref
|
||||
result.push(p);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
if (thisfd.frequire)
|
||||
{
|
||||
/* in { ... }
|
||||
* becomes:
|
||||
* void __require(ref params) { ... }
|
||||
* __require(params);
|
||||
*/
|
||||
Loc loc = thisfd.frequire.loc;
|
||||
thisfd.fdrequireParams = new Expressions();
|
||||
if (thisfd.parameters)
|
||||
{
|
||||
foreach (vd; *thisfd.parameters)
|
||||
thisfd.fdrequireParams.push(new VarExp(loc, vd));
|
||||
}
|
||||
auto fo = cast(TypeFunction)(thisfd.originalType ? thisfd.originalType : f);
|
||||
auto fparams = toRefCopy(fo.parameterList);
|
||||
auto tf = new TypeFunction(ParameterList(fparams), Type.tvoid, LINK.d);
|
||||
tf.isnothrow = f.isnothrow;
|
||||
tf.isnogc = f.isnogc;
|
||||
tf.purity = f.purity;
|
||||
tf.trust = f.trust;
|
||||
auto fd = new FuncDeclaration(loc, loc, Id.require, STC.undefined_, tf);
|
||||
fd.fbody = thisfd.frequire;
|
||||
Statement s1 = new ExpStatement(loc, fd);
|
||||
Expression e = new CallExp(loc, new VarExp(loc, fd, false), thisfd.fdrequireParams);
|
||||
Statement s2 = new ExpStatement(loc, e);
|
||||
thisfd.frequire = new CompoundStatement(loc, s1, s2);
|
||||
thisfd.fdrequire = fd;
|
||||
}
|
||||
/* We need to set fdensureParams here and not in the block below to
|
||||
* have the parameters available when calling a base class ensure(),
|
||||
* even if this function doesn't have an out contract.
|
||||
*/
|
||||
thisfd.fdensureParams = new Expressions();
|
||||
if (thisfd.canBuildResultVar())
|
||||
thisfd.fdensureParams.push(new IdentifierExp(thisfd.loc, Id.result));
|
||||
if (thisfd.parameters)
|
||||
{
|
||||
foreach (vd; *thisfd.parameters)
|
||||
thisfd.fdensureParams.push(new VarExp(thisfd.loc, vd));
|
||||
}
|
||||
if (thisfd.fensure)
|
||||
{
|
||||
/* out (result) { ... }
|
||||
* becomes:
|
||||
* void __ensure(ref tret result, ref params) { ... }
|
||||
* __ensure(result, params);
|
||||
*/
|
||||
Loc loc = thisfd.fensure.loc;
|
||||
auto fparams = new Parameters();
|
||||
if (thisfd.canBuildResultVar())
|
||||
{
|
||||
Parameter p = new Parameter(loc, STC.ref_ | STC.const_, f.nextOf(), Id.result, null, null);
|
||||
fparams.push(p);
|
||||
}
|
||||
auto fo = cast(TypeFunction)(thisfd.originalType ? thisfd.originalType : f);
|
||||
fparams.pushSlice((*toRefCopy(fo.parameterList))[]);
|
||||
auto tf = new TypeFunction(ParameterList(fparams), Type.tvoid, LINK.d);
|
||||
tf.isnothrow = f.isnothrow;
|
||||
tf.isnogc = f.isnogc;
|
||||
tf.purity = f.purity;
|
||||
tf.trust = f.trust;
|
||||
auto fd = new FuncDeclaration(loc, loc, Id.ensure, STC.undefined_, tf);
|
||||
fd.fbody = thisfd.fensure;
|
||||
Statement s1 = new ExpStatement(loc, fd);
|
||||
Expression e = new CallExp(loc, new VarExp(loc, fd, false), thisfd.fdensureParams);
|
||||
Statement s2 = new ExpStatement(loc, e);
|
||||
thisfd.fensure = new CompoundStatement(loc, s1, s2);
|
||||
thisfd.fdensure = fd;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************
|
||||
* 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.
|
||||
*/
|
||||
Statement mergeFensure(FuncDeclaration fd, Statement sf, Identifier oid, Expressions* params)
|
||||
{
|
||||
/* 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.
|
||||
*/
|
||||
foreach (fdv; fd.foverrides)
|
||||
{
|
||||
/* The semantic pass on the contracts of the overridden functions must
|
||||
* be completed before code generation occurs.
|
||||
* https://issues.dlang.org/show_bug.cgi?id=3602 and
|
||||
* https://issues.dlang.org/show_bug.cgi?id=5230
|
||||
*/
|
||||
if (fd.needsFensure(fdv) && fdv.semanticRun != PASS.semantic3done)
|
||||
{
|
||||
assert(fdv._scope);
|
||||
Scope* sc = fdv._scope.push();
|
||||
sc.stc &= ~STC.override_;
|
||||
fdv.semantic3(sc);
|
||||
sc.pop();
|
||||
}
|
||||
sf = fdv.mergeFensure(sf, oid, params);
|
||||
if (fdv.fdensure)
|
||||
{
|
||||
//printf("fdv.fensure: %s\n", fdv.fensure.toChars());
|
||||
// Make the call: __ensure(result, params)
|
||||
params = Expression.arraySyntaxCopy(params);
|
||||
if (fd.canBuildResultVar())
|
||||
{
|
||||
Type t1 = fdv.type.nextOf().toBasetype();
|
||||
Type t2 = fd.type.nextOf().toBasetype();
|
||||
if (t1.isBaseOf(t2, null))
|
||||
{
|
||||
/* Making temporary reference variable is necessary
|
||||
* in covariant return.
|
||||
* https://issues.dlang.org/show_bug.cgi?id=5204
|
||||
* https://issues.dlang.org/show_bug.cgi?id=10479
|
||||
*/
|
||||
Expression* eresult = &(*params)[0];
|
||||
auto ei = new ExpInitializer(Loc.initial, *eresult);
|
||||
auto v = new VarDeclaration(Loc.initial, t1, Identifier.generateId("__covres"), ei);
|
||||
v.storage_class |= STC.temp;
|
||||
auto de = new DeclarationExp(Loc.initial, v);
|
||||
auto ve = new VarExp(Loc.initial, v);
|
||||
*eresult = new CommaExp(Loc.initial, de, ve);
|
||||
}
|
||||
}
|
||||
Expression e = new CallExp(fd.loc, new VarExp(fd.loc, fdv.fdensure, false), params);
|
||||
Statement s2 = new ExpStatement(fd.loc, e);
|
||||
if (sf)
|
||||
{
|
||||
sf = new CompoundStatement(sf.loc, s2, sf);
|
||||
}
|
||||
else
|
||||
sf = s2;
|
||||
}
|
||||
}
|
||||
return sf;
|
||||
}
|
||||
|
||||
/*******************************
|
||||
* 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(FuncLiteralDeclaration fld, Scope* sc, Type tret)
|
||||
{
|
||||
import dmd.statement_rewrite_walker;
|
||||
extern (C++) final class RetWalker : StatementRewriteWalker
|
||||
{
|
||||
alias visit = typeof(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.implicitCastTo(sc, tret);
|
||||
}
|
||||
}
|
||||
if (fld.semanticRun < PASS.semantic3done)
|
||||
return;
|
||||
if (fld.fes)
|
||||
return;
|
||||
scope RetWalker w = new RetWalker();
|
||||
w.sc = sc;
|
||||
w.tret = tret;
|
||||
w.fld = fld;
|
||||
fld.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 (fld.inferRetType && fld.type.nextOf() != tret)
|
||||
fld.type.toTypeFunction().next = tret;
|
||||
}
|
||||
|
||||
/**************************************
|
||||
* When a traits(compiles) is used on a function literal call
|
||||
* we need to take into account if the body of the function
|
||||
* violates any attributes, however, we must not affect the
|
||||
* attribute inference on the outer function. The attributes
|
||||
* of the function literal still need to be inferred, therefore
|
||||
* we need a way to check for the scope that the traits compiles
|
||||
* introduces.
|
||||
*
|
||||
* Params:
|
||||
* sc = scope to be checked for
|
||||
*
|
||||
* Returns: `true` if the provided scope is the root
|
||||
* of the traits compiles list of scopes.
|
||||
*/
|
||||
bool isRootTraitsCompilesScope(Scope* sc)
|
||||
{
|
||||
return (sc.flags & SCOPE.compile) && !(sc.func.flags & SCOPE.compile);
|
||||
}
|
||||
|
||||
/**************************************
|
||||
* A statement / expression in this scope is not `@safe`,
|
||||
* so mark the enclosing function as `@system`
|
||||
*
|
||||
* Params:
|
||||
* sc = scope that the unsafe statement / expression is in
|
||||
* gag = surpress error message (used in escape.d)
|
||||
* loc = location of error
|
||||
* fmt = printf-style format string
|
||||
* arg0 = (optional) argument for first %s format specifier
|
||||
* arg1 = (optional) argument for second %s format specifier
|
||||
* arg2 = (optional) argument for third %s format specifier
|
||||
* Returns: whether there's a safe error
|
||||
*/
|
||||
bool setUnsafe(Scope* sc,
|
||||
bool gag = false, Loc loc = Loc.init, const(char)* fmt = null,
|
||||
RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null)
|
||||
{
|
||||
if (sc.intypeof)
|
||||
return false; // typeof(cast(int*)0) is safe
|
||||
|
||||
if (sc.flags & SCOPE.debug_) // debug {} scopes are permissive
|
||||
return false;
|
||||
|
||||
if (!sc.func)
|
||||
{
|
||||
if (sc.varDecl)
|
||||
{
|
||||
if (sc.varDecl.storage_class & STC.safe)
|
||||
{
|
||||
.error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : "");
|
||||
return true;
|
||||
}
|
||||
else if (!(sc.varDecl.storage_class & STC.trusted))
|
||||
{
|
||||
sc.varDecl.storage_class |= STC.system;
|
||||
sc.varDecl.systemInferred = true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (isRootTraitsCompilesScope(sc)) // __traits(compiles, x)
|
||||
{
|
||||
if (sc.func.isSafeBypassingInference())
|
||||
{
|
||||
// Message wil be gagged, but still call error() to update global.errors and for
|
||||
// -verrors=spec
|
||||
.error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : "");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return sc.func.setUnsafe(gag, loc, fmt, arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
/***************************************
|
||||
* Like `setUnsafe`, but for safety errors still behind preview switches
|
||||
*
|
||||
* Given a `FeatureState fs`, for example dip1000 / dip25 / systemVariables,
|
||||
* the behavior changes based on the setting:
|
||||
*
|
||||
* - In case of `-revert=fs`, it does nothing.
|
||||
* - In case of `-preview=fs`, it's the same as `setUnsafe`
|
||||
* - By default, print a deprecation in `@safe` functions, or store an attribute violation in inferred functions.
|
||||
*
|
||||
* Params:
|
||||
* sc = used to find affected function/variable, and for checking whether we are in a deprecated / speculative scope
|
||||
* fs = feature state from the preview flag
|
||||
* gag = surpress error message
|
||||
* loc = location of error
|
||||
* msg = printf-style format string
|
||||
* arg0 = (optional) argument for first %s format specifier
|
||||
* arg1 = (optional) argument for second %s format specifier
|
||||
* arg2 = (optional) argument for third %s format specifier
|
||||
* Returns: whether an actual safe error (not deprecation) occured
|
||||
*/
|
||||
bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char)* msg,
|
||||
RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null)
|
||||
{
|
||||
//printf("setUnsafePreview() fs:%d %s\n", fs, msg);
|
||||
with (FeatureState) final switch (fs)
|
||||
{
|
||||
case disabled:
|
||||
return false;
|
||||
|
||||
case enabled:
|
||||
return sc.setUnsafe(gag, loc, msg, arg0, arg1, arg2);
|
||||
|
||||
case default_:
|
||||
if (!sc.func)
|
||||
return false;
|
||||
if (sc.func.isSafeBypassingInference())
|
||||
{
|
||||
if (!gag && !sc.isDeprecated())
|
||||
{
|
||||
deprecation(loc, msg, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : "");
|
||||
}
|
||||
}
|
||||
else if (!sc.func.safetyViolation)
|
||||
{
|
||||
import dmd.func : AttributeViolation;
|
||||
sc.func.safetyViolation = new AttributeViolation(loc, msg, arg0, arg1, arg2);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import dmd.errors;
|
|||
import dmd.expression;
|
||||
import dmd.expressionsem;
|
||||
import dmd.func;
|
||||
import dmd.funcsem;
|
||||
import dmd.globals;
|
||||
import dmd.hdrgen;
|
||||
import dmd.id;
|
||||
|
|
|
@ -27,7 +27,7 @@ import dmd.mtype;
|
|||
import dmd.target;
|
||||
import dmd.tokens;
|
||||
import dmd.typesem : hasPointers, arrayOf;
|
||||
import dmd.func : setUnsafe, setUnsafePreview;
|
||||
import dmd.funcsem : setUnsafe, setUnsafePreview;
|
||||
|
||||
/*************************************************************
|
||||
* Check for unsafe access in @safe code:
|
||||
|
|
|
@ -40,6 +40,7 @@ import dmd.escape;
|
|||
import dmd.expression;
|
||||
import dmd.expressionsem;
|
||||
import dmd.func;
|
||||
import dmd.funcsem;
|
||||
import dmd.globals;
|
||||
import dmd.id;
|
||||
import dmd.identifier;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue