@@ -1,4 +1,4 @@
-643b1261bba0757d97efa3ff1f63e461271eb000
+65a3da148c0c700a6c928f0e13799b2a7d34fcbe
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.
@@ -172,7 +172,7 @@ Expression arrayOp(BinAssignExp e, Scope* sc)
}
if (e.e1.op == EXP.arrayLiteral)
{
- return e.e1.modifiableLvalue(sc, e.e1);
+ return e.e1.modifiableLvalue(sc);
}
return arrayOp(e.isBinExp(), sc);
@@ -22,6 +22,7 @@ import dmd.declaration;
import dmd.dsymbol;
import dmd.errorsink;
import dmd.expression;
+import dmd.expressionsem;
import dmd.func;
import dmd.globals;
import dmd.init;
@@ -80,7 +81,7 @@ CT canThrow(Expression e, FuncDeclaration func, ErrorSink eSink)
if (!f.isDtorDeclaration())
errorSupplementalInferredAttr(f, 10, false, STC.nothrow_);
- e.checkOverriddenDtor(null, f, dd => dd.type.toTypeFunction().isnothrow, "not nothrow");
+ f.checkOverriddenDtor(null, e.loc, dd => dd.type.toTypeFunction().isnothrow, "not nothrow");
}
else if (func)
{
@@ -12,6 +12,7 @@
module dmd.compiler;
import dmd.arraytypes;
+import dmd.ctfeexpr;
import dmd.dmodule;
import dmd.expression;
import dmd.mtype;
@@ -29,6 +29,7 @@ import dmd.globals;
import dmd.identifier;
import dmd.location;
import dmd.mtype;
+import dmd.optimize;
import dmd.typesem;
import dmd.common.outbuffer;
import dmd.rootobject;
@@ -2023,6 +2023,9 @@ final class CParser(AST) : Parser!AST
}
symbols.push(s);
}
+ if (level == LVL.global && !id)
+ error("expected identifier for declaration");
+
first = false;
switch (token.value)
@@ -2736,7 +2739,7 @@ final class CParser(AST) : Parser!AST
private AST.Type cparseDeclarator(DTR declarator, AST.Type tbase,
out Identifier pident, ref Specifier specifier)
{
- //printf("cparseDeclarator(%d, %p)\n", declarator, t);
+ //printf("cparseDeclarator(%d, %s)\n", declarator, tbase.toChars());
AST.Types constTypes; // all the Types that will need `const` applied to them
/* Insert tx -> t into
@@ -2755,6 +2758,7 @@ final class CParser(AST) : Parser!AST
AST.Type parseDecl(AST.Type t)
{
+ //printf("parseDecl() t: %s\n", t.toChars());
AST.Type ts;
while (1)
{
@@ -2770,9 +2774,18 @@ final class CParser(AST) : Parser!AST
break;
case TOK.leftParenthesis: // ( declarator )
+ //printf("leftParen\n");
/* like: T (*fp)();
* T ((*fp))();
*/
+ auto tk = &token;
+ if (!isCDeclarator(tk, declarator))
+ {
+ /* Not ( declarator ), might be parameter-list
+ */
+ ts = t;
+ break;
+ }
nextToken();
if (token.value == TOK.__stdcall) // T (__stdcall*fp)();
@@ -2786,6 +2799,7 @@ final class CParser(AST) : Parser!AST
break;
case TOK.mul: // pointer
+ //printf("star\n");
t = new AST.TypePointer(t);
nextToken();
// add post fixes const/volatile/restrict/_Atomic
@@ -2797,6 +2811,7 @@ final class CParser(AST) : Parser!AST
continue;
default:
+ //printf("default %s\n", token.toChars());
if (declarator == DTR.xdirect)
{
if (!t || t.isTypeIdentifier())
@@ -2914,7 +2929,7 @@ final class CParser(AST) : Parser!AST
if (specifier._pure)
stc |= STC.pure_;
AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, stc);
- // tf = tf.addSTC(storageClass); // TODO
+ //tf = tf.addSTC(storageClass); // TODO
insertTx(ts, tf, t); // ts -> ... -> tf -> t
if (ts != tf)
@@ -2927,6 +2942,8 @@ final class CParser(AST) : Parser!AST
}
break;
}
+ if (declarator == DTR.xdirect && !pident)
+ error("expected identifier for declarator");
return ts;
}
@@ -4556,6 +4573,7 @@ final class CParser(AST) : Parser!AST
*/
private bool isCDeclarator(ref Token* pt, DTR declarator)
{
+ //printf("isCDeclarator()\n");
auto t = pt;
while (1)
{
@@ -4578,6 +4596,8 @@ final class CParser(AST) : Parser!AST
else if (t.value == TOK.leftParenthesis)
{
t = peek(t);
+ if (t.value == TOK.__stdcall)
+ t = peek(t);
if (!isCDeclarator(t, declarator))
return false;
if (t.value != TOK.rightParenthesis)
@@ -35,75 +35,98 @@ import dmd.root.rmem;
import dmd.tokens;
import dmd.visitor;
-
-/***********************************************************
- * A reference to a class, or an interface. We need this when we
- * point to a base class (we must record what the type is).
+/****************************************************************/
+/* A type meant as a union of all the Expression types,
+ * to serve essentially as a Variant that will sit on the stack
+ * during CTFE to reduce memory consumption.
*/
-extern (C++) final class ClassReferenceExp : Expression
+extern (D) struct UnionExp
{
- StructLiteralExp value;
-
- extern (D) this(const ref Loc loc, StructLiteralExp lit, Type type) @safe
+ // yes, default constructor does nothing
+ extern (D) this(Expression e)
{
- super(loc, EXP.classReference);
- assert(lit && lit.sd && lit.sd.isClassDeclaration());
- this.value = lit;
- this.type = type;
+ memcpy(&this, cast(void*)e, e.size);
}
- ClassDeclaration originalClass()
+ /* Extract pointer to Expression
+ */
+ extern (D) Expression exp() return
{
- return value.sd.isClassDeclaration();
+ return cast(Expression)&u;
}
- // Return index of the field, or -1 if not found
- private int getFieldIndex(Type fieldtype, uint fieldoffset)
+ /* Convert to an allocated Expression
+ */
+ extern (D) Expression copy()
{
- ClassDeclaration cd = originalClass();
- uint fieldsSoFar = 0;
- for (size_t j = 0; j < value.elements.length; j++)
+ Expression e = exp();
+ //if (e.size > sizeof(u)) printf("%s\n", EXPtoString(e.op).ptr);
+ assert(e.size <= u.sizeof);
+ switch (e.op)
{
- while (j - fieldsSoFar >= cd.fields.length)
- {
- fieldsSoFar += cd.fields.length;
- cd = cd.baseClass;
- }
- VarDeclaration v2 = cd.fields[j - fieldsSoFar];
- if (fieldoffset == v2.offset && fieldtype.size() == v2.type.size())
- {
- return cast(int)(value.elements.length - fieldsSoFar - cd.fields.length + (j - fieldsSoFar));
- }
+ case EXP.cantExpression: return CTFEExp.cantexp;
+ case EXP.voidExpression: return CTFEExp.voidexp;
+ case EXP.break_: return CTFEExp.breakexp;
+ case EXP.continue_: return CTFEExp.continueexp;
+ case EXP.goto_: return CTFEExp.gotoexp;
+ default: return e.copy();
}
- return -1;
}
- // Return index of the field, or -1 if not found
- // Same as getFieldIndex, but checks for a direct match with the VarDeclaration
- int findFieldIndexByName(VarDeclaration v)
- {
- ClassDeclaration cd = originalClass();
- size_t fieldsSoFar = 0;
- for (size_t j = 0; j < value.elements.length; j++)
- {
- while (j - fieldsSoFar >= cd.fields.length)
- {
- fieldsSoFar += cd.fields.length;
- cd = cd.baseClass;
- }
- VarDeclaration v2 = cd.fields[j - fieldsSoFar];
- if (v == v2)
- {
- return cast(int)(value.elements.length - fieldsSoFar - cd.fields.length + (j - fieldsSoFar));
- }
- }
- return -1;
- }
+private:
+ // Ensure that the union is suitably aligned.
+ align(8) union _AnonStruct_u
+ {
+ char[__traits(classInstanceSize, Expression)] exp;
+ char[__traits(classInstanceSize, IntegerExp)] integerexp;
+ char[__traits(classInstanceSize, ErrorExp)] errorexp;
+ char[__traits(classInstanceSize, RealExp)] realexp;
+ char[__traits(classInstanceSize, ComplexExp)] complexexp;
+ char[__traits(classInstanceSize, SymOffExp)] symoffexp;
+ char[__traits(classInstanceSize, StringExp)] stringexp;
+ char[__traits(classInstanceSize, ArrayLiteralExp)] arrayliteralexp;
+ char[__traits(classInstanceSize, AssocArrayLiteralExp)] assocarrayliteralexp;
+ char[__traits(classInstanceSize, StructLiteralExp)] structliteralexp;
+ char[__traits(classInstanceSize, CompoundLiteralExp)] compoundliteralexp;
+ char[__traits(classInstanceSize, NullExp)] nullexp;
+ char[__traits(classInstanceSize, DotVarExp)] dotvarexp;
+ char[__traits(classInstanceSize, AddrExp)] addrexp;
+ char[__traits(classInstanceSize, IndexExp)] indexexp;
+ char[__traits(classInstanceSize, SliceExp)] sliceexp;
+ char[__traits(classInstanceSize, VectorExp)] vectorexp;
+ }
+
+ _AnonStruct_u u;
+}
- override void accept(Visitor v)
- {
- v.visit(this);
- }
+void emplaceExp(T : Expression, Args...)(void* p, Args args)
+{
+ static if (__VERSION__ < 2099)
+ const init = typeid(T).initializer;
+ else
+ const init = __traits(initSymbol, T);
+ p[0 .. __traits(classInstanceSize, T)] = init[];
+ (cast(T)p).__ctor(args);
+}
+
+void emplaceExp(T : UnionExp)(T* p, Expression e)
+{
+ memcpy(p, cast(void*)e, e.size);
+}
+
+// Generate an error message when this exception is not caught
+void generateUncaughtError(ThrownExceptionExp tee)
+{
+ UnionExp ue = void;
+ Expression e = resolveSlice((*tee.thrown.value.elements)[0], &ue);
+ StringExp se = e.toStringExp();
+ error(tee.thrown.loc, "uncaught CTFE exception `%s(%s)`", tee.thrown.type.toChars(), se ? se.toChars() : e.toChars());
+ /* Also give the line where the throw statement was. We won't have it
+ * in the case where the ThrowStatement is generated internally
+ * (eg, in ScopeStatement)
+ */
+ if (tee.loc.isValid() && !tee.loc.equals(tee.thrown.loc))
+ .errorSupplemental(tee.loc, "thrown from here");
}
/*************************
@@ -121,100 +144,6 @@ int findFieldIndexByName(const StructDeclaration sd, const VarDeclaration v) pur
return -1;
}
-/***********************************************************
- * Fake class which holds the thrown exception.
- * Used for implementing exception handling.
- */
-extern (C++) final class ThrownExceptionExp : Expression
-{
- ClassReferenceExp thrown; // the thing being tossed
-
- extern (D) this(const ref Loc loc, ClassReferenceExp victim) @safe
- {
- super(loc, EXP.thrownException);
- this.thrown = victim;
- this.type = victim.type;
- }
-
- override const(char)* toChars() const
- {
- return "CTFE ThrownException";
- }
-
- // Generate an error message when this exception is not caught
- extern (D) void generateUncaughtError()
- {
- UnionExp ue = void;
- Expression e = resolveSlice((*thrown.value.elements)[0], &ue);
- StringExp se = e.toStringExp();
- error(thrown.loc, "uncaught CTFE exception `%s(%s)`", thrown.type.toChars(), se ? se.toChars() : e.toChars());
- /* Also give the line where the throw statement was. We won't have it
- * in the case where the ThrowStatement is generated internally
- * (eg, in ScopeStatement)
- */
- if (loc.isValid() && !loc.equals(thrown.loc))
- .errorSupplemental(loc, "thrown from here");
- }
-
- override void accept(Visitor v)
- {
- v.visit(this);
- }
-}
-
-/***********************************************************
- * This type is only used by the interpreter.
- */
-extern (C++) final class CTFEExp : Expression
-{
- extern (D) this(EXP tok)
- {
- super(Loc.initial, tok);
- type = Type.tvoid;
- }
-
- override const(char)* toChars() const
- {
- switch (op)
- {
- case EXP.cantExpression:
- return "<cant>";
- case EXP.voidExpression:
- return "cast(void)0";
- case EXP.showCtfeContext:
- return "<error>";
- case EXP.break_:
- return "<break>";
- case EXP.continue_:
- return "<continue>";
- case EXP.goto_:
- return "<goto>";
- default:
- assert(0);
- }
- }
-
- extern (D) __gshared CTFEExp cantexp;
- extern (D) __gshared CTFEExp voidexp;
- extern (D) __gshared CTFEExp breakexp;
- extern (D) __gshared CTFEExp continueexp;
- extern (D) __gshared CTFEExp gotoexp;
- /* Used when additional information is needed regarding
- * a ctfe error.
- */
- extern (D) __gshared CTFEExp showcontext;
-
- extern (D) static bool isCantExp(const Expression e) @safe
- {
- return e && e.op == EXP.cantExpression;
- }
-
- extern (D) static bool isGotoExp(const Expression e) @safe
- {
- return e && e.op == EXP.goto_;
- }
-}
-
// True if 'e' is CTFEExp::cantexp, or an exception
bool exceptionOrCantInterpret(const Expression e) @safe
{
@@ -38,6 +38,7 @@ import dmd.init;
import dmd.intrange;
import dmd.mtype;
import dmd.opover;
+import dmd.optimize;
import dmd.root.ctfloat;
import dmd.common.outbuffer;
import dmd.root.rmem;
@@ -152,6 +152,7 @@ import dmd.identifier;
import dmd.mtype;
import dmd.root.ctfloat;
import dmd.common.outbuffer;
+import dmd.optimize;
import dmd.root.aav;
import dmd.root.string;
import dmd.root.stringtable;
@@ -793,7 +793,7 @@ extern (C++) final class Module : Package
}
else
{
- const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput;
+ const bool doUnittests = global.params.parsingUnittestsRequired();
scope p = new Parser!AST(this, buf, cast(bool) docfile, global.errorSink, &global.compileEnv, doUnittests);
p.transitionIn = global.params.v.vin;
p.nextToken();
@@ -57,6 +57,7 @@ import dmd.nogc;
import dmd.nspace;
import dmd.objc;
import dmd.opover;
+import dmd.optimize;
import dmd.parse;
import dmd.root.array;
import dmd.root.filename;
@@ -1951,7 +1952,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
const len = buf.length;
buf.writeByte(0);
const str = buf.extractSlice()[0 .. len];
- const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput;
+ const bool doUnittests = global.params.parsingUnittestsRequired();
auto loc = adjustLocForMixin(str, cd.loc, global.params.mixinOut);
scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
p.transitionIn = global.params.v.vin;
@@ -3384,9 +3385,13 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
if (!tf.isNaked() && !(funcdecl.isThis() || funcdecl.isNested()))
{
- OutBuffer buf;
- MODtoBuffer(buf, tf.mod);
- .error(funcdecl.loc, "%s `%s` without `this` cannot be `%s`", funcdecl.kind, funcdecl.toPrettyChars, buf.peekChars());
+ import core.bitop;
+ auto mods = MODtoChars(tf.mod);
+ .error(funcdecl.loc, "%s `%s` without `this` cannot be `%s`", funcdecl.kind, funcdecl.toPrettyChars, mods);
+ if (tf.next && tf.next.ty != Tvoid && popcnt(tf.mod) == 1)
+ .errorSupplemental(funcdecl.loc,
+ "did you mean to use `%s(%s)` as the return type?", mods, tf.next.toChars());
+
tf.mod = 0; // remove qualifiers
}
@@ -69,6 +69,7 @@ import dmd.initsem;
import dmd.location;
import dmd.mtype;
import dmd.opover;
+import dmd.optimize;
import dmd.root.array;
import dmd.common.outbuffer;
import dmd.rootobject;
@@ -18,25 +18,19 @@ import core.stdc.stdio;
import core.stdc.string;
import dmd.aggregate;
-import dmd.aliasthis;
-import dmd.arrayop;
import dmd.arraytypes;
import dmd.astenums;
import dmd.ast_node;
import dmd.gluelayer;
-import dmd.ctfeexpr;
-import dmd.ctorflow;
import dmd.dclass;
import dmd.declaration;
import dmd.dimport;
import dmd.dmodule;
-import dmd.dscope;
import dmd.dstruct;
import dmd.dsymbol;
import dmd.dtemplate;
import dmd.errors;
import dmd.errorsink;
-import dmd.expressionsem;
import dmd.func;
import dmd.globals;
import dmd.hdrgen;
@@ -45,39 +39,20 @@ import dmd.identifier;
import dmd.init;
import dmd.location;
import dmd.mtype;
-import dmd.opover;
-import dmd.optimize;
import dmd.root.complex;
import dmd.root.ctfloat;
-import dmd.root.filename;
import dmd.common.outbuffer;
import dmd.root.optional;
import dmd.root.rmem;
import dmd.rootobject;
import dmd.root.string;
import dmd.root.utf;
-import dmd.safe;
import dmd.target;
import dmd.tokens;
import dmd.visitor;
enum LOGSEMANTIC = false;
-void emplaceExp(T : Expression, Args...)(void* p, Args args)
-{
- static if (__VERSION__ < 2099)
- const init = typeid(T).initializer;
- else
- const init = __traits(initSymbol, T);
- p[0 .. __traits(classInstanceSize, T)] = init[];
- (cast(T)p).__ctor(args);
-}
-
-void emplaceExp(T : UnionExp)(T* p, Expression e)
-{
- memcpy(p, cast(void*)e, e.size);
-}
-
/// Return value for `checkModifiable`
enum Modifiable
{
@@ -118,45 +93,6 @@ inout(Expression) lastComma(inout Expression e)
}
-/***********************************
- * Determine if a `this` is needed to access `d`.
- * Params:
- * sc = context
- * d = declaration to check
- * Returns:
- * true means a `this` is needed
- */
-bool isNeedThisScope(Scope* sc, Declaration d)
-{
- if (sc.intypeof == 1)
- return false;
-
- AggregateDeclaration ad = d.isThis();
- if (!ad)
- return false;
- //printf("d = %s, ad = %s\n", d.toChars(), ad.toChars());
-
- for (Dsymbol s = sc.parent; s; s = s.toParentLocal())
- {
- //printf("\ts = %s %s, toParent2() = %p\n", s.kind(), s.toChars(), s.toParent2());
- if (AggregateDeclaration ad2 = s.isAggregateDeclaration())
- {
- if (ad2 == ad)
- return false;
- else if (ad2.isNested())
- continue;
- else
- return true;
- }
- if (FuncDeclaration f = s.isFuncDeclaration())
- {
- if (f.isMemberLocal())
- break;
- }
- }
- return true;
-}
-
/****************************************
* Expand tuples in-place.
*
@@ -315,70 +251,6 @@ TemplateDeclaration getFuncTemplateDecl(Dsymbol s) @safe
return null;
}
-/****************************************************************/
-/* A type meant as a union of all the Expression types,
- * to serve essentially as a Variant that will sit on the stack
- * during CTFE to reduce memory consumption.
- */
-extern (D) struct UnionExp
-{
- // yes, default constructor does nothing
- extern (D) this(Expression e)
- {
- memcpy(&this, cast(void*)e, e.size);
- }
-
- /* Extract pointer to Expression
- */
- extern (D) Expression exp() return
- {
- return cast(Expression)&u;
- }
-
- /* Convert to an allocated Expression
- */
- extern (D) Expression copy()
- {
- Expression e = exp();
- //if (e.size > sizeof(u)) printf("%s\n", EXPtoString(e.op).ptr);
- assert(e.size <= u.sizeof);
- switch (e.op)
- {
- case EXP.cantExpression: return CTFEExp.cantexp;
- case EXP.voidExpression: return CTFEExp.voidexp;
- case EXP.break_: return CTFEExp.breakexp;
- case EXP.continue_: return CTFEExp.continueexp;
- case EXP.goto_: return CTFEExp.gotoexp;
- default: return e.copy();
- }
- }
-
-private:
- // Ensure that the union is suitably aligned.
- align(8) union _AnonStruct_u
- {
- char[__traits(classInstanceSize, Expression)] exp;
- char[__traits(classInstanceSize, IntegerExp)] integerexp;
- char[__traits(classInstanceSize, ErrorExp)] errorexp;
- char[__traits(classInstanceSize, RealExp)] realexp;
- char[__traits(classInstanceSize, ComplexExp)] complexexp;
- char[__traits(classInstanceSize, SymOffExp)] symoffexp;
- char[__traits(classInstanceSize, StringExp)] stringexp;
- char[__traits(classInstanceSize, ArrayLiteralExp)] arrayliteralexp;
- char[__traits(classInstanceSize, AssocArrayLiteralExp)] assocarrayliteralexp;
- char[__traits(classInstanceSize, StructLiteralExp)] structliteralexp;
- char[__traits(classInstanceSize, CompoundLiteralExp)] compoundliteralexp;
- char[__traits(classInstanceSize, NullExp)] nullexp;
- char[__traits(classInstanceSize, DotVarExp)] dotvarexp;
- char[__traits(classInstanceSize, AddrExp)] addrexp;
- char[__traits(classInstanceSize, IndexExp)] indexexp;
- char[__traits(classInstanceSize, SliceExp)] sliceexp;
- char[__traits(classInstanceSize, VectorExp)] vectorexp;
- }
-
- _AnonStruct_u u;
-}
-
/************************ TypeDotIdExp ************************************/
/* Things like:
* int.size
@@ -678,71 +550,6 @@ extern (C++) abstract class Expression : ASTNode
return false;
}
- /*******************************
- * Give error if we're not an lvalue.
- * If we can, convert expression to be an lvalue.
- */
- Expression toLvalue(Scope* sc, Expression e)
- {
- if (!e)
- e = this;
- else if (!loc.isValid())
- loc = e.loc;
-
- if (e.op == EXP.type)
- error(loc, "`%s` is a `%s` definition and cannot be modified", e.type.toChars(), e.type.kind());
- else
- error(loc, "`%s` is not an lvalue and cannot be modified", e.toChars());
-
- return ErrorExp.get();
- }
-
- Expression modifiableLvalue(Scope* sc, Expression e)
- {
- //printf("Expression::modifiableLvalue() %s, type = %s\n", toChars(), type.toChars());
- // See if this expression is a modifiable lvalue (i.e. not const)
- if (checkModifiable(this, sc) == Modifiable.yes)
- {
- assert(type);
- if (!type.isMutable())
- {
- if (auto dve = this.isDotVarExp())
- {
- if (isNeedThisScope(sc, dve.var))
- for (Dsymbol s = sc.func; s; s = s.toParentLocal())
- {
- FuncDeclaration ff = s.isFuncDeclaration();
- if (!ff)
- break;
- if (!ff.type.isMutable)
- {
- error(loc, "cannot modify `%s` in `%s` function", toChars(), MODtoChars(type.mod));
- return ErrorExp.get();
- }
- }
- }
- error(loc, "cannot modify `%s` expression `%s`", MODtoChars(type.mod), toChars());
- return ErrorExp.get();
- }
- else if (!type.isAssignable())
- {
- error(loc, "cannot modify struct instance `%s` of type `%s` because it contains `const` or `immutable` members",
- toChars(), type.toChars());
- return ErrorExp.get();
- }
- }
- return toLvalue(sc, e);
- }
-
- /****************************************
- * Resolve __FILE__, __LINE__, __MODULE__, __FUNCTION__, __PRETTY_FUNCTION__, __FILE_FULL_PATH__ to loc.
- */
- Expression resolveLoc(const ref Loc loc, Scope* sc)
- {
- this.loc = loc;
- return this;
- }
-
/****************************************
* Check that the expression has a valid type.
* If not, generates an error "... has no type".
@@ -835,417 +642,6 @@ extern (C++) abstract class Expression : ASTNode
return checkValue();
}
- extern (D) final bool checkDeprecated(Scope* sc, Dsymbol s)
- {
- return s.checkDeprecated(loc, sc);
- }
-
- extern (D) final bool checkDisabled(Scope* sc, Dsymbol s)
- {
- if (auto d = s.isDeclaration())
- {
- return d.checkDisabled(loc, sc);
- }
-
- return false;
- }
-
- /*********************************************
- * Calling function f.
- * Check the purity, i.e. if we're in a pure function
- * we can only call other pure functions.
- * Returns true if error occurs.
- */
- extern (D) final bool checkPurity(Scope* sc, FuncDeclaration f)
- {
- if (!sc.func)
- return false;
- if (sc.func == f)
- return false;
- if (sc.intypeof == 1)
- return false;
- if (sc.flags & (SCOPE.ctfe | SCOPE.debug_))
- return false;
-
- // If the call has a pure parent, then the called func must be pure.
- if (!f.isPure() && checkImpure(sc, loc, null, f))
- {
- error(loc, "`pure` %s `%s` cannot call impure %s `%s`",
- sc.func.kind(), sc.func.toPrettyChars(), f.kind(),
- f.toPrettyChars());
-
- if (!f.isDtorDeclaration())
- errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.pure_);
-
- checkOverriddenDtor(sc, f, dd => dd.type.toTypeFunction().purity != PURE.impure, "impure");
- return true;
- }
- return false;
- }
-
- /**
- * Checks whether `f` is a generated `DtorDeclaration` that hides a user-defined one
- * which passes `check` while `f` doesn't (e.g. when the user defined dtor is pure but
- * the generated dtor is not).
- * In that case the method will identify and print all members causing the attribute
- * missmatch.
- *
- * Params:
- * sc = scope
- * f = potential `DtorDeclaration`
- * check = current check (e.g. whether it's pure)
- * checkName = the kind of check (e.g. `"pure"`)
- */
- extern (D) final void checkOverriddenDtor(Scope* sc, FuncDeclaration f,
- scope bool function(DtorDeclaration) check, const string checkName
- ) {
- auto dd = f.isDtorDeclaration();
- if (!dd || !dd.isGenerated())
- return;
-
- // DtorDeclaration without parents should fail at an earlier stage
- auto ad = cast(AggregateDeclaration) f.toParent2();
- assert(ad);
-
- if (ad.userDtors.length)
- {
- if (!check(ad.userDtors[0])) // doesn't match check (e.g. is impure as well)
- return;
-
- // Sanity check
- assert(!check(ad.fieldDtor));
- }
-
- dd.loc.errorSupplemental("%s`%s.~this` is %.*s because of the following field's destructors:",
- dd.isGenerated() ? "generated " : "".ptr,
- ad.toChars,
- cast(int) checkName.length, checkName.ptr);
-
- // Search for the offending fields
- foreach (field; ad.fields)
- {
- // Only structs may define automatically called destructors
- auto ts = field.type.isTypeStruct();
- if (!ts)
- {
- // But they might be part of a static array
- auto ta = field.type.isTypeSArray();
- if (!ta)
- continue;
-
- ts = ta.baseElemOf().isTypeStruct();
- if (!ts)
- continue;
- }
-
- auto fieldSym = ts.toDsymbol(sc);
- assert(fieldSym); // Resolving ts must succeed because missing defs. should error before
-
- auto fieldSd = fieldSym.isStructDeclaration();
- assert(fieldSd); // ts is a TypeStruct, this would imply a malformed ASR
-
- if (fieldSd.dtor && !check(fieldSd.dtor))
- {
- field.loc.errorSupplemental(" - %s %s", field.type.toChars(), field.toChars());
-
- if (fieldSd.dtor.isGenerated())
- checkOverriddenDtor(sc, fieldSd.dtor, check, checkName);
- else
- fieldSd.dtor.loc.errorSupplemental(" %.*s `%s.~this` is declared here",
- cast(int) checkName.length, checkName.ptr, fieldSd.toChars());
- }
- }
- }
-
- /*******************************************
- * Accessing variable v.
- * Check for purity and safety violations.
- * Returns true if error occurs.
- */
- extern (D) final bool checkPurity(Scope* sc, VarDeclaration v)
- {
- //printf("v = %s %s\n", v.type.toChars(), v.toChars());
- /* Look for purity and safety violations when accessing variable v
- * from current function.
- */
- if (!sc.func)
- return false;
- if (sc.intypeof == 1)
- return false; // allow violations inside typeof(expression)
- if (sc.flags & (SCOPE.ctfe | SCOPE.debug_))
- return false; // allow violations inside compile-time evaluated expressions and debug conditionals
- if (v.ident == Id.ctfe)
- return false; // magic variable never violates pure and safe
- if (v.isImmutable())
- return false; // always safe and pure to access immutables...
- if (v.isConst() && !v.isReference() && (v.isDataseg() || v.isParameter()) && v.type.implicitConvTo(v.type.immutableOf()))
- return false; // or const global/parameter values which have no mutable indirections
- if (v.storage_class & STC.manifest)
- return false; // ...or manifest constants
-
- // accessing empty structs is pure
- // https://issues.dlang.org/show_bug.cgi?id=18694
- // https://issues.dlang.org/show_bug.cgi?id=21464
- // https://issues.dlang.org/show_bug.cgi?id=23589
- if (v.type.ty == Tstruct)
- {
- StructDeclaration sd = (cast(TypeStruct)v.type).sym;
- if (sd.members) // not opaque
- {
- if (sd.semanticRun >= PASS.semanticdone)
- sd.determineSize(v.loc);
- if (sd.hasNoFields)
- return false;
- }
- }
-
- bool err = false;
- if (v.isDataseg())
- {
- // https://issues.dlang.org/show_bug.cgi?id=7533
- // Accessing implicit generated __gate is pure.
- if (v.ident == Id.gate)
- return false;
-
- if (checkImpure(sc, loc, "`pure` %s `%s` cannot access mutable static data `%s`", v))
- {
- error(loc, "`pure` %s `%s` cannot access mutable static data `%s`",
- sc.func.kind(), sc.func.toPrettyChars(), v.toChars());
- err = true;
- }
- }
- else
- {
- /* Given:
- * void f() {
- * int fx;
- * pure void g() {
- * int gx;
- * /+pure+/ void h() {
- * int hx;
- * /+pure+/ void i() { }
- * }
- * }
- * }
- * i() can modify hx and gx but not fx
- */
-
- Dsymbol vparent = v.toParent2();
- for (Dsymbol s = sc.func; !err && s; s = s.toParentP(vparent))
- {
- if (s == vparent)
- break;
-
- if (AggregateDeclaration ad = s.isAggregateDeclaration())
- {
- if (ad.isNested())
- continue;
- break;
- }
- FuncDeclaration ff = s.isFuncDeclaration();
- if (!ff)
- break;
- if (ff.isNested() || ff.isThis())
- {
- if (ff.type.isImmutable() ||
- ff.type.isShared() && !MODimplicitConv(ff.type.mod, v.type.mod))
- {
- OutBuffer ffbuf;
- OutBuffer vbuf;
- MODMatchToBuffer(&ffbuf, ff.type.mod, v.type.mod);
- MODMatchToBuffer(&vbuf, v.type.mod, ff.type.mod);
- error(loc, "%s%s `%s` cannot access %sdata `%s`",
- ffbuf.peekChars(), ff.kind(), ff.toPrettyChars(), vbuf.peekChars(), v.toChars());
- err = true;
- break;
- }
- continue;
- }
- break;
- }
- }
-
- /* Do not allow safe functions to access __gshared data
- */
- if (v.storage_class & STC.gshared)
- {
- if (sc.setUnsafe(false, this.loc,
- "`@safe` function `%s` cannot access `__gshared` data `%s`", sc.func, v))
- {
- err = true;
- }
- }
-
- return err;
- }
-
- /*
- Check if sc.func is impure or can be made impure.
- Returns true on error, i.e. if sc.func is pure and cannot be made impure.
- */
- private static bool checkImpure(Scope* sc, Loc loc, const(char)* fmt, RootObject arg0)
- {
- return sc.func && (isRootTraitsCompilesScope(sc)
- ? sc.func.isPureBypassingInference() >= PURE.weak
- : sc.func.setImpure(loc, fmt, arg0));
- }
-
- /*********************************************
- * Calling function f.
- * Check the safety, i.e. if we're in a @safe function
- * we can only call @safe or @trusted functions.
- * Returns true if error occurs.
- */
- extern (D) final bool checkSafety(Scope* sc, FuncDeclaration f)
- {
- if (sc.func == f)
- return false;
- if (sc.intypeof == 1)
- return false;
- if (sc.flags & SCOPE.debug_)
- return false;
- if ((sc.flags & SCOPE.ctfe) && sc.func)
- return false;
-
- if (!sc.func)
- {
- if (sc.varDecl && !f.safetyInprocess && !f.isSafe() && !f.isTrusted())
- {
- if (sc.varDecl.storage_class & STC.safe)
- {
- error(loc, "`@safe` variable `%s` cannot be initialized by calling `@system` function `%s`",
- sc.varDecl.toChars(), f.toChars());
- return true;
- }
- else
- {
- sc.varDecl.storage_class |= STC.system;
- sc.varDecl.systemInferred = true;
- }
- }
- return false;
- }
-
- if (!f.isSafe() && !f.isTrusted())
- {
- if (isRootTraitsCompilesScope(sc) ? sc.func.isSafeBypassingInference() : sc.func.setUnsafeCall(f))
- {
- if (!loc.isValid()) // e.g. implicitly generated dtor
- loc = sc.func.loc;
-
- const prettyChars = f.toPrettyChars();
- error(loc, "`@safe` %s `%s` cannot call `@system` %s `%s`",
- sc.func.kind(), sc.func.toPrettyChars(), f.kind(),
- prettyChars);
- if (!f.isDtorDeclaration)
- errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.safe);
- .errorSupplemental(f.loc, "`%s` is declared here", prettyChars);
-
- checkOverriddenDtor(sc, f, dd => dd.type.toTypeFunction().trust > TRUST.system, "@system");
-
- return true;
- }
- }
- else if (f.isSafe() && f.safetyViolation)
- {
- // for dip1000 by default transition, print deprecations for calling functions that will become `@system`
- if (sc.func.isSafeBypassingInference())
- {
- .deprecation(this.loc, "`@safe` function `%s` calling `%s`", sc.func.toChars(), f.toChars());
- errorSupplementalInferredAttr(f, 10, true, STC.safe);
- }
- else if (!sc.func.safetyViolation)
- {
- import dmd.func : AttributeViolation;
- sc.func.safetyViolation = new AttributeViolation(this.loc, null, f, null, null);
- }
- }
- return false;
- }
-
- /*********************************************
- * Calling function f.
- * Check the @nogc-ness, i.e. if we're in a @nogc function
- * we can only call other @nogc functions.
- * Returns true if error occurs.
- */
- extern (D) final bool checkNogc(Scope* sc, FuncDeclaration f)
- {
- if (!sc.func)
- return false;
- if (sc.func == f)
- return false;
- if (sc.intypeof == 1)
- return false;
- if (sc.flags & (SCOPE.ctfe | SCOPE.debug_))
- return false;
- /* The original expressions (`new S(...)` or `new S[...]``) will be
- * verified instead. This is to keep errors related to the original code
- * and not the lowering.
- */
- if (f.ident == Id._d_newitemT || f.ident == Id._d_newarrayT)
- return false;
-
- if (!f.isNogc())
- {
- if (isRootTraitsCompilesScope(sc) ? sc.func.isNogcBypassingInference() : sc.func.setGCCall(f))
- {
- if (loc.linnum == 0) // e.g. implicitly generated dtor
- loc = sc.func.loc;
-
- // Lowered non-@nogc'd hooks will print their own error message inside of nogc.d (NOGCVisitor.visit(CallExp e)),
- // so don't print anything to avoid double error messages.
- if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT
- || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX
- || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT))
- {
- error(loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`",
- sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars());
-
- if (!f.isDtorDeclaration)
- f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc);
- }
-
- checkOverriddenDtor(sc, f, dd => dd.type.toTypeFunction().isnogc, "non-@nogc");
-
- return true;
- }
- }
- return false;
- }
-
- /********************************************
- * Check that the postblit is callable if t is an array of structs.
- * Returns true if error happens.
- */
- extern (D) final bool checkPostblit(Scope* sc, Type t)
- {
- if (auto ts = t.baseElemOf().isTypeStruct())
- {
- if (global.params.useTypeInfo && Type.dtypeinfo)
- {
- // https://issues.dlang.org/show_bug.cgi?id=11395
- // Require TypeInfo generation for array concatenation
- semanticTypeInfo(sc, t);
- }
-
- StructDeclaration sd = ts.sym;
- if (sd.postblit)
- {
- if (sd.postblit.checkDisabled(loc, sc))
- return true;
-
- //checkDeprecated(sc, sd.postblit); // necessary?
- checkPurity(sc, sd.postblit);
- checkSafety(sc, sd.postblit);
- checkNogc(sc, sd.postblit);
- //checkAccess(sd, loc, sc, sd.postblit); // necessary?
- return false;
- }
- }
- return false;
- }
-
/*******************************
* Check whether the expression allows RMW operations, error with rmw operator diagnostic if not.
* ex is the RHS expression, or NULL if ++/-- is used (for diagnostics)
@@ -1308,11 +704,6 @@ extern (C++) abstract class Expression : ASTNode
return this;
}
- final Expression optimize(int result, bool keepLvalue = false)
- {
- return Expression_optimize(this, result, keepLvalue);
- }
-
final int isConst()
{
//printf("Expression::isConst(): %s\n", e.toChars());
@@ -1587,16 +978,6 @@ extern (C++) final class IntegerExp : Expression
return typeof(return)(r);
}
- override Expression toLvalue(Scope* sc, Expression e)
- {
- if (!e)
- e = this;
- else if (!loc.isValid())
- loc = e.loc;
- error(e.loc, "cannot modify constant `%s`", e.toChars());
- return ErrorExp.get();
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -1722,7 +1103,7 @@ extern (C++) final class IntegerExp : Expression
*/
extern (C++) final class ErrorExp : Expression
{
- private extern (D) this()
+ extern (D) this()
{
super(Loc.initial, EXP.error);
type = Type.terror;
@@ -1745,11 +1126,6 @@ extern (C++) final class ErrorExp : Expression
return errorexp;
}
- override Expression toLvalue(Scope* sc, Expression e)
- {
- return this;
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -1987,11 +1363,6 @@ extern (C++) class IdentifierExp : Expression
return true;
}
- override final Expression toLvalue(Scope* sc, Expression e)
- {
- return this;
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -2036,11 +1407,6 @@ extern (C++) final class DsymbolExp : Expression
return true;
}
- override Expression toLvalue(Scope* sc, Expression e)
- {
- return this;
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -2087,16 +1453,6 @@ extern (C++) class ThisExp : Expression
return type.toBasetype().ty != Tclass;
}
- override final Expression toLvalue(Scope* sc, Expression e)
- {
- if (type.toBasetype().ty == Tclass)
- {
- // Class `this` is an rvalue; struct `this` is an lvalue.
- return Expression.toLvalue(sc, e);
- }
- return this;
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -2464,18 +1820,6 @@ extern (C++) final class StringExp : Expression
return (type && type.toBasetype().ty == Tsarray);
}
- override Expression toLvalue(Scope* sc, Expression e)
- {
- //printf("StringExp::toLvalue(%s) type = %s\n", toChars(), type ? type.toChars() : NULL);
- return (type && type.toBasetype().ty == Tsarray) ? this : Expression.toLvalue(sc, e);
- }
-
- override Expression modifiableLvalue(Scope* sc, Expression e)
- {
- error(loc, "cannot modify string literal `%s`", toChars());
- return ErrorExp.get();
- }
-
/********************************
* Convert string contents to a 0 terminated string,
* allocated by mem.xmalloc().
@@ -3050,14 +2394,6 @@ extern (C++) final class StructLiteralExp : Expression
return -1;
}
- override Expression toLvalue(Scope* sc, Expression e)
- {
- if (sc.flags & SCOPE.Cfile)
- return this; // C struct literals are lvalues
- else
- return Expression.toLvalue(sc, e);
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -3203,15 +2539,6 @@ extern (C++) final class TemplateExp : Expression
return fd !is null;
}
- override Expression toLvalue(Scope* sc, Expression e)
- {
- if (!fd)
- return Expression.toLvalue(sc, e);
-
- assert(sc);
- return symbolToExp(fd, loc, sc, true);
- }
-
override bool checkType()
{
error(loc, "%s `%s` has no type", td.kind(), toChars());
@@ -3409,43 +2736,6 @@ extern (C++) final class VarExp : SymbolExp
return true;
}
- override Expression toLvalue(Scope* sc, Expression e)
- {
- if (var.storage_class & STC.manifest)
- {
- error(loc, "manifest constant `%s` cannot be modified", var.toChars());
- return ErrorExp.get();
- }
- if (var.storage_class & STC.lazy_ && !delegateWasExtracted)
- {
- error(loc, "lazy variable `%s` cannot be modified", var.toChars());
- return ErrorExp.get();
- }
- if (var.ident == Id.ctfe)
- {
- error(loc, "cannot modify compiler-generated variable `__ctfe`");
- return ErrorExp.get();
- }
- if (var.ident == Id.dollar) // https://issues.dlang.org/show_bug.cgi?id=13574
- {
- error(loc, "cannot modify operator `$`");
- return ErrorExp.get();
- }
- return this;
- }
-
- override Expression modifiableLvalue(Scope* sc, Expression e)
- {
- //printf("VarExp::modifiableLvalue('%s')\n", var.toChars());
- if (var.storage_class & STC.manifest)
- {
- error(loc, "cannot modify manifest constant `%s`", toChars());
- return ErrorExp.get();
- }
- // See if this expression is a modifiable lvalue (i.e. not const)
- return Expression.modifiableLvalue(sc, e);
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -3472,11 +2762,6 @@ extern (C++) final class OverExp : Expression
return true;
}
- override Expression toLvalue(Scope* sc, Expression e)
- {
- return this;
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -3522,52 +2807,6 @@ extern (C++) final class FuncExp : Expression
return false;
}
- extern (D) void genIdent(Scope* sc)
- {
- if (fd.ident == Id.empty)
- {
- const(char)[] s;
- if (fd.fes)
- s = "__foreachbody";
- else if (fd.tok == TOK.reserved)
- s = "__lambda";
- else if (fd.tok == TOK.delegate_)
- s = "__dgliteral";
- else
- s = "__funcliteral";
-
- DsymbolTable symtab;
- if (FuncDeclaration func = sc.parent.isFuncDeclaration())
- {
- if (func.localsymtab is null)
- {
- // Inside template constraint, symtab is not set yet.
- // Initialize it lazily.
- func.localsymtab = new DsymbolTable();
- }
- symtab = func.localsymtab;
- }
- else
- {
- ScopeDsymbol sds = sc.parent.isScopeDsymbol();
- if (!sds.symtab)
- {
- // Inside template constraint, symtab may not be set yet.
- // Initialize it lazily.
- assert(sds.isTemplateInstance());
- sds.symtab = new DsymbolTable();
- }
- symtab = sds.symtab;
- }
- assert(symtab);
- Identifier id = Identifier.generateId(s, symtab.length() + 1);
- fd.ident = id;
- if (td)
- td.ident = id;
- symtab.insert(td ? cast(Dsymbol)td : cast(Dsymbol)fd);
- }
- }
-
override FuncExp syntaxCopy()
{
if (td)
@@ -3815,12 +3054,6 @@ extern (C++) abstract class UnaExp : Expression
}
- override final Expression resolveLoc(const ref Loc loc, Scope* sc)
- {
- e1 = e1.resolveLoc(loc, sc);
- return this;
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -3936,18 +3169,6 @@ extern (C++) class BinAssignExp : BinExp
return true;
}
- override final Expression toLvalue(Scope* sc, Expression ex)
- {
- // Lvalue-ness will be handled in glue layer.
- return this;
- }
-
- override final Expression modifiableLvalue(Scope* sc, Expression e)
- {
- // should check e1.checkModifiable() ?
- return toLvalue(sc, this);
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -4134,78 +3355,26 @@ extern (C++) final class DotTemplateExp : UnaExp
*/
extern (C++) final class DotVarExp : UnaExp
{
- Declaration var;
- bool hasOverloads;
-
- extern (D) this(const ref Loc loc, Expression e, Declaration var, bool hasOverloads = true) @safe
- {
- if (var.isVarDeclaration())
- hasOverloads = false;
-
- super(loc, EXP.dotVariable, e);
- //printf("DotVarExp()\n");
- this.var = var;
- this.hasOverloads = hasOverloads;
- }
-
- override bool isLvalue()
- {
- if (e1.op != EXP.structLiteral)
- return true;
- auto vd = var.isVarDeclaration();
- return !(vd && vd.isField());
- }
-
- override Expression toLvalue(Scope* sc, Expression e)
- {
- //printf("DotVarExp::toLvalue(%s)\n", toChars());
- if (sc && sc.flags & SCOPE.Cfile)
- {
- /* C11 6.5.2.3-3: A postfix expression followed by the '.' or '->' operator
- * is an lvalue if the first expression is an lvalue.
- */
- if (!e1.isLvalue())
- return Expression.toLvalue(sc, e);
- }
- if (!isLvalue())
- return Expression.toLvalue(sc, e);
- if (e1.op == EXP.this_ && sc.ctorflow.fieldinit.length && !(sc.ctorflow.callSuper & CSX.any_ctor))
- {
- if (VarDeclaration vd = var.isVarDeclaration())
- {
- auto ad = vd.isMember2();
- if (ad && ad.fields.length == sc.ctorflow.fieldinit.length)
- {
- foreach (i, f; ad.fields)
- {
- if (f == vd)
- {
- if (!(sc.ctorflow.fieldinit[i].csx & CSX.this_ctor))
- {
- /* If the address of vd is taken, assume it is thereby initialized
- * https://issues.dlang.org/show_bug.cgi?id=15869
- */
- modifyFieldVar(loc, sc, vd, e1);
- }
- break;
- }
- }
- }
- }
- }
- return this;
- }
+ Declaration var;
+ bool hasOverloads;
- override Expression modifiableLvalue(Scope* sc, Expression e)
+ extern (D) this(const ref Loc loc, Expression e, Declaration var, bool hasOverloads = true) @safe
{
- version (none)
- {
- printf("DotVarExp::modifiableLvalue(%s)\n", toChars());
- printf("e1.type = %s\n", e1.type.toChars());
- printf("var.type = %s\n", var.type.toChars());
- }
+ if (var.isVarDeclaration())
+ hasOverloads = false;
+
+ super(loc, EXP.dotVariable, e);
+ //printf("DotVarExp()\n");
+ this.var = var;
+ this.hasOverloads = hasOverloads;
+ }
- return Expression.modifiableLvalue(sc, e);
+ override bool isLvalue()
+ {
+ if (e1.op != EXP.structLiteral)
+ return true;
+ auto vd = var.isVarDeclaration();
+ return !(vd && vd.isField());
}
override void accept(Visitor v)
@@ -4239,49 +3408,6 @@ extern (C++) final class DotTemplateInstanceExp : UnaExp
return new DotTemplateInstanceExp(loc, e1.syntaxCopy(), ti.name, TemplateInstance.arraySyntaxCopy(ti.tiargs));
}
- extern (D) bool findTempDecl(Scope* sc)
- {
- static if (LOGSEMANTIC)
- {
- printf("DotTemplateInstanceExp::findTempDecl('%s')\n", toChars());
- }
- if (ti.tempdecl)
- return true;
-
- Expression e = new DotIdExp(loc, e1, ti.name);
- e = e.expressionSemantic(sc);
- if (e.op == EXP.dot)
- e = (cast(DotExp)e).e2;
-
- Dsymbol s = null;
- switch (e.op)
- {
- case EXP.overloadSet:
- s = (cast(OverExp)e).vars;
- break;
-
- case EXP.dotTemplateDeclaration:
- s = (cast(DotTemplateExp)e).td;
- break;
-
- case EXP.scope_:
- s = (cast(ScopeExp)e).sds;
- break;
-
- case EXP.dotVariable:
- s = (cast(DotVarExp)e).var;
- break;
-
- case EXP.variable:
- s = (cast(VarExp)e).var;
- break;
-
- default:
- return false;
- }
- return ti.updateTempDecl(sc, s);
- }
-
override bool checkType()
{
// Same logic as ScopeExp.checkType()
@@ -4486,13 +3612,6 @@ extern (C++) final class CallExp : UnaExp
return false;
}
- override Expression toLvalue(Scope* sc, Expression e)
- {
- if (isLvalue())
- return this;
- return Expression.toLvalue(sc, e);
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -4600,30 +3719,6 @@ extern (C++) final class PtrExp : UnaExp
return true;
}
- override Expression toLvalue(Scope* sc, Expression e)
- {
- return this;
- }
-
- override Expression modifiableLvalue(Scope* sc, Expression e)
- {
- //printf("PtrExp::modifiableLvalue() %s, type %s\n", toChars(), type.toChars());
- Declaration var;
- if (auto se = e1.isSymOffExp())
- var = se.var;
- else if (auto ve = e1.isVarExp())
- var = ve.var;
- if (var && var.type.isFunction_Delegate_PtrToFunction())
- {
- if (var.type.isTypeFunction())
- error(loc, "function `%s` is not an lvalue and cannot be modified", var.toChars());
- else
- error(loc, "function pointed to by `%s` is not an lvalue and cannot be modified", var.toChars());
- return ErrorExp.get();
- }
- return Expression.modifiableLvalue(sc, e);
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -4755,19 +3850,6 @@ extern (C++) final class CastExp : UnaExp
e1.type.mutableOf().unSharedOf().equals(to.mutableOf().unSharedOf());
}
- override Expression toLvalue(Scope* sc, Expression e)
- {
- if (sc && sc.flags & SCOPE.Cfile)
- {
- /* C11 6.5.4-5: A cast does not yield an lvalue.
- */
- return Expression.toLvalue(sc, e);
- }
- if (isLvalue())
- return this;
- return Expression.toLvalue(sc, e);
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -4822,12 +3904,6 @@ extern (C++) final class VectorArrayExp : UnaExp
return e1.isLvalue();
}
- override Expression toLvalue(Scope* sc, Expression e)
- {
- e1 = e1.toLvalue(sc, e);
- return this;
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -4885,18 +3961,6 @@ extern (C++) final class SliceExp : UnaExp
return (type && type.toBasetype().ty == Tsarray);
}
- override Expression toLvalue(Scope* sc, Expression e)
- {
- //printf("SliceExp::toLvalue(%s) type = %s\n", toChars(), type ? type.toChars() : NULL);
- return (type && type.toBasetype().ty == Tsarray) ? this : Expression.toLvalue(sc, e);
- }
-
- override Expression modifiableLvalue(Scope* sc, Expression e)
- {
- error(loc, "slice expression `%s` is not a modifiable lvalue", toChars());
- return this;
- }
-
override Optional!bool toBool()
{
return e1.toBool();
@@ -4964,13 +4028,6 @@ extern (C++) final class ArrayExp : UnaExp
return true;
}
- override Expression toLvalue(Scope* sc, Expression e)
- {
- if (type && type.toBasetype().ty == Tvoid)
- error(loc, "`void`s have no value");
- return this;
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -5018,18 +4075,6 @@ extern (C++) final class CommaExp : BinExp
return e2.isLvalue();
}
- override Expression toLvalue(Scope* sc, Expression e)
- {
- e2 = e2.toLvalue(sc, null);
- return this;
- }
-
- override Expression modifiableLvalue(Scope* sc, Expression e)
- {
- e2 = e2.modifiableLvalue(sc, e);
- return this;
- }
-
override Optional!bool toBool()
{
return e2.toBool();
@@ -5105,21 +4150,6 @@ extern (C++) final class DelegatePtrExp : UnaExp
return e1.isLvalue();
}
- override Expression toLvalue(Scope* sc, Expression e)
- {
- e1 = e1.toLvalue(sc, e);
- return this;
- }
-
- override Expression modifiableLvalue(Scope* sc, Expression e)
- {
- if (sc.setUnsafe(false, this.loc, "cannot modify delegate pointer in `@safe` code `%s`", this))
- {
- return ErrorExp.get();
- }
- return Expression.modifiableLvalue(sc, e);
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -5143,21 +4173,6 @@ extern (C++) final class DelegateFuncptrExp : UnaExp
return e1.isLvalue();
}
- override Expression toLvalue(Scope* sc, Expression e)
- {
- e1 = e1.toLvalue(sc, e);
- return this;
- }
-
- override Expression modifiableLvalue(Scope* sc, Expression e)
- {
- if (sc.setUnsafe(false, this.loc, "cannot modify delegate function pointer in `@safe` code `%s`", this))
- {
- return ErrorExp.get();
- }
- return Expression.modifiableLvalue(sc, e);
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -5205,23 +4220,6 @@ extern (C++) final class IndexExp : BinExp
return true;
}
- override Expression toLvalue(Scope* sc, Expression e)
- {
- if (isLvalue())
- return this;
- return Expression.toLvalue(sc, e);
- }
-
- override Expression modifiableLvalue(Scope* sc, Expression e)
- {
- //printf("IndexExp::modifiableLvalue(%s)\n", toChars());
- Expression ex = markSettingAAElem();
- if (ex.op == EXP.error)
- return ex;
-
- return Expression.modifiableLvalue(sc, e);
- }
-
extern (D) Expression markSettingAAElem()
{
if (e1.type.toBasetype().ty == Taarray)
@@ -5324,20 +4322,6 @@ extern (C++) class AssignExp : BinExp
return true;
}
- override final Expression toLvalue(Scope* sc, Expression ex)
- {
- if (e1.op == EXP.slice || e1.op == EXP.arrayLength)
- {
- return Expression.toLvalue(sc, ex);
- }
-
- /* In front-end level, AssignExp should make an lvalue of e1.
- * Taking the address of e1 will be handled in low level layer,
- * so this function does nothing.
- */
- return this;
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -5733,13 +4717,6 @@ extern (C++) final class CatExp : BinExp
super(loc, EXP.concatenate, e1, e2);
}
- override Expression resolveLoc(const ref Loc loc, Scope* sc)
- {
- e1 = e1.resolveLoc(loc, sc);
- e2 = e2.resolveLoc(loc, sc);
- return this;
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -6074,28 +5051,6 @@ extern (C++) final class CondExp : BinExp
return e1.isLvalue() && e2.isLvalue();
}
- override Expression toLvalue(Scope* sc, Expression ex)
- {
- // convert (econd ? e1 : e2) to *(econd ? &e1 : &e2)
- CondExp e = cast(CondExp)copy();
- e.e1 = e1.toLvalue(sc, null).addressOf();
- e.e2 = e2.toLvalue(sc, null).addressOf();
- e.type = type.pointerTo();
- return new PtrExp(loc, e, type);
- }
-
- override Expression modifiableLvalue(Scope* sc, Expression e)
- {
- if (!e1.isLvalue() && !e2.isLvalue())
- {
- error(loc, "conditional expression `%s` is not a modifiable lvalue", toChars());
- return ErrorExp.get();
- }
- e1 = e1.modifiableLvalue(sc, e1);
- e2 = e2.modifiableLvalue(sc, e2);
- return toLvalue(sc, this);
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -6145,19 +5100,6 @@ extern (C++) final class FileInitExp : DefaultInitExp
super(loc, tok);
}
- override Expression resolveLoc(const ref Loc loc, Scope* sc)
- {
- //printf("FileInitExp::resolve() %s\n", toChars());
- const(char)* s;
- if (op == EXP.fileFullPath)
- s = FileName.toAbsolute(loc.isValid() ? loc.filename : sc._module.srcfile.toChars());
- else
- s = loc.isValid() ? loc.filename : sc._module.ident.toChars();
-
- Expression e = new StringExp(loc, s.toDString());
- return e.expressionSemantic(sc);
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -6174,12 +5116,6 @@ extern (C++) final class LineInitExp : DefaultInitExp
super(loc, EXP.line);
}
- override Expression resolveLoc(const ref Loc loc, Scope* sc)
- {
- Expression e = new IntegerExp(loc, loc.linnum, Type.tint32);
- return e.expressionSemantic(sc);
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -6196,13 +5132,6 @@ extern (C++) final class ModuleInitExp : DefaultInitExp
super(loc, EXP.moduleString);
}
- override Expression resolveLoc(const ref Loc loc, Scope* sc)
- {
- const auto s = (sc.callsc ? sc.callsc : sc)._module.toPrettyChars().toDString();
- Expression e = new StringExp(loc, s);
- return e.expressionSemantic(sc);
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -6219,19 +5148,6 @@ extern (C++) final class FuncInitExp : DefaultInitExp
super(loc, EXP.functionString);
}
- override Expression resolveLoc(const ref Loc loc, Scope* sc)
- {
- const(char)* s;
- if (sc.callsc && sc.callsc.func)
- s = sc.callsc.func.Dsymbol.toPrettyChars();
- else if (sc.func)
- s = sc.func.Dsymbol.toPrettyChars();
- else
- s = "";
- Expression e = new StringExp(loc, s.toDString());
- return e.expressionSemantic(sc);
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -6248,29 +5164,153 @@ extern (C++) final class PrettyFuncInitExp : DefaultInitExp
super(loc, EXP.prettyFunction);
}
- override Expression resolveLoc(const ref Loc loc, Scope* sc)
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * A reference to a class, or an interface. We need this when we
+ * point to a base class (we must record what the type is).
+ */
+extern (C++) final class ClassReferenceExp : Expression
+{
+ StructLiteralExp value;
+
+ extern (D) this(const ref Loc loc, StructLiteralExp lit, Type type) @safe
+ {
+ super(loc, EXP.classReference);
+ assert(lit && lit.sd && lit.sd.isClassDeclaration());
+ this.value = lit;
+ this.type = type;
+ }
+
+ ClassDeclaration originalClass()
{
- FuncDeclaration fd = (sc.callsc && sc.callsc.func)
- ? sc.callsc.func
- : sc.func;
+ return value.sd.isClassDeclaration();
+ }
- const(char)* s;
- if (fd)
+ // Return index of the field, or -1 if not found
+ int getFieldIndex(Type fieldtype, uint fieldoffset)
+ {
+ ClassDeclaration cd = originalClass();
+ uint fieldsSoFar = 0;
+ for (size_t j = 0; j < value.elements.length; j++)
{
- const funcStr = fd.Dsymbol.toPrettyChars();
- OutBuffer buf;
- functionToBufferWithIdent(fd.type.isTypeFunction(), buf, funcStr, fd.isStatic);
- s = buf.extractChars();
+ while (j - fieldsSoFar >= cd.fields.length)
+ {
+ fieldsSoFar += cd.fields.length;
+ cd = cd.baseClass;
+ }
+ VarDeclaration v2 = cd.fields[j - fieldsSoFar];
+ if (fieldoffset == v2.offset && fieldtype.size() == v2.type.size())
+ {
+ return cast(int)(value.elements.length - fieldsSoFar - cd.fields.length + (j - fieldsSoFar));
+ }
}
- else
+ return -1;
+ }
+
+ // Return index of the field, or -1 if not found
+ // Same as getFieldIndex, but checks for a direct match with the VarDeclaration
+ int findFieldIndexByName(VarDeclaration v)
+ {
+ ClassDeclaration cd = originalClass();
+ size_t fieldsSoFar = 0;
+ for (size_t j = 0; j < value.elements.length; j++)
{
- s = "";
+ while (j - fieldsSoFar >= cd.fields.length)
+ {
+ fieldsSoFar += cd.fields.length;
+ cd = cd.baseClass;
+ }
+ VarDeclaration v2 = cd.fields[j - fieldsSoFar];
+ if (v == v2)
+ {
+ return cast(int)(value.elements.length - fieldsSoFar - cd.fields.length + (j - fieldsSoFar));
+ }
}
+ return -1;
+ }
- Expression e = new StringExp(loc, s.toDString());
- e = e.expressionSemantic(sc);
- e.type = Type.tstring;
- return e;
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * This type is only used by the interpreter.
+ */
+extern (C++) final class CTFEExp : Expression
+{
+ extern (D) this(EXP tok)
+ {
+ super(Loc.initial, tok);
+ type = Type.tvoid;
+ }
+
+ override const(char)* toChars() const
+ {
+ switch (op)
+ {
+ case EXP.cantExpression:
+ return "<cant>";
+ case EXP.voidExpression:
+ return "cast(void)0";
+ case EXP.showCtfeContext:
+ return "<error>";
+ case EXP.break_:
+ return "<break>";
+ case EXP.continue_:
+ return "<continue>";
+ case EXP.goto_:
+ return "<goto>";
+ default:
+ assert(0);
+ }
+ }
+
+ extern (D) __gshared CTFEExp cantexp;
+ extern (D) __gshared CTFEExp voidexp;
+ extern (D) __gshared CTFEExp breakexp;
+ extern (D) __gshared CTFEExp continueexp;
+ extern (D) __gshared CTFEExp gotoexp;
+ /* Used when additional information is needed regarding
+ * a ctfe error.
+ */
+ extern (D) __gshared CTFEExp showcontext;
+
+ extern (D) static bool isCantExp(const Expression e) @safe
+ {
+ return e && e.op == EXP.cantExpression;
+ }
+
+ extern (D) static bool isGotoExp(const Expression e) @safe
+ {
+ return e && e.op == EXP.goto_;
+ }
+}
+
+/***********************************************************
+ * Fake class which holds the thrown exception.
+ * Used for implementing exception handling.
+ */
+extern (C++) final class ThrownExceptionExp : Expression
+{
+ ClassReferenceExp thrown; // the thing being tossed
+
+ extern (D) this(const ref Loc loc, ClassReferenceExp victim) @safe
+ {
+ super(loc, EXP.thrownException);
+ this.thrown = victim;
+ this.type = victim.type;
+ }
+
+ override const(char)* toChars() const
+ {
+ return "CTFE ThrownException";
}
override void accept(Visitor v)
@@ -6330,154 +5370,6 @@ extern (C++) final class GenericExp : Expression
}
}
-/***************************************
- * Parameters:
- * sc: scope
- * flag: 1: do not issue error message for invalid modification
- 2: the exp is a DotVarExp and a subfield of the leftmost
- variable is modified
- * Returns:
- * Whether the type is modifiable
- */
-extern(D) Modifiable checkModifiable(Expression exp, Scope* sc, ModifyFlags flag = ModifyFlags.none)
-{
- switch(exp.op)
- {
- case EXP.variable:
- auto varExp = cast(VarExp)exp;
-
- //printf("VarExp::checkModifiable %s", varExp.toChars());
- assert(varExp.type);
- return varExp.var.checkModify(varExp.loc, sc, null, flag);
-
- case EXP.dotVariable:
- auto dotVarExp = cast(DotVarExp)exp;
-
- //printf("DotVarExp::checkModifiable %s %s\n", dotVarExp.toChars(), dotVarExp.type.toChars());
- if (dotVarExp.e1.op == EXP.this_)
- return dotVarExp.var.checkModify(dotVarExp.loc, sc, dotVarExp.e1, flag);
-
- /* https://issues.dlang.org/show_bug.cgi?id=12764
- * If inside a constructor and an expression of type `this.field.var`
- * is encountered, where `field` is a struct declaration with
- * default construction disabled, we must make sure that
- * assigning to `var` does not imply that `field` was initialized
- */
- if (sc.func && sc.func.isCtorDeclaration())
- {
- // if inside a constructor scope and e1 of this DotVarExp
- // is another DotVarExp, then check if the leftmost expression is a `this` identifier
- if (auto dve = dotVarExp.e1.isDotVarExp())
- {
- // Iterate the chain of DotVarExp to find `this`
- // Keep track whether access to fields was limited to union members
- // s.t. one can initialize an entire struct inside nested unions
- // (but not its members)
- bool onlyUnion = true;
- while (true)
- {
- auto v = dve.var.isVarDeclaration();
- assert(v);
-
- // Accessing union member?
- auto t = v.type.isTypeStruct();
- if (!t || !t.sym.isUnionDeclaration())
- onlyUnion = false;
-
- // Another DotVarExp left?
- if (!dve.e1 || dve.e1.op != EXP.dotVariable)
- break;
-
- dve = cast(DotVarExp) dve.e1;
- }
-
- if (dve.e1.op == EXP.this_)
- {
- scope v = dve.var.isVarDeclaration();
- /* if v is a struct member field with no initializer, no default construction
- * and v wasn't intialized before
- */
- if (v && v.isField() && !v._init && !v.ctorinit)
- {
- if (auto ts = v.type.isTypeStruct())
- {
- if (ts.sym.noDefaultCtor)
- {
- /* checkModify will consider that this is an initialization
- * of v while it is actually an assignment of a field of v
- */
- scope modifyLevel = v.checkModify(dotVarExp.loc, sc, dve.e1, !onlyUnion ? (flag | ModifyFlags.fieldAssign) : flag);
- if (modifyLevel == Modifiable.initialization)
- {
- // https://issues.dlang.org/show_bug.cgi?id=22118
- // v is a union type field that was assigned
- // a variable, therefore it counts as initialization
- if (v.ctorinit)
- return Modifiable.initialization;
-
- return Modifiable.yes;
- }
- return modifyLevel;
- }
- }
- }
- }
- }
- }
-
- //printf("\te1 = %s\n", e1.toChars());
- return dotVarExp.e1.checkModifiable(sc, flag);
-
- case EXP.star:
- auto ptrExp = cast(PtrExp)exp;
- if (auto se = ptrExp.e1.isSymOffExp())
- {
- return se.var.checkModify(ptrExp.loc, sc, null, flag);
- }
- else if (auto ae = ptrExp.e1.isAddrExp())
- {
- return ae.e1.checkModifiable(sc, flag);
- }
- return Modifiable.yes;
-
- case EXP.slice:
- auto sliceExp = cast(SliceExp)exp;
-
- //printf("SliceExp::checkModifiable %s\n", sliceExp.toChars());
- auto e1 = sliceExp.e1;
- if (e1.type.ty == Tsarray || (e1.op == EXP.index && e1.type.ty != Tarray) || e1.op == EXP.slice)
- {
- return e1.checkModifiable(sc, flag);
- }
- return Modifiable.yes;
-
- case EXP.comma:
- return (cast(CommaExp)exp).e2.checkModifiable(sc, flag);
-
- case EXP.index:
- auto indexExp = cast(IndexExp)exp;
- auto e1 = indexExp.e1;
- if (e1.type.ty == Tsarray ||
- e1.type.ty == Taarray ||
- (e1.op == EXP.index && e1.type.ty != Tarray) ||
- e1.op == EXP.slice)
- {
- return e1.checkModifiable(sc, flag);
- }
- return Modifiable.yes;
-
- case EXP.question:
- auto condExp = cast(CondExp)exp;
- if (condExp.e1.checkModifiable(sc, flag) != Modifiable.no
- && condExp.e2.checkModifiable(sc, flag) != Modifiable.no)
- return Modifiable.yes;
- return Modifiable.no;
-
- default:
- return exp.type ? Modifiable.yes : Modifiable.no; // default modifiable
- }
-}
-
/**
* Verify if the given identifier is _d_array{,set}ctor.
*
@@ -50,7 +50,10 @@ struct Symbol; // back end symbol
Expression *ctfeInterpret(Expression *e);
void expandTuples(Expressions *exps, Identifiers *names = nullptr);
StringExp *toUTF8(StringExp *se, Scope *sc);
+Expression *resolveLoc(Expression *exp, const Loc &loc, Scope *sc);
MATCH implicitConvTo(Expression *e, Type *t);
+Expression *toLvalue(Expression *_this, Scope *sc);
+Expression *modifiableLvalue(Expression* exp, Scope *sc);
typedef unsigned char OwnedBy;
enum
@@ -99,9 +102,6 @@ public:
virtual complex_t toComplex();
virtual StringExp *toStringExp();
virtual bool isLvalue();
- virtual Expression *toLvalue(Scope *sc, Expression *e);
- virtual Expression *modifiableLvalue(Scope *sc, Expression *e);
- virtual Expression *resolveLoc(const Loc &loc, Scope *sc);
virtual bool checkType();
virtual bool checkValue();
Expression *addressOf();
@@ -244,7 +244,6 @@ public:
real_t toImaginary() override;
complex_t toComplex() override;
Optional<bool> toBool() override;
- Expression *toLvalue(Scope *sc, Expression *e) override;
void accept(Visitor *v) override { v->visit(this); }
dinteger_t getInteger() { return value; }
template<int v>
@@ -254,7 +253,6 @@ public:
class ErrorExp final : public Expression
{
public:
- Expression *toLvalue(Scope *sc, Expression *e) override;
void accept(Visitor *v) override { v->visit(this); }
static ErrorExp *errorexp; // handy shared value
@@ -302,7 +300,6 @@ public:
static IdentifierExp *create(const Loc &loc, Identifier *ident);
bool isLvalue() override final;
- Expression *toLvalue(Scope *sc, Expression *e) override final;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -320,7 +317,6 @@ public:
DsymbolExp *syntaxCopy() override;
bool isLvalue() override;
- Expression *toLvalue(Scope *sc, Expression *e) override;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -331,8 +327,7 @@ public:
ThisExp *syntaxCopy() override;
Optional<bool> toBool() override;
- bool isLvalue() override final;
- Expression *toLvalue(Scope *sc, Expression *e) override final;
+ bool isLvalue() override;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -370,8 +365,6 @@ public:
StringExp *toStringExp() override;
Optional<bool> toBool() override;
bool isLvalue() override;
- Expression *toLvalue(Scope *sc, Expression *e) override;
- Expression *modifiableLvalue(Scope *sc, Expression *e) override;
void accept(Visitor *v) override { v->visit(this); }
size_t numberOfCodeUnits(int tynto = 0) const;
void writeTo(void* dest, bool zero, int tyto = 0) const;
@@ -469,7 +462,6 @@ public:
static StructLiteralExp *create(const Loc &loc, StructDeclaration *sd, void *elements, Type *stype = NULL);
bool equals(const RootObject * const o) const override;
StructLiteralExp *syntaxCopy() override;
- Expression *toLvalue(Scope *sc, Expression *e) override;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -501,7 +493,6 @@ public:
FuncDeclaration *fd;
bool isLvalue() override;
- Expression *toLvalue(Scope *sc, Expression *e) override;
bool checkType() override;
bool checkValue() override;
void accept(Visitor *v) override { v->visit(this); }
@@ -575,8 +566,6 @@ public:
static VarExp *create(const Loc &loc, Declaration *var, bool hasOverloads = true);
bool equals(const RootObject * const o) const override;
bool isLvalue() override;
- Expression *toLvalue(Scope *sc, Expression *e) override;
- Expression *modifiableLvalue(Scope *sc, Expression *e) override;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -589,7 +578,6 @@ public:
OverloadSet *vars;
bool isLvalue() override;
- Expression *toLvalue(Scope *sc, Expression *e) override;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -678,7 +666,6 @@ public:
Expression *e1;
UnaExp *syntaxCopy() override;
- Expression *resolveLoc(const Loc &loc, Scope *sc) override final;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -701,8 +688,6 @@ class BinAssignExp : public BinExp
{
public:
bool isLvalue() override final;
- Expression *toLvalue(Scope *sc, Expression *ex) override final;
- Expression *modifiableLvalue(Scope *sc, Expression *e) override final;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -767,8 +752,6 @@ public:
d_bool hasOverloads;
bool isLvalue() override;
- Expression *toLvalue(Scope *sc, Expression *e) override;
- Expression *modifiableLvalue(Scope *sc, Expression *e) override;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -821,7 +804,6 @@ public:
CallExp *syntaxCopy() override;
bool isLvalue() override;
- Expression *toLvalue(Scope *sc, Expression *e) override;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -836,8 +818,6 @@ class PtrExp final : public UnaExp
{
public:
bool isLvalue() override;
- Expression *toLvalue(Scope *sc, Expression *e) override;
- Expression *modifiableLvalue(Scope *sc, Expression *e) override;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -882,7 +862,6 @@ public:
CastExp *syntaxCopy() override;
bool isLvalue() override;
- Expression *toLvalue(Scope *sc, Expression *e) override;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -903,7 +882,6 @@ class VectorArrayExp final : public UnaExp
{
public:
bool isLvalue() override;
- Expression *toLvalue(Scope *sc, Expression *e) override;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -926,8 +904,6 @@ private:
public:
SliceExp *syntaxCopy() override;
bool isLvalue() override;
- Expression *toLvalue(Scope *sc, Expression *e) override;
- Expression *modifiableLvalue(Scope *sc, Expression *e) override;
Optional<bool> toBool() override;
void accept(Visitor *v) override { v->visit(this); }
@@ -953,8 +929,6 @@ class DelegatePtrExp final : public UnaExp
{
public:
bool isLvalue() override;
- Expression *toLvalue(Scope *sc, Expression *e) override;
- Expression *modifiableLvalue(Scope *sc, Expression *e) override;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -962,8 +936,6 @@ class DelegateFuncptrExp final : public UnaExp
{
public:
bool isLvalue() override;
- Expression *toLvalue(Scope *sc, Expression *e) override;
- Expression *modifiableLvalue(Scope *sc, Expression *e) override;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -978,7 +950,6 @@ public:
ArrayExp *syntaxCopy() override;
bool isLvalue() override;
- Expression *toLvalue(Scope *sc, Expression *e) override;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -997,8 +968,6 @@ public:
d_bool isGenerated;
d_bool allowCommaExp;
bool isLvalue() override;
- Expression *toLvalue(Scope *sc, Expression *e) override;
- Expression *modifiableLvalue(Scope *sc, Expression *e) override;
Optional<bool> toBool() override;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -1012,8 +981,6 @@ public:
IndexExp *syntaxCopy() override;
bool isLvalue() override;
- Expression *toLvalue(Scope *sc, Expression *e) override;
- Expression *modifiableLvalue(Scope *sc, Expression *e) override;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -1047,7 +1014,6 @@ public:
MemorySet memset;
bool isLvalue() override final;
- Expression *toLvalue(Scope *sc, Expression *ex) override final;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -1292,8 +1258,6 @@ public:
CondExp *syntaxCopy() override;
bool isLvalue() override;
- Expression *toLvalue(Scope *sc, Expression *e) override;
- Expression *modifiableLvalue(Scope *sc, Expression *e) override;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -1320,35 +1284,30 @@ public:
class FileInitExp final : public DefaultInitExp
{
public:
- Expression *resolveLoc(const Loc &loc, Scope *sc) override;
void accept(Visitor *v) override { v->visit(this); }
};
class LineInitExp final : public DefaultInitExp
{
public:
- Expression *resolveLoc(const Loc &loc, Scope *sc) override;
void accept(Visitor *v) override { v->visit(this); }
};
class ModuleInitExp final : public DefaultInitExp
{
public:
- Expression *resolveLoc(const Loc &loc, Scope *sc) override;
void accept(Visitor *v) override { v->visit(this); }
};
class FuncInitExp final : public DefaultInitExp
{
public:
- Expression *resolveLoc(const Loc &loc, Scope *sc) override;
void accept(Visitor *v) override { v->visit(this); }
};
class PrettyFuncInitExp final : public DefaultInitExp
{
public:
- Expression *resolveLoc(const Loc &loc, Scope *sc) override;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -87,6 +87,46 @@ import dmd.visitor;
enum LOGSEMANTIC = false;
+/***********************************
+ * Determine if a `this` is needed to access `d`.
+ * Params:
+ * sc = context
+ * d = declaration to check
+ * Returns:
+ * true means a `this` is needed
+ */
+private bool isNeedThisScope(Scope* sc, Declaration d)
+{
+ if (sc.intypeof == 1)
+ return false;
+
+ AggregateDeclaration ad = d.isThis();
+ if (!ad)
+ return false;
+ //printf("d = %s, ad = %s\n", d.toChars(), ad.toChars());
+
+ for (Dsymbol s = sc.parent; s; s = s.toParentLocal())
+ {
+ //printf("\ts = %s %s, toParent2() = %p\n", s.kind(), s.toChars(), s.toParent2());
+ if (AggregateDeclaration ad2 = s.isAggregateDeclaration())
+ {
+ if (ad2 == ad)
+ return false;
+ else if (ad2.isNested())
+ continue;
+ else
+ return true;
+ }
+ if (FuncDeclaration f = s.isFuncDeclaration())
+ {
+ if (f.isMemberLocal())
+ break;
+ }
+ }
+ return true;
+}
+
+
/********************************************************
* Perform semantic analysis and CTFE on expressions to produce
* a string.
@@ -196,6 +236,51 @@ FuncDeclaration hasThis(Scope* sc)
}
+extern (D) bool findTempDecl(DotTemplateInstanceExp exp, Scope* sc)
+{
+ auto ti = exp.ti;
+ auto e1 = exp.e1;
+ static if (LOGSEMANTIC)
+ {
+ printf("DotTemplateInstanceExp::findTempDecl('%s')\n", exp.toChars());
+ }
+ if (ti.tempdecl)
+ return true;
+
+ Expression e = new DotIdExp(exp.loc, e1, ti.name);
+ e = e.expressionSemantic(sc);
+ if (e.op == EXP.dot)
+ e = (cast(DotExp)e).e2;
+
+ Dsymbol s = null;
+ switch (e.op)
+ {
+ case EXP.overloadSet:
+ s = (cast(OverExp)e).vars;
+ break;
+
+ case EXP.dotTemplateDeclaration:
+ s = (cast(DotTemplateExp)e).td;
+ break;
+
+ case EXP.scope_:
+ s = (cast(ScopeExp)e).sds;
+ break;
+
+ case EXP.dotVariable:
+ s = (cast(DotVarExp)e).var;
+ break;
+
+ case EXP.variable:
+ s = (cast(VarExp)e).var;
+ break;
+
+ default:
+ return false;
+ }
+ return ti.updateTempDecl(sc, s);
+}
+
/***********************************************************
* Resolve `exp` as a compile-time known string.
* Params:
@@ -1731,6 +1816,403 @@ private bool haveSameThis(FuncDeclaration outerFunc, FuncDeclaration calledFunc)
return false;
}
+/*********************************************
+ * Calling function f.
+ * Check the purity, i.e. if we're in a pure function
+ * we can only call other pure functions.
+ * Returns true if error occurs.
+ */
+private bool checkPurity(FuncDeclaration f, const ref Loc loc, Scope* sc)
+{
+ if (!sc.func)
+ return false;
+ if (sc.func == f)
+ return false;
+ if (sc.intypeof == 1)
+ return false;
+ if (sc.flags & (SCOPE.ctfe | SCOPE.debug_))
+ return false;
+
+ // If the call has a pure parent, then the called func must be pure.
+ if (!f.isPure() && checkImpure(sc, loc, null, f))
+ {
+ error(loc, "`pure` %s `%s` cannot call impure %s `%s`",
+ sc.func.kind(), sc.func.toPrettyChars(), f.kind(),
+ f.toPrettyChars());
+
+ if (!f.isDtorDeclaration())
+ errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.pure_);
+
+ f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().purity != PURE.impure, "impure");
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Checks whether `f` is a generated `DtorDeclaration` that hides a user-defined one
+ * which passes `check` while `f` doesn't (e.g. when the user defined dtor is pure but
+ * the generated dtor is not).
+ * In that case the method will identify and print all members causing the attribute
+ * missmatch.
+ *
+ * Params:
+ * f = potential `DtorDeclaration`
+ * sc = scope
+ * loc = location
+ * check = current check (e.g. whether it's pure)
+ * checkName = the kind of check (e.g. `"pure"`)
+ */
+void checkOverriddenDtor(FuncDeclaration f, Scope* sc, const ref Loc loc,
+ scope bool function(DtorDeclaration) check, const string checkName)
+{
+ auto dd = f.isDtorDeclaration();
+ if (!dd || !dd.isGenerated())
+ return;
+
+ // DtorDeclaration without parents should fail at an earlier stage
+ auto ad = cast(AggregateDeclaration) f.toParent2();
+ assert(ad);
+
+ if (ad.userDtors.length)
+ {
+ if (!check(ad.userDtors[0])) // doesn't match check (e.g. is impure as well)
+ return;
+
+ // Sanity check
+ assert(!check(ad.fieldDtor));
+ }
+
+ dd.loc.errorSupplemental("%s`%s.~this` is %.*s because of the following field's destructors:",
+ dd.isGenerated() ? "generated " : "".ptr,
+ ad.toChars,
+ cast(int) checkName.length, checkName.ptr);
+
+ // Search for the offending fields
+ foreach (field; ad.fields)
+ {
+ // Only structs may define automatically called destructors
+ auto ts = field.type.isTypeStruct();
+ if (!ts)
+ {
+ // But they might be part of a static array
+ auto ta = field.type.isTypeSArray();
+ if (!ta)
+ continue;
+
+ ts = ta.baseElemOf().isTypeStruct();
+ if (!ts)
+ continue;
+ }
+
+ auto fieldSym = ts.toDsymbol(sc);
+ assert(fieldSym); // Resolving ts must succeed because missing defs. should error before
+
+ auto fieldSd = fieldSym.isStructDeclaration();
+ assert(fieldSd); // ts is a TypeStruct, this would imply a malformed ASR
+
+ if (fieldSd.dtor && !check(fieldSd.dtor))
+ {
+ field.loc.errorSupplemental(" - %s %s", field.type.toChars(), field.toChars());
+
+ if (fieldSd.dtor.isGenerated())
+ fieldSd.dtor.checkOverriddenDtor(sc, loc, check, checkName);
+ else
+ fieldSd.dtor.loc.errorSupplemental(" %.*s `%s.~this` is declared here",
+ cast(int) checkName.length, checkName.ptr, fieldSd.toChars());
+ }
+ }
+}
+
+/*******************************************
+ * Accessing variable v.
+ * Check for purity and safety violations.
+ * Returns true if error occurs.
+ */
+private bool checkPurity(VarDeclaration v, const ref Loc loc, Scope* sc)
+{
+ //printf("v = %s %s\n", v.type.toChars(), v.toChars());
+ /* Look for purity and safety violations when accessing variable v
+ * from current function.
+ */
+ if (!sc.func)
+ return false;
+ if (sc.intypeof == 1)
+ return false; // allow violations inside typeof(expression)
+ if (sc.flags & (SCOPE.ctfe | SCOPE.debug_))
+ return false; // allow violations inside compile-time evaluated expressions and debug conditionals
+ if (v.ident == Id.ctfe)
+ return false; // magic variable never violates pure and safe
+ if (v.isImmutable())
+ return false; // always safe and pure to access immutables...
+ if (v.isConst() && !v.isReference() && (v.isDataseg() || v.isParameter()) && v.type.implicitConvTo(v.type.immutableOf()))
+ return false; // or const global/parameter values which have no mutable indirections
+ if (v.storage_class & STC.manifest)
+ return false; // ...or manifest constants
+
+ // accessing empty structs is pure
+ // https://issues.dlang.org/show_bug.cgi?id=18694
+ // https://issues.dlang.org/show_bug.cgi?id=21464
+ // https://issues.dlang.org/show_bug.cgi?id=23589
+ if (v.type.ty == Tstruct)
+ {
+ StructDeclaration sd = (cast(TypeStruct)v.type).sym;
+ if (sd.members) // not opaque
+ {
+ if (sd.semanticRun >= PASS.semanticdone)
+ sd.determineSize(v.loc);
+ if (sd.hasNoFields)
+ return false;
+ }
+ }
+
+ bool err = false;
+ if (v.isDataseg())
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=7533
+ // Accessing implicit generated __gate is pure.
+ if (v.ident == Id.gate)
+ return false;
+
+ if (checkImpure(sc, loc, "`pure` %s `%s` cannot access mutable static data `%s`", v))
+ {
+ error(loc, "`pure` %s `%s` cannot access mutable static data `%s`",
+ sc.func.kind(), sc.func.toPrettyChars(), v.toChars());
+ err = true;
+ }
+ }
+ else
+ {
+ /* Given:
+ * void f() {
+ * int fx;
+ * pure void g() {
+ * int gx;
+ * /+pure+/ void h() {
+ * int hx;
+ * /+pure+/ void i() { }
+ * }
+ * }
+ * }
+ * i() can modify hx and gx but not fx
+ */
+
+ Dsymbol vparent = v.toParent2();
+ for (Dsymbol s = sc.func; !err && s; s = s.toParentP(vparent))
+ {
+ if (s == vparent)
+ break;
+
+ if (AggregateDeclaration ad = s.isAggregateDeclaration())
+ {
+ if (ad.isNested())
+ continue;
+ break;
+ }
+ FuncDeclaration ff = s.isFuncDeclaration();
+ if (!ff)
+ break;
+ if (ff.isNested() || ff.isThis())
+ {
+ if (ff.type.isImmutable() ||
+ ff.type.isShared() && !MODimplicitConv(ff.type.mod, v.type.mod))
+ {
+ OutBuffer ffbuf;
+ OutBuffer vbuf;
+ MODMatchToBuffer(&ffbuf, ff.type.mod, v.type.mod);
+ MODMatchToBuffer(&vbuf, v.type.mod, ff.type.mod);
+ error(loc, "%s%s `%s` cannot access %sdata `%s`",
+ ffbuf.peekChars(), ff.kind(), ff.toPrettyChars(), vbuf.peekChars(), v.toChars());
+ err = true;
+ break;
+ }
+ continue;
+ }
+ break;
+ }
+ }
+
+ /* Do not allow safe functions to access __gshared data
+ */
+ if (v.storage_class & STC.gshared)
+ {
+ if (sc.setUnsafe(false, loc,
+ "`@safe` function `%s` cannot access `__gshared` data `%s`", sc.func, v))
+ {
+ err = true;
+ }
+ }
+
+ return err;
+}
+
+/*
+Check if sc.func is impure or can be made impure.
+Returns true on error, i.e. if sc.func is pure and cannot be made impure.
+*/
+private bool checkImpure(Scope* sc, Loc loc, const(char)* fmt, RootObject arg0)
+{
+ return sc.func && (isRootTraitsCompilesScope(sc)
+ ? sc.func.isPureBypassingInference() >= PURE.weak
+ : sc.func.setImpure(loc, fmt, arg0));
+}
+
+/*********************************************
+ * Calling function f.
+ * Check the safety, i.e. if we're in a @safe function
+ * we can only call @safe or @trusted functions.
+ * Returns true if error occurs.
+ */
+private bool checkSafety(FuncDeclaration f, ref Loc loc, Scope* sc)
+{
+ if (sc.func == f)
+ return false;
+ if (sc.intypeof == 1)
+ return false;
+ if (sc.flags & SCOPE.debug_)
+ return false;
+ if ((sc.flags & SCOPE.ctfe) && sc.func)
+ return false;
+
+ if (!sc.func)
+ {
+ if (sc.varDecl && !f.safetyInprocess && !f.isSafe() && !f.isTrusted())
+ {
+ if (sc.varDecl.storage_class & STC.safe)
+ {
+ error(loc, "`@safe` variable `%s` cannot be initialized by calling `@system` function `%s`",
+ sc.varDecl.toChars(), f.toChars());
+ return true;
+ }
+ else
+ {
+ sc.varDecl.storage_class |= STC.system;
+ sc.varDecl.systemInferred = true;
+ }
+ }
+ return false;
+ }
+
+ if (!f.isSafe() && !f.isTrusted())
+ {
+ if (isRootTraitsCompilesScope(sc) ? sc.func.isSafeBypassingInference() : sc.func.setUnsafeCall(f))
+ {
+ if (!loc.isValid()) // e.g. implicitly generated dtor
+ loc = sc.func.loc;
+
+ const prettyChars = f.toPrettyChars();
+ error(loc, "`@safe` %s `%s` cannot call `@system` %s `%s`",
+ sc.func.kind(), sc.func.toPrettyChars(), f.kind(),
+ prettyChars);
+ if (!f.isDtorDeclaration)
+ errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.safe);
+ .errorSupplemental(f.loc, "`%s` is declared here", prettyChars);
+
+ f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().trust > TRUST.system, "@system");
+
+ return true;
+ }
+ }
+ else if (f.isSafe() && f.safetyViolation)
+ {
+ // for dip1000 by default transition, print deprecations for calling functions that will become `@system`
+ if (sc.func.isSafeBypassingInference())
+ {
+ .deprecation(loc, "`@safe` function `%s` calling `%s`", sc.func.toChars(), f.toChars());
+ errorSupplementalInferredAttr(f, 10, true, STC.safe);
+ }
+ else if (!sc.func.safetyViolation)
+ {
+ import dmd.func : AttributeViolation;
+ sc.func.safetyViolation = new AttributeViolation(loc, null, f, null, null);
+ }
+ }
+ return false;
+}
+
+/*********************************************
+ * Calling function f.
+ * Check the @nogc-ness, i.e. if we're in a @nogc function
+ * we can only call other @nogc functions.
+ * Returns true if error occurs.
+ */
+private bool checkNogc(FuncDeclaration f, ref Loc loc, Scope* sc)
+{
+ if (!sc.func)
+ return false;
+ if (sc.func == f)
+ return false;
+ if (sc.intypeof == 1)
+ return false;
+ if (sc.flags & (SCOPE.ctfe | SCOPE.debug_))
+ return false;
+ /* The original expressions (`new S(...)` or `new S[...]``) will be
+ * verified instead. This is to keep errors related to the original code
+ * and not the lowering.
+ */
+ if (f.ident == Id._d_newitemT || f.ident == Id._d_newarrayT)
+ return false;
+
+ if (!f.isNogc())
+ {
+ if (isRootTraitsCompilesScope(sc) ? sc.func.isNogcBypassingInference() : sc.func.setGCCall(f))
+ {
+ if (loc.linnum == 0) // e.g. implicitly generated dtor
+ loc = sc.func.loc;
+
+ // Lowered non-@nogc'd hooks will print their own error message inside of nogc.d (NOGCVisitor.visit(CallExp e)),
+ // so don't print anything to avoid double error messages.
+ if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT
+ || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX
+ || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT))
+ {
+ error(loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`",
+ sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars());
+
+ if (!f.isDtorDeclaration)
+ f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc);
+ }
+
+ f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().isnogc, "non-@nogc");
+
+ return true;
+ }
+ }
+ return false;
+}
+
+/********************************************
+ * Check that the postblit is callable if t is an array of structs.
+ * Returns true if error happens.
+ */
+private bool checkPostblit(Type t, ref Loc loc, Scope* sc)
+{
+ if (auto ts = t.baseElemOf().isTypeStruct())
+ {
+ if (global.params.useTypeInfo && Type.dtypeinfo)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=11395
+ // Require TypeInfo generation for array concatenation
+ semanticTypeInfo(sc, t);
+ }
+
+ StructDeclaration sd = ts.sym;
+ if (sd.postblit)
+ {
+ if (sd.postblit.checkDisabled(loc, sc))
+ return true;
+
+ //checkDeprecated(sc, sd.postblit); // necessary?
+ sd.postblit.checkPurity(loc, sc);
+ sd.postblit.checkSafety(loc, sc);
+ sd.postblit.checkNogc(loc, sc);
+ //checkAccess(sd, loc, sc, sd.postblit); // necessary?
+ return false;
+ }
+ }
+ return false;
+}
+
/***************************************
* Pull out any properties.
*/
@@ -1942,7 +2424,7 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 =
{
if (auto v = ve.var.isVarDeclaration())
{
- if (ve.checkPurity(sc, v))
+ if (v.checkPurity(ve.loc, sc))
return ErrorExp.get();
}
}
@@ -2647,7 +3129,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
ev = new CommaExp(arg.loc, ev, new VarExp(arg.loc, v));
arg = ev.expressionSemantic(sc);
}
- arg = arg.toLvalue(sc, arg);
+ arg = arg.toLvalue(sc);
// Look for mutable misaligned pointer, etc., in @safe mode
err |= checkUnsafeAccess(sc, arg, false, true);
@@ -2665,7 +3147,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
ev = new CommaExp(arg.loc, ev, new VarExp(arg.loc, v));
arg = ev.expressionSemantic(sc);
}
- arg = arg.toLvalue(sc, arg);
+ arg = arg.toLvalue(sc);
// Look for mutable misaligned pointer, etc., in @safe mode
err |= checkUnsafeAccess(sc, arg, false, true);
@@ -2684,7 +3166,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
err |= checkUnsafeAccess(sc, arg, false, true);
err |= checkDefCtor(arg.loc, t); // t must be default constructible
}
- arg = arg.toLvalue(sc, arg);
+ arg = arg.toLvalue(sc);
}
else if (p.isLazy())
{
@@ -4852,6 +5334,52 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
result = e;
}
+ private void genIdent(FuncExp exp, Scope* sc)
+ {
+ if (exp.fd.ident == Id.empty)
+ {
+ const(char)[] s;
+ if (exp.fd.fes)
+ s = "__foreachbody";
+ else if (exp.fd.tok == TOK.reserved)
+ s = "__lambda";
+ else if (exp.fd.tok == TOK.delegate_)
+ s = "__dgliteral";
+ else
+ s = "__funcliteral";
+
+ DsymbolTable symtab;
+ if (FuncDeclaration func = sc.parent.isFuncDeclaration())
+ {
+ if (func.localsymtab is null)
+ {
+ // Inside template constraint, symtab is not set yet.
+ // Initialize it lazily.
+ func.localsymtab = new DsymbolTable();
+ }
+ symtab = func.localsymtab;
+ }
+ else
+ {
+ ScopeDsymbol sds = sc.parent.isScopeDsymbol();
+ if (!sds.symtab)
+ {
+ // Inside template constraint, symtab may not be set yet.
+ // Initialize it lazily.
+ assert(sds.isTemplateInstance());
+ sds.symtab = new DsymbolTable();
+ }
+ symtab = sds.symtab;
+ }
+ assert(symtab);
+ Identifier id = Identifier.generateId(s, symtab.length() + 1);
+ exp.fd.ident = id;
+ if (exp.td)
+ exp.td.ident = id;
+ symtab.insert(exp.td ? cast(Dsymbol)exp.td : cast(Dsymbol)exp.fd);
+ }
+ }
+
override void visit(FuncExp exp)
{
static if (LOGSEMANTIC)
@@ -4882,7 +5410,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
//if (fd.treq)
// fd.treq = fd.treq.dsymbolSemantic(loc, sc);
- exp.genIdent(sc);
+ genIdent(exp, sc);
// Set target of return type inference
if (exp.fd.treq && !exp.fd.type.nextOf())
@@ -4995,7 +5523,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
return checkarg;
}
- exp.genIdent(sc);
+ genIdent(exp, sc);
assert(exp.td.parameters && exp.td.parameters.length);
exp.td.dsymbolSemantic(sc);
@@ -5257,7 +5785,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
ve.type = t.typeSemantic(exp.loc, sc);
}
VarDeclaration v = ve.var.isVarDeclaration();
- if (v && ve.checkPurity(sc, v))
+ if (v && v.checkPurity(ve.loc, sc))
return setError();
}
@@ -5885,9 +6413,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
// Purity and safety check should run after testing arguments matching
if (exp.f)
{
- exp.checkPurity(sc, exp.f);
- exp.checkSafety(sc, exp.f);
- exp.checkNogc(sc, exp.f);
+ exp.f.checkPurity(exp.loc, sc);
+ exp.f.checkSafety(exp.loc, sc);
+ exp.f.checkNogc(exp.loc, sc);
if (exp.f.checkNestedReference(sc, exp.loc))
return setError();
}
@@ -6795,7 +7323,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}
exp.e1 = exp.e1.expressionSemantic(sc);
- exp.e1 = exp.e1.modifiableLvalue(sc, exp.e1);
+ exp.e1 = exp.e1.modifiableLvalue(sc);
exp.e1 = exp.e1.optimize(WANTvalue, /*keepLvalue*/ true);
exp.type = exp.e1.type;
@@ -6873,7 +7401,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
uint errors = global.errors;
const len = buf.length;
const str = buf.extractChars()[0 .. len];
- const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput;
+ const bool doUnittests = global.params.parsingUnittestsRequired();
auto loc = adjustLocForMixin(str, exp.loc, global.params.mixinOut);
scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
p.transitionIn = global.params.v.vin;
@@ -7767,7 +8295,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
else
{
// `toLvalue` call further below is upon exp.e1, omitting & from the error message
- exp.toLvalue(sc, null);
+ exp.toLvalue(sc);
return setError();
}
}
@@ -7857,7 +8385,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}
}
- exp.e1 = exp.e1.toLvalue(sc, null);
+ exp.e1 = exp.e1.toLvalue(sc);
if (exp.e1.op == EXP.error)
{
result = exp.e1;
@@ -7934,7 +8462,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
if (!checkAddressVar(sc, exp.e1, v))
return setError();
- ve.checkPurity(sc, v);
+ v.checkPurity(ve.loc, sc);
}
FuncDeclaration f = ve.var.isFuncDeclaration();
if (f)
@@ -8006,7 +8534,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
*/
if (VarDeclaration v = expToVariable(exp.e1))
{
- exp.e1.checkPurity(sc, v);
+ v.checkPurity(exp.e1.loc, sc);
}
}
else if (wasCond)
@@ -8275,7 +8803,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
return;
}
exp.e1 = resolveProperties(sc, exp.e1);
- exp.e1 = exp.e1.modifiableLvalue(sc, null);
+ exp.e1 = exp.e1.modifiableLvalue(sc);
if (exp.e1.op == EXP.error)
{
result = exp.e1;
@@ -8306,9 +8834,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
if (cd.dtor)
{
err |= !cd.dtor.functionSemantic();
- err |= exp.checkPurity(sc, cd.dtor);
- err |= exp.checkSafety(sc, cd.dtor);
- err |= exp.checkNogc(sc, cd.dtor);
+ err |= cd.dtor.checkPurity(exp.loc, sc);
+ err |= cd.dtor.checkSafety(exp.loc, sc);
+ err |= cd.dtor.checkNogc(exp.loc, sc);
}
if (err)
return setError();
@@ -9491,7 +10019,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
return;
}
- exp.e1 = exp.e1.modifiableLvalue(sc, exp.e1);
+ exp.e1 = exp.e1.modifiableLvalue(sc);
exp.e1 = exp.e1.optimize(WANTvalue, /*keepLvalue*/ true);
e = exp;
@@ -10253,7 +10781,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
Expression ex;
ex = new IndexExp(exp.loc, ea, ek);
ex = ex.expressionSemantic(sc);
- ex = ex.modifiableLvalue(sc, ex); // allocate new slot
+ ex = ex.modifiableLvalue(sc); // allocate new slot
ex = ex.optimize(WANTvalue);
ey = new ConstructExp(exp.loc, ex, ey);
@@ -10344,7 +10872,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
if (exp.op != EXP.blit && (e2x.op == EXP.slice && (cast(UnaExp)e2x).e1.isLvalue() || e2x.op == EXP.cast_ && (cast(UnaExp)e2x).e1.isLvalue() || e2x.op != EXP.slice && e2x.isLvalue()))
{
- if (e1x.checkPostblit(sc, t1))
+ if (t1.checkPostblit(e1x.loc, sc))
return setError();
}
@@ -10446,7 +10974,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
// e1 is not an lvalue, but we let code generator handle it
- auto ale1x = ale.e1.modifiableLvalue(sc, exp.e1);
+ auto ale1x = ale.e1.modifiableLvalueImpl(sc, exp.e1);
if (ale1x.op == EXP.error)
return setResult(ale1x);
ale.e1 = ale1x;
@@ -10532,7 +11060,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
se = cast(SliceExp)se.e1;
if (se.e1.op == EXP.question && se.e1.type.toBasetype().ty == Tsarray)
{
- se.e1 = se.e1.modifiableLvalue(sc, exp.e1);
+ se.e1 = se.e1.modifiableLvalueImpl(sc, exp.e1);
if (se.e1.op == EXP.error)
return setResult(se.e1);
}
@@ -10556,7 +11084,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
// Try to do a decent error message with the expression
// before it gets constant folded
if (exp.op == EXP.assign)
- e1x = e1x.modifiableLvalue(sc, e1old);
+ e1x = e1x.modifiableLvalueImpl(sc, e1old);
e1x = e1x.optimize(WANTvalue, /*keepLvalue*/ true);
@@ -10587,7 +11115,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
// '= null' is the only allowable block assignment (Bug 7493)
exp.memset = MemorySet.blockAssign; // make it easy for back end to tell what this is
e2x = e2x.implicitCastTo(sc, t1.nextOf());
- if (exp.op != EXP.blit && e2x.isLvalue() && exp.e1.checkPostblit(sc, t1.nextOf()))
+ if (exp.op != EXP.blit && e2x.isLvalue() && t1.nextOf.checkPostblit(exp.e1.loc, sc))
return setError();
}
else if (exp.e1.op == EXP.slice &&
@@ -10625,7 +11153,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
e2x.op == EXP.cast_ && (cast(UnaExp)e2x).e1.isLvalue() ||
e2x.op != EXP.slice && e2x.isLvalue()))
{
- if (exp.e1.checkPostblit(sc, t1.nextOf()))
+ if (t1.nextOf().checkPostblit(exp.e1.loc, sc))
return setError();
}
@@ -11004,7 +11532,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}
else
{
- exp.e1 = exp.e1.modifiableLvalue(sc, exp.e1);
+ exp.e1 = exp.e1.modifiableLvalue(sc);
}
if ((exp.e1.type.isintegral() || exp.e1.type.isfloating()) && (exp.e2.type.isintegral() || exp.e2.type.isfloating()))
@@ -11063,7 +11591,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}
}
- exp.e1 = exp.e1.modifiableLvalue(sc, exp.e1);
+ exp.e1 = exp.e1.modifiableLvalue(sc);
if (exp.e1.op == EXP.error)
{
result = exp.e1;
@@ -11095,7 +11623,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
// EXP.concatenateAssign
assert(exp.op == EXP.concatenateAssign);
- if (exp.e1.checkPostblit(sc, tb1next))
+ if (tb1next.checkPostblit(exp.e1.loc, sc))
return setError();
exp.e2 = exp.e2.castTo(sc, exp.e1.type);
@@ -11113,7 +11641,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
if (tb2.ty == Tclass && (cast(TypeClass)tb2).implicitConvToThroughAliasThis(tb1next))
goto Laliasthis;
// Append element
- if (exp.e2.checkPostblit(sc, tb2))
+ if (tb2.checkPostblit(exp.e2.loc, sc))
return setError();
if (checkNewEscape(sc, exp.e2, false))
@@ -11746,7 +12274,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}
else
{
- if (exp.e2.checkPostblit(sc, tb2))
+ if (tb2.checkPostblit(exp.e2.loc, sc))
return setError();
// Postblit call will be done in runtime helper function
}
@@ -11781,7 +12309,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}
else
{
- if (exp.e1.checkPostblit(sc, tb1))
+ if (tb1.checkPostblit(exp.e1.loc, sc))
return setError();
}
@@ -11836,7 +12364,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}
if (Type tbn = tb.nextOf())
{
- if (exp.checkPostblit(sc, tbn))
+ if (tbn.checkPostblit(exp.loc, sc))
return setError();
}
Type t1 = exp.e1.type.toBasetype();
@@ -13582,6 +14110,14 @@ private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc)
return exp;
}
+private bool checkDisabled(Dsymbol s, ref Loc loc, Scope* sc)
+{
+ if (auto d = s.isDeclaration())
+ return d.checkDisabled(loc, sc);
+
+ return false;
+}
+
/******************************
* Resolve properties, i.e. `e1.ident`, without seeing UFCS.
* Params:
@@ -13675,8 +14211,8 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag)
// if 's' is a tuple variable, the tuple is returned.
s = s.toAlias();
- exp.checkDeprecated(sc, s);
- exp.checkDisabled(sc, s);
+ s.checkDeprecated(exp.loc, sc);
+ s.checkDisabled(exp.loc, sc);
if (auto em = s.isEnumMember())
{
@@ -14466,12 +15002,112 @@ bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false)
return check(e, returnRef);
}
-/************************************************
- * Destructors are attached to VarDeclarations.
- * Hence, if expression returns a temp that needs a destructor,
- * make sure and create a VarDeclaration for that temp.
+/****************************************
+ * Resolve __FILE__, __LINE__, __MODULE__, __FUNCTION__, __PRETTY_FUNCTION__, __FILE_FULL_PATH__ to loc.
*/
-Expression addDtorHook(Expression e, Scope* sc)
+Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc)
+{
+ Expression visit(Expression exp)
+ {
+ if (auto unaExp = exp.isUnaExp())
+ {
+ unaExp.e1 = unaExp.e1.resolveLoc(loc, sc);
+ return unaExp;
+ }
+ exp.loc = loc;
+ return exp;
+ }
+
+ Expression visitCat(CatExp exp)
+ {
+ exp.e1 = exp.e1.resolveLoc(loc, sc);
+ exp.e2 = exp.e2.resolveLoc(loc, sc);
+ return exp;
+ }
+
+ Expression visitFileInit(FileInitExp exp)
+ {
+ //printf("FileInitExp::resolve() %s\n", exp.toChars());
+ const(char)* s;
+ if (exp.op == EXP.fileFullPath)
+ s = FileName.toAbsolute(loc.isValid() ? loc.filename : sc._module.srcfile.toChars());
+ else
+ s = loc.isValid() ? loc.filename : sc._module.ident.toChars();
+
+ Expression e = new StringExp(loc, s.toDString());
+ return e.expressionSemantic(sc);
+ }
+
+ Expression visitLineInit(LineInitExp _)
+ {
+ Expression e = new IntegerExp(loc, loc.linnum, Type.tint32);
+ return e.expressionSemantic(sc);
+ }
+
+ Expression visitModuleInit(ModuleInitExp _)
+ {
+ const auto s = (sc.callsc ? sc.callsc : sc)._module.toPrettyChars().toDString();
+ Expression e = new StringExp(loc, s);
+ return e.expressionSemantic(sc);
+ }
+
+ Expression visitFuncInit(FuncInitExp _)
+ {
+ const(char)* s;
+ if (sc.callsc && sc.callsc.func)
+ s = sc.callsc.func.Dsymbol.toPrettyChars();
+ else if (sc.func)
+ s = sc.func.Dsymbol.toPrettyChars();
+ else
+ s = "";
+ Expression e = new StringExp(loc, s.toDString());
+ return e.expressionSemantic(sc);
+ }
+
+ Expression visitPrettyFunc(PrettyFuncInitExp _)
+ {
+ FuncDeclaration fd = (sc.callsc && sc.callsc.func)
+ ? sc.callsc.func
+ : sc.func;
+
+ const(char)* s;
+ if (fd)
+ {
+ const funcStr = fd.Dsymbol.toPrettyChars();
+ OutBuffer buf;
+ functionToBufferWithIdent(fd.type.isTypeFunction(), buf, funcStr, fd.isStatic);
+ s = buf.extractChars();
+ }
+ else
+ {
+ s = "";
+ }
+
+ Expression e = new StringExp(loc, s.toDString());
+ e = e.expressionSemantic(sc);
+ e.type = Type.tstring;
+ return e;
+ }
+
+ switch(exp.op)
+ {
+ default: return visit(exp);
+ case EXP.concatenate: return visitCat(exp.isCatExp());
+ case EXP.file:
+ case EXP.fileFullPath: return visitFileInit(exp.isFileInitExp());
+ case EXP.line: return visitLineInit(exp.isLineInitExp);
+ case EXP.moduleString: return visitModuleInit(exp.isModuleInitExp());
+ case EXP.functionString: return visitFuncInit(exp.isFuncInitExp());
+ case EXP.prettyFunction: return visitPrettyFunc(exp.isPrettyFuncInitExp());
+ }
+}
+
+/************************************************
+ * Destructors are attached to VarDeclarations.
+ * Hence, if expression returns a temp that needs a destructor,
+ * make sure and create a VarDeclaration for that temp.
+ */
+Expression addDtorHook(Expression e, Scope* sc)
{
Expression visit(Expression exp)
{
@@ -14567,6 +15203,584 @@ Expression addDtorHook(Expression e, Scope* sc)
}
}
+/*******************************
+ * Try to convert an expression to be an lvalue.
+ *
+ * Give error if we're not an lvalue.
+ * Params:
+ * _this = expression to convert
+ * sc = scope
+ * Returns: converted expression, or `ErrorExp` on error
+*/
+extern(C++) Expression toLvalue(Expression _this, Scope* sc)
+{
+ return toLvalueImpl(_this, sc, _this);
+}
+
+// e = original un-lowered expression for error messages, in case of recursive calls
+private Expression toLvalueImpl(Expression _this, Scope* sc, Expression e) {
+ Expression visit(Expression _this)
+ {
+ // BinaryAssignExp does not have an EXP associated
+ // so it's treated on the default path.
+ // Lvalue-ness will be handled in glue :layer.
+ if (_this.isBinAssignExp())
+ return _this;
+ if (!_this.loc.isValid())
+ _this.loc = e.loc;
+
+ if (e.op == EXP.type)
+ error(_this.loc, "`%s` is a `%s` definition and cannot be modified", e.type.toChars(), e.type.kind());
+ else
+ error(_this.loc, "`%s` is not an lvalue and cannot be modified", e.toChars());
+
+ return ErrorExp.get();
+ }
+
+ Expression visitInteger(IntegerExp _this)
+ {
+ if (!_this.loc.isValid())
+ _this.loc = e.loc;
+ error(e.loc, "cannot modify constant `%s`", e.toChars());
+ return ErrorExp.get();
+ }
+
+ Expression visitThis(ThisExp _this)
+ {
+ if (_this.type.toBasetype().ty == Tclass)
+ {
+ // Class `this` is an rvalue; struct `this` is an lvalue.
+ return visit(_this);
+ }
+
+ return _this;
+ }
+
+ Expression visitString(StringExp _this)
+ {
+ //printf("StringExp::toLvalue(%s) type = %s\n", _this.toChars(), _this.type ? _this.type.toChars() : NULL);
+ return (_this.type && _this.type.toBasetype().ty == Tsarray) ? _this : visit(_this);
+ }
+
+ Expression visitStructLiteral(StructLiteralExp _this)
+ {
+ if (sc.flags & SCOPE.Cfile)
+ return _this; // C struct literals are lvalues
+ else
+ return visit(_this);
+ }
+
+ Expression visitTemplate(TemplateExp _this)
+ {
+ if (!_this.fd)
+ return visit(_this);
+
+ assert(sc);
+ return symbolToExp(_this.fd, _this.loc, sc, true);
+
+ }
+
+ Expression visitVar(VarExp _this)
+ {
+ auto var = _this.var;
+ if (var.storage_class & STC.manifest)
+ {
+ error(_this.loc, "manifest constant `%s` cannot be modified", var.toChars());
+ return ErrorExp.get();
+ }
+ if (var.storage_class & STC.lazy_ && !_this.delegateWasExtracted)
+ {
+ error(_this.loc, "lazy variable `%s` cannot be modified", var.toChars());
+ return ErrorExp.get();
+ }
+ if (var.ident == Id.ctfe)
+ {
+ error(_this.loc, "cannot modify compiler-generated variable `__ctfe`");
+ return ErrorExp.get();
+ }
+ if (var.ident == Id.dollar) // https://issues.dlang.org/show_bug.cgi?id=13574
+ {
+ error(_this.loc, "cannot modify operator `$`");
+ return ErrorExp.get();
+ }
+ return _this;
+ }
+
+ Expression visitDotVar(DotVarExp _this)
+ {
+ auto e1 = _this.e1;
+ auto var = _this.var;
+ //printf("DotVarExp::toLvalue(%s)\n", toChars());
+ if (sc && sc.flags & SCOPE.Cfile)
+ {
+ /* C11 6.5.2.3-3: A postfix expression followed by the '.' or '->' operator
+ * is an lvalue if the first expression is an lvalue.
+ */
+ if (!e1.isLvalue())
+ return visit(_this);
+ }
+ if (!_this.isLvalue())
+ return visit(_this);
+ if (e1.op == EXP.this_ && sc.ctorflow.fieldinit.length && !(sc.ctorflow.callSuper & CSX.any_ctor))
+ {
+ if (VarDeclaration vd = var.isVarDeclaration())
+ {
+ auto ad = vd.isMember2();
+ if (ad && ad.fields.length == sc.ctorflow.fieldinit.length)
+ {
+ foreach (i, f; ad.fields)
+ {
+ if (f == vd)
+ {
+ if (!(sc.ctorflow.fieldinit[i].csx & CSX.this_ctor))
+ {
+ /* If the address of vd is taken, assume it is thereby initialized
+ * https://issues.dlang.org/show_bug.cgi?id=15869
+ */
+ modifyFieldVar(_this.loc, sc, vd, e1);
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ return _this;
+ }
+
+ Expression visitCall(CallExp _this)
+ {
+ if (_this.isLvalue())
+ return _this;
+ return visit(_this);
+ }
+
+ Expression visitCast(CastExp _this)
+ {
+ if (sc && sc.flags & SCOPE.Cfile)
+ {
+ /* C11 6.5.4-5: A cast does not yield an lvalue.
+ */
+ return visit(_this);
+ }
+ if (_this.isLvalue())
+ return _this;
+ return visit(_this);
+ }
+
+ Expression visitVectorArray(VectorArrayExp _this)
+ {
+ _this.e1 = _this.e1.toLvalueImpl(sc, e);
+ return _this;
+ }
+
+ Expression visitSlice(SliceExp _this)
+ {
+ //printf("SliceExp::toLvalue(%s) _this.type = %s\n", _this.toChars(), _this.type ? _this.type.toChars() : NULL);
+ return (_this.type && _this.type.toBasetype().ty == Tsarray) ? _this : visit(_this);
+ }
+
+ Expression visitArray(ArrayExp _this)
+ {
+ if (_this.type && _this.type.toBasetype().ty == Tvoid)
+ error(_this.loc, "`void`s have no value");
+ return _this;
+ }
+
+ Expression visitComma(CommaExp _this)
+ {
+ _this.e2 = _this.e2.toLvalue(sc);
+ return _this;
+ }
+
+ Expression visitDelegatePointer(DelegatePtrExp _this)
+ {
+ _this.e1 = _this.e1.toLvalueImpl(sc, e);
+ return _this;
+ }
+
+ Expression visitDelegateFuncptr(DelegateFuncptrExp _this)
+ {
+ _this.e1 = _this.e1.toLvalueImpl(sc, e);
+ return _this;
+ }
+
+ Expression visitIndex(IndexExp _this)
+ {
+ if (_this.isLvalue())
+ return _this;
+ return visit(_this);
+ }
+
+ Expression visitAssign(AssignExp _this)
+ {
+ if (_this.e1.op == EXP.slice || _this.e1.op == EXP.arrayLength)
+ {
+ return visit(_this);
+ }
+
+ /* In front-end level, AssignExp should make an lvalue of e1.
+ * Taking the address of e1 will be handled in low level layer,
+ * so this function does nothing.
+ */
+ return _this;
+ }
+
+ Expression visitCond(CondExp _this)
+ {
+ // convert (econd ? e1 : e2) to *(econd ? &e1 : &e2)
+ CondExp e = cast(CondExp)(_this.copy());
+ e.e1 = _this.e1.toLvalue(sc).addressOf();
+ e.e2 = _this.e2.toLvalue(sc).addressOf();
+ e.type = _this.type.pointerTo();
+ return new PtrExp(_this.loc, e, _this.type);
+
+ }
+
+ switch(_this.op)
+ {
+ default: return visit(_this);
+
+ case EXP.int64: return visitInteger(_this.isIntegerExp());
+ case EXP.error: return _this;
+ case EXP.identifier: return _this;
+ case EXP.dSymbol: return _this;
+ case EXP.this_: return visitThis(_this.isThisExp());
+ case EXP.super_: return visitThis(_this.isSuperExp());
+ case EXP.string_: return visitString(_this.isStringExp());
+ case EXP.structLiteral: return visitStructLiteral(_this.isStructLiteralExp());
+ case EXP.template_: return visitTemplate(_this.isTemplateExp());
+ case EXP.variable: return visitVar(_this.isVarExp());
+ case EXP.overloadSet: return _this;
+ case EXP.dotVariable: return visitDotVar(_this.isDotVarExp());
+ case EXP.call: return visitCall(_this.isCallExp());
+ case EXP.star: return _this;
+ case EXP.cast_: return visitCast(_this.isCastExp());
+ case EXP.vectorArray: return visitVectorArray(_this.isVectorArrayExp());
+ case EXP.slice: return visitSlice(_this.isSliceExp());
+ case EXP.array: return visitArray(_this.isArrayExp());
+ case EXP.comma: return visitComma(_this.isCommaExp());
+ case EXP.delegatePointer: return visitDelegatePointer(_this.isDelegatePtrExp());
+ case EXP.delegateFunctionPointer: return visitDelegateFuncptr(_this.isDelegateFuncptrExp());
+ case EXP.index: return visitIndex(_this.isIndexExp());
+ case EXP.construct: return visitAssign(_this.isConstructExp());
+ case EXP.loweredAssignExp: return visitAssign(_this.isLoweredAssignExp());
+ case EXP.blit: return visitAssign(_this.isBlitExp());
+ case EXP.assign: return visitAssign(_this.isAssignExp());
+ case EXP.question: return visitCond(_this.isCondExp());
+ }
+}
+
+/***************************************
+ * Parameters:
+ * sc: scope
+ * flag: 1: do not issue error message for invalid modification
+ 2: the exp is a DotVarExp and a subfield of the leftmost
+ variable is modified
+ * Returns:
+ * Whether the type is modifiable
+ */
+Modifiable checkModifiable(Expression exp, Scope* sc, ModifyFlags flag = ModifyFlags.none)
+{
+ switch(exp.op)
+ {
+ case EXP.variable:
+ auto varExp = cast(VarExp)exp;
+
+ //printf("VarExp::checkModifiable %s", varExp.toChars());
+ assert(varExp.type);
+ return varExp.var.checkModify(varExp.loc, sc, null, flag);
+
+ case EXP.dotVariable:
+ auto dotVarExp = cast(DotVarExp)exp;
+
+ //printf("DotVarExp::checkModifiable %s %s\n", dotVarExp.toChars(), dotVarExp.type.toChars());
+ if (dotVarExp.e1.op == EXP.this_)
+ return dotVarExp.var.checkModify(dotVarExp.loc, sc, dotVarExp.e1, flag);
+
+ /* https://issues.dlang.org/show_bug.cgi?id=12764
+ * If inside a constructor and an expression of type `this.field.var`
+ * is encountered, where `field` is a struct declaration with
+ * default construction disabled, we must make sure that
+ * assigning to `var` does not imply that `field` was initialized
+ */
+ if (sc.func && sc.func.isCtorDeclaration())
+ {
+ // if inside a constructor scope and e1 of this DotVarExp
+ // is another DotVarExp, then check if the leftmost expression is a `this` identifier
+ if (auto dve = dotVarExp.e1.isDotVarExp())
+ {
+ // Iterate the chain of DotVarExp to find `this`
+ // Keep track whether access to fields was limited to union members
+ // s.t. one can initialize an entire struct inside nested unions
+ // (but not its members)
+ bool onlyUnion = true;
+ while (true)
+ {
+ auto v = dve.var.isVarDeclaration();
+ assert(v);
+
+ // Accessing union member?
+ auto t = v.type.isTypeStruct();
+ if (!t || !t.sym.isUnionDeclaration())
+ onlyUnion = false;
+
+ // Another DotVarExp left?
+ if (!dve.e1 || dve.e1.op != EXP.dotVariable)
+ break;
+
+ dve = cast(DotVarExp) dve.e1;
+ }
+
+ if (dve.e1.op == EXP.this_)
+ {
+ scope v = dve.var.isVarDeclaration();
+ /* if v is a struct member field with no initializer, no default construction
+ * and v wasn't intialized before
+ */
+ if (v && v.isField() && !v._init && !v.ctorinit)
+ {
+ if (auto ts = v.type.isTypeStruct())
+ {
+ if (ts.sym.noDefaultCtor)
+ {
+ /* checkModify will consider that this is an initialization
+ * of v while it is actually an assignment of a field of v
+ */
+ scope modifyLevel = v.checkModify(dotVarExp.loc, sc, dve.e1, !onlyUnion ? (flag | ModifyFlags.fieldAssign) : flag);
+ if (modifyLevel == Modifiable.initialization)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=22118
+ // v is a union type field that was assigned
+ // a variable, therefore it counts as initialization
+ if (v.ctorinit)
+ return Modifiable.initialization;
+
+ return Modifiable.yes;
+ }
+ return modifyLevel;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ //printf("\te1 = %s\n", e1.toChars());
+ return dotVarExp.e1.checkModifiable(sc, flag);
+
+ case EXP.star:
+ auto ptrExp = cast(PtrExp)exp;
+ if (auto se = ptrExp.e1.isSymOffExp())
+ {
+ return se.var.checkModify(ptrExp.loc, sc, null, flag);
+ }
+ else if (auto ae = ptrExp.e1.isAddrExp())
+ {
+ return ae.e1.checkModifiable(sc, flag);
+ }
+ return Modifiable.yes;
+
+ case EXP.slice:
+ auto sliceExp = cast(SliceExp)exp;
+
+ //printf("SliceExp::checkModifiable %s\n", sliceExp.toChars());
+ auto e1 = sliceExp.e1;
+ if (e1.type.ty == Tsarray || (e1.op == EXP.index && e1.type.ty != Tarray) || e1.op == EXP.slice)
+ {
+ return e1.checkModifiable(sc, flag);
+ }
+ return Modifiable.yes;
+
+ case EXP.comma:
+ return (cast(CommaExp)exp).e2.checkModifiable(sc, flag);
+
+ case EXP.index:
+ auto indexExp = cast(IndexExp)exp;
+ auto e1 = indexExp.e1;
+ if (e1.type.ty == Tsarray ||
+ e1.type.ty == Taarray ||
+ (e1.op == EXP.index && e1.type.ty != Tarray) ||
+ e1.op == EXP.slice)
+ {
+ return e1.checkModifiable(sc, flag);
+ }
+ return Modifiable.yes;
+
+ case EXP.question:
+ auto condExp = cast(CondExp)exp;
+ if (condExp.e1.checkModifiable(sc, flag) != Modifiable.no
+ && condExp.e2.checkModifiable(sc, flag) != Modifiable.no)
+ return Modifiable.yes;
+ return Modifiable.no;
+
+ default:
+ return exp.type ? Modifiable.yes : Modifiable.no; // default modifiable
+ }
+}
+
+/**
+ * Similar to `toLvalue`, but also enforce it is mutable or raise an error.
+ * Params:
+ * _this = Expression to convert
+ * sc = scope
+ * Returns: `_this` converted to an lvalue, or an `ErrorExp`
+ */
+extern(C++) Expression modifiableLvalue(Expression _this, Scope* sc)
+{
+ return modifiableLvalueImpl(_this, sc, _this);
+}
+
+// e = original / un-lowered expression to print in error messages
+private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression e)
+{
+ Expression visit(Expression exp)
+ {
+ //printf("Expression::modifiableLvalue() %s, type = %s\n", exp.toChars(), exp.type.toChars());
+ // See if this expression is a modifiable lvalue (i.e. not const)
+ if (exp.isBinAssignExp())
+ return exp.toLvalue(sc);
+
+ auto type = exp.type;
+ if (checkModifiable(exp, sc) == Modifiable.yes)
+ {
+ assert(type);
+ if (!type.isMutable())
+ {
+ if (auto dve = exp.isDotVarExp())
+ {
+ if (isNeedThisScope(sc, dve.var))
+ for (Dsymbol s = sc.func; s; s = s.toParentLocal())
+ {
+ FuncDeclaration ff = s.isFuncDeclaration();
+ if (!ff)
+ break;
+ if (!ff.type.isMutable)
+ {
+ error(exp.loc, "cannot modify `%s` in `%s` function", exp.toChars(), MODtoChars(type.mod));
+ return ErrorExp.get();
+ }
+ }
+ }
+ error(exp.loc, "cannot modify `%s` expression `%s`", MODtoChars(type.mod), exp.toChars());
+ return ErrorExp.get();
+ }
+ else if (!type.isAssignable())
+ {
+ error(exp.loc, "cannot modify struct instance `%s` of type `%s` because it contains `const` or `immutable` members",
+ exp.toChars(), type.toChars());
+ return ErrorExp.get();
+ }
+ }
+ return exp.toLvalueImpl(sc, e);
+ }
+
+ Expression visitString(StringExp exp)
+ {
+ error(exp.loc, "cannot modify string literal `%s`", exp.toChars());
+ return ErrorExp.get();
+ }
+
+ Expression visitVar(VarExp exp)
+ {
+ //printf("VarExp::modifiableLvalue('%s')\n", exp.var.toChars());
+ if (exp.var.storage_class & STC.manifest)
+ {
+ error(exp.loc, "cannot modify manifest constant `%s`", exp.toChars());
+ return ErrorExp.get();
+ }
+ // See if this expression is a modifiable lvalue (i.e. not const)
+ return visit(exp);
+ }
+
+ Expression visitPtr(PtrExp exp)
+ {
+ //printf("PtrExp::modifiableLvalue() %s, type %s\n", exp.toChars(), exp.type.toChars());
+ Declaration var;
+ auto e1 = exp.e1;
+ if (auto se = e1.isSymOffExp())
+ var = se.var;
+ else if (auto ve = e1.isVarExp())
+ var = ve.var;
+ if (var && var.type.isFunction_Delegate_PtrToFunction())
+ {
+ if (var.type.isTypeFunction())
+ error(exp.loc, "function `%s` is not an lvalue and cannot be modified", var.toChars());
+ else
+ error(exp.loc, "function pointed to by `%s` is not an lvalue and cannot be modified", var.toChars());
+ return ErrorExp.get();
+ }
+ return visit(exp);
+ }
+
+ Expression visitSlice(SliceExp exp)
+ {
+ error(exp.loc, "slice expression `%s` is not a modifiable lvalue", exp.toChars());
+ return exp;
+ }
+
+ Expression visitComma(CommaExp exp)
+ {
+ exp.e2 = exp.e2.modifiableLvalueImpl(sc, e);
+ return exp;
+ }
+
+ Expression visitDelegatePtr(DelegatePtrExp exp)
+ {
+ if (sc.setUnsafe(false, exp.loc, "cannot modify delegate pointer in `@safe` code `%s`", exp))
+ {
+ return ErrorExp.get();
+ }
+ return visit(exp);
+ }
+
+ Expression visitDelegateFuncptr(DelegateFuncptrExp exp)
+ {
+ if (sc.setUnsafe(false, exp.loc, "cannot modify delegate function pointer in `@safe` code `%s`", exp))
+ {
+ return ErrorExp.get();
+ }
+ return visit(exp);
+ }
+
+ Expression visitIndex(IndexExp exp)
+ {
+ //printf("IndexExp::modifiableLvalue(%s)\n", exp.toChars());
+ Expression ex = exp.markSettingAAElem();
+ if (ex.op == EXP.error)
+ return ex;
+
+ return visit(exp);
+ }
+
+ Expression visitCond(CondExp exp)
+ {
+ if (!exp.e1.isLvalue() && !exp.e2.isLvalue())
+ {
+ error(exp.loc, "conditional expression `%s` is not a modifiable lvalue", exp.toChars());
+ return ErrorExp.get();
+ }
+ exp.e1 = exp.e1.modifiableLvalue(sc);
+ exp.e2 = exp.e2.modifiableLvalue(sc);
+ return exp.toLvalue(sc);
+ }
+
+ switch(_this.op)
+ {
+ default: return visit(_this);
+ case EXP.string_: return visitString(_this.isStringExp());
+ case EXP.variable: return visitVar(_this.isVarExp());
+ case EXP.star: return visitPtr(_this.isPtrExp());
+ case EXP.slice: return visitSlice(_this.isSliceExp());
+ case EXP.comma: return visitComma(_this.isCommaExp());
+ case EXP.delegatePointer: return visitDelegatePtr(_this.isDelegatePtrExp());
+ case EXP.delegateFunctionPointer: return visitDelegateFuncptr(_this.isDelegateFuncptrExp());
+ case EXP.index: return visitIndex(_this.isIndexExp());
+ case EXP.question: return visitCond(_this.isCondExp());
+ }
+}
+
+
/****************************************************
* Determine if `exp`, which gets its address taken, can do so safely.
* Params:
@@ -14676,15 +15890,12 @@ bool checkAddressable(Expression e, Scope* sc)
*/
private bool checkFunctionAttributes(Expression exp, Scope* sc, FuncDeclaration f)
{
- with(exp)
- {
- bool error = checkDisabled(sc, f);
- error |= checkDeprecated(sc, f);
- error |= checkPurity(sc, f);
- error |= checkSafety(sc, f);
- error |= checkNogc(sc, f);
- return error;
- }
+ bool error = f.checkDisabled(exp.loc, sc);
+ error |= f.checkDeprecated(exp.loc, sc);
+ error |= f.checkPurity(exp.loc, sc);
+ error |= f.checkSafety(exp.loc, sc);
+ error |= f.checkNogc(exp.loc, sc);
+ return error;
}
/*******************************
@@ -250,6 +250,12 @@ extern (C++) struct Param
const(char)[] resfile;
const(char)[] exefile;
const(char)[] mapfile;
+
+ ///
+ bool parsingUnittestsRequired()
+ {
+ return useUnitTests || ddoc.doOutput || dihdr.doOutput;
+ }
}
enum mars_ext = "d"; // for D source files
@@ -41,6 +41,7 @@ import dmd.identifier;
import dmd.init;
import dmd.mtype;
import dmd.nspace;
+import dmd.optimize;
import dmd.parse;
import dmd.root.complex;
import dmd.root.ctfloat;
@@ -302,7 +302,7 @@ Ldone:
extern (C++) public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc)
{
//printf("GccAsmStatement.semantic()\n");
- const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput;
+ const bool doUnittests = global.params.parsingUnittestsRequired();
scope p = new Parser!ASTCodegen(sc._module, ";", false, global.errorSink, &global.compileEnv, doUnittests);
// Make a safe copy of the token list before parsing.
@@ -341,7 +341,7 @@ extern (C++) public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc)
e = e.expressionSemantic(sc);
// Check argument is a valid lvalue/rvalue.
if (i < s.outputargs)
- e = e.modifiableLvalue(sc, null);
+ e = e.modifiableLvalue(sc);
else if (e.checkValue())
e = ErrorExp.get();
(*s.args)[i] = e;
@@ -38,6 +38,7 @@ import dmd.init;
import dmd.location;
import dmd.mtype;
import dmd.opover;
+import dmd.optimize;
import dmd.statement;
import dmd.target;
import dmd.tokens;
@@ -3258,6 +3258,24 @@ class Lexer
scanloc.linnum = scanloc.linnum + 1;
line = p;
}
+
+ /****************************
+ * Print the tokens from the current `token` to the end,
+ * while not advancing the parser forward.
+ * Useful for debugging.
+ */
+ void printRestOfTokens()
+ {
+ auto tk = &token;
+ while (1)
+ {
+ printf("%s ", (*tk).toChars());
+ if (tk.value == TOK.endOfFile)
+ break;
+ tk = peek(tk);
+ }
+ printf("\n");
+ }
}
@@ -34,6 +34,7 @@ import dmd.id;
import dmd.identifier;
import dmd.location;
import dmd.mtype;
+import dmd.optimize;
import dmd.statement;
import dmd.tokens;
import dmd.typesem;
@@ -272,9 +272,9 @@ package void setLengthVarIfKnown(VarDeclaration lengthVar, Type type)
* Returns:
* Constant folded version of `e`
*/
-Expression Expression_optimize(Expression e, int result, bool keepLvalue)
+Expression optimize(Expression e, int result, bool keepLvalue = false)
{
- //printf("Expression_optimize() e: %s result: %d keepLvalue %d\n", e.toChars(), result, keepLvalue);
+ //printf("optimize() e: %s result: %d keepLvalue %d\n", e.toChars(), result, keepLvalue);
Expression ret = e;
void errorReturn()
@@ -288,7 +288,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue)
{
if (!e)
return false;
- Expression ex = Expression_optimize(e, flags, keepLvalue);
+ Expression ex = optimize(e, flags, keepLvalue);
if (ex.op == EXP.error)
{
ret = ex; // store error result
@@ -591,7 +591,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue)
Expression add = new AddExp(ae.loc, ex, new IntegerExp(ae.e2.loc, offset, ae.e2.type));
add.type = e.type;
- ret = Expression_optimize(add, result, keepLvalue);
+ ret = optimize(add, result, keepLvalue);
return;
}
}
@@ -1239,7 +1239,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue)
ret = new CastExp(e.loc, ret, Type.tvoid);
ret.type = e.type;
}
- ret = Expression_optimize(ret, result, false);
+ ret = optimize(ret, result, false);
return;
}
expOptimize(e.e2, WANTvalue);
@@ -1294,7 +1294,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue)
// `["c"] ~ "a" ~ "b"` becoming `["c"] ~ "ab"`
scope CatExp cex = new CatExp(e.loc, ce1.e2, e.e2);
cex.type = e.type;
- Expression ex = Expression_optimize(cex, result, false);
+ Expression ex = optimize(cex, result, false);
if (ex != cex)
{
e.e1 = ce1.e1;
@@ -1323,9 +1323,9 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue)
return;
const opt = e.econd.toBool();
if (opt.hasValue(true))
- ret = Expression_optimize(e.e1, result, keepLvalue);
+ ret = optimize(e.e1, result, keepLvalue);
else if (opt.hasValue(false))
- ret = Expression_optimize(e.e2, result, keepLvalue);
+ ret = optimize(e.e2, result, keepLvalue);
else
{
expOptimize(e.e1, result, keepLvalue);
@@ -54,6 +54,7 @@ import dmd.nspace;
import dmd.ob;
import dmd.objc;
import dmd.opover;
+import dmd.optimize;
import dmd.parse;
import dmd.root.filename;
import dmd.common.outbuffer;
@@ -917,7 +918,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
if (f.isref)
{
// Function returns a reference
- exp = exp.toLvalue(sc2, exp);
+ exp = exp.toLvalue(sc2);
checkReturnEscapeRef(sc2, exp, false);
exp = exp.optimize(WANTvalue, /*keepLvalue*/ true);
}
@@ -55,6 +55,7 @@ import dmd.location;
import dmd.mtype;
import dmd.mustuse;
import dmd.nogc;
+import dmd.optimize;
import dmd.opover;
import dmd.parse;
import dmd.common.outbuffer;
@@ -4887,7 +4888,7 @@ private Statements* flatten(Statement statement, Scope* sc)
const len = buf.length;
buf.writeByte(0);
const str = buf.extractSlice()[0 .. len];
- const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput;
+ const bool doUnittests = global.params.parsingUnittestsRequired();
auto loc = adjustLocForMixin(str, cs.loc, global.params.mixinOut);
scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
p.transitionIn = global.params.v.vin;
@@ -22,6 +22,7 @@ import dmd.expressionsem;
import dmd.globals;
import dmd.identifier;
import dmd.mtype;
+import dmd.optimize;
import dmd.root.array;
import dmd.common.outbuffer;
import dmd.tokens;
@@ -42,6 +42,7 @@ import dmd.identifier;
import dmd.location;
import dmd.mtype;
import dmd.nogc;
+import dmd.optimize;
import dmd.parse;
import dmd.root.array;
import dmd.root.speller;
@@ -1875,16 +1876,24 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
return dimError(1);
auto o = (*e.args)[0];
- Type t = isType(o);
- AggregateDeclaration ad = t ? isAggregate(t) : null;
- // Interfaces don't have an init symbol and hence cause linker errors
- if (!ad || ad.isInterfaceDeclaration())
+ ErrorExp badArgument()
{
error(e.loc, "struct / class type expected as argument to __traits(initSymbol) instead of `%s`", o.toChars());
return ErrorExp.get();
}
+ Type t = isType(o);
+
+ if (!t || t.isTypeEnum())
+ return badArgument();
+
+ AggregateDeclaration ad = isAggregate(t);
+
+ // Interfaces don't have an init symbol and hence cause linker errors
+ if (!ad || ad.isInterfaceDeclaration())
+ return badArgument();
+
Declaration d = new SymbolDeclaration(ad.loc, ad);
d.type = Type.tvoid.arrayOf().constOf();
d.storage_class |= STC.rvalue;
@@ -53,6 +53,7 @@ import dmd.visitor;
import dmd.mtype;
import dmd.objc;
import dmd.opover;
+import dmd.optimize;
import dmd.parse;
import dmd.root.complex;
import dmd.root.ctfloat;
@@ -1098,7 +1099,7 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
if (isRefOrOut && !isAuto &&
!(global.params.previewIn && (fparam.storageClass & STC.in_)) &&
global.params.rvalueRefParam != FeatureState.enabled)
- e = e.toLvalue(sc, e);
+ e = e.toLvalue(sc);
fparam.defaultArg = e;
return (e.op != EXP.error);
@@ -3748,7 +3749,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag
}
// check before alias resolution; the alias itself might be deprecated!
if (s.isAliasDeclaration)
- e.checkDeprecated(sc, s);
+ s.checkDeprecated(e.loc, sc);
s = s.toAlias();
if (auto em = s.isEnumMember())
@@ -5009,7 +5010,7 @@ RootObject compileTypeMixin(TypeMixin tm, ref const Loc loc, Scope* sc)
const len = buf.length;
buf.writeByte(0);
const str = buf.extractSlice()[0 .. len];
- const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput;
+ const bool doUnittests = global.params.parsingUnittestsRequired();
auto locm = adjustLocForMixin(str, loc, global.params.mixinOut);
scope p = new Parser!ASTCodegen(locm, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
p.transitionIn = global.params.v.vin;
@@ -79,14 +79,11 @@ version (Win64)
{
void checkReal(in real p)
{
- // ref for x87 real, value for double-precision real
- static assert(__traits(isRef, p) == (real.sizeof > 8));
}
struct RGB { ubyte r, g, b; }
void checkNonPowerOf2(in RGB p)
{
- static assert(__traits(isRef, p));
}
}
else version (X86_64) // Posix x86_64
@@ -94,7 +91,6 @@ else version (X86_64) // Posix x86_64
struct Empty {} // 1 dummy byte passed on the stack
void checkEmptyStruct(in Empty p)
{
- static assert(!__traits(isRef, p));
}
static if (is(__vector(double[4])))
@@ -102,7 +98,6 @@ else version (X86_64) // Posix x86_64
struct AvxVectorWrapper { __vector(double[4]) a; } // 256 bits
void checkAvxVector(in AvxVectorWrapper p)
{
- static assert(!__traits(isRef, p));
}
}
}
@@ -111,6 +106,5 @@ else version (AArch64)
alias HVA = __vector(float[4])[4]; // can be passed in 4 vector registers
void checkHVA(in HVA p)
{
- static assert(!__traits(isRef, p));
}
}
@@ -1,10 +1,14 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail212.d(14): Error: function `fail212.S.bar` without `this` cannot be `const`
+fail_compilation/fail212.d(10): Error: function `fail212.baz` without `this` cannot be `const`
+fail_compilation/fail212.d(10): did you mean to use `const(int)` as the return type?
+fail_compilation/fail212.d(18): Error: function `fail212.S.bar` without `this` cannot be `const`
---
*/
+const int baz();
+
struct S
{
void foo() const
@@ -154,27 +154,25 @@ struct WithDtor
@safe pure nothrow @nogc:
// By value
-void testin1(in uint p) { static assert(!__traits(isRef, p)); }
+void testin1(in uint p) { }
// By ref because of size
-void testin2(in ulong[64] p) { static assert(__traits(isRef, p)); }
+void testin2(in ulong[64] p) { }
// By value or ref depending on size (or structs always passed by reference)
-void testin3(in ValueT p) { static assert(!__traits(isRef, p) || true); }
-void testin3(in RefT p) { static assert(__traits(isRef, p)); }
+void testin3(in ValueT p) { }
+void testin3(in RefT p) { }
// By ref because of size (or arrays always passed by reference)
-void testin4(in ValueT[64] p) { static assert(__traits(isRef, p)); }
-void testin4(in RefT[4] p) { static assert(__traits(isRef, p)); }
+void testin4(in ValueT[64] p) { }
+void testin4(in RefT[4] p) { }
// By ref because of non-copyability
-void testin5(in NonCopyable noncopy) { static assert(__traits(isRef, noncopy)); }
-static assert(testin5.mangleof == "_D9previewin7testin5FNaNbNiNfIKSQBe11NonCopyableZv"); // incl. `ref`
+void testin5(in NonCopyable noncopy) { }
// By ref because of postblit
-void testin6(in WithPostblit withpostblit) { static assert(__traits(isRef, withpostblit)); }
+void testin6(in WithPostblit withpostblit) { }
// By ref because of copy ctor
-void testin7(in WithCopyCtor withcopy) { static assert(__traits(isRef, withcopy)); }
+void testin7(in WithCopyCtor withcopy) { }
// By ref because of dtor
void testin8(in WithDtor withdtor, scope bool* isTestOver)
{
- static assert(__traits(isRef, withdtor));
if (isTestOver)
*isTestOver = true;
}
@@ -1,4 +1,4 @@
-1c98326e787e504d9045004e593273ec99b13121
+fc06c514a8c4492f60fc89b8c4f857e6932fbcbd
The first line of this file holds the git revision number of the last
merge done from the dlang/phobos repository.
@@ -594,6 +594,10 @@ if (!is(immutable T == immutable bool))
assert(capacity == values.length); // We check that reserve has been called before the loop.
}
+ /// ditto
+ // needed when T is an array and only one argument is passed
+ this(T single) { __ctor!T(single); }
+
/**
* Constructor taking an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
*/
@@ -1282,6 +1286,13 @@ if (!is(immutable T == immutable bool))
}
}
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ auto a = Array!string("test");
+ assert(a[].equal(["test"]));
+}
+
@safe unittest
{
// https://issues.dlang.org/show_bug.cgi?id=13621
@@ -54,6 +54,7 @@ $(UL
$(LI `trace`)
$(LI `info`)
$(LI `warning`)
+ #(LI `error`)
$(LI `critical`)
$(LI `fatal`)
)
@@ -1015,12 +1015,27 @@ See_Also:
enum bool isForwardRange(R) = isInputRange!R
&& is(typeof((R r) { return r.save; } (R.init)) == R);
+/// ditto
+enum bool isForwardRange(R, E) =
+ .isForwardRange!R && isQualifierConvertible!(ElementType!R, E);
+
///
@safe unittest
{
static assert(!isForwardRange!(int));
static assert( isForwardRange!(int[]));
static assert( isForwardRange!(inout(int)[]));
+
+ static assert( isForwardRange!(int[], const int));
+ static assert(!isForwardRange!(int[], immutable int));
+
+ static assert(!isForwardRange!(const(int)[], int));
+ static assert( isForwardRange!(const(int)[], const int));
+ static assert(!isForwardRange!(const(int)[], immutable int));
+
+ static assert(!isForwardRange!(immutable(int)[], int));
+ static assert( isForwardRange!(immutable(int)[], const int));
+ static assert( isForwardRange!(immutable(int)[], immutable int));
}
@safe unittest