diff --git a/src/dmd/dcast.d b/src/dmd/dcast.d index 98ca59584fa0..b92675fb9530 100644 --- a/src/dmd/dcast.d +++ b/src/dmd/dcast.d @@ -261,7 +261,7 @@ MATCH implicitConvTo(Expression e, Type t) e.type = Type.terror; } - Expression ex = e.optimize(WANTvalue); + Expression ex = e.optimize(WANTvalue | WANTnoctfe); if (ex.type.equals(t)) { result = MATCH.exact; diff --git a/src/dmd/dinterpret.d b/src/dmd/dinterpret.d index 8806f2442e0f..fcd0ff66158e 100644 --- a/src/dmd/dinterpret.d +++ b/src/dmd/dinterpret.d @@ -2084,8 +2084,9 @@ public: if (v._init.isVoidInitializer()) { // var should have been initialized when it was created + // But this can happen with __swap=void generation error(loc, "CTFE internal error: trying to access uninitialized var"); - assert(0); + return CTFEExp.cantexp; } e = v._init.initializerToExpression(); } @@ -6199,10 +6200,10 @@ public: } // We can't use getField, because it makes a copy - if (ex.op == TOK.classReference) + if (auto cre = ex.isClassReferenceExp()) { - se = (cast(ClassReferenceExp)ex).value; - i = (cast(ClassReferenceExp)ex).findFieldIndexByName(v); + se = cre.value; + i = cre.findFieldIndexByName(v); } else if (ex.op == TOK.typeid_) { @@ -6223,7 +6224,7 @@ public: } else { - se = cast(StructLiteralExp)ex; + se = ex.isStructLiteralExp(); i = findFieldIndexByName(se.sd, v); } if (i == -1) @@ -6253,6 +6254,7 @@ public: return; } + assert(i < se.elements.dim); result = (*se.elements)[i]; if (!result) { @@ -6260,6 +6262,8 @@ public: result = CTFEExp.cantexp; return; } + + assert(result.op != 0xFF); if (auto vie = result.isVoidInitExp()) { const s = vie.var.toChars(); @@ -6662,6 +6666,8 @@ private Expression copyRegionExp(Expression e) if (!e) return e; + assert(e.op != 0xFF); + static void copyArray(Expressions* elems) { foreach (ref e; *elems) diff --git a/src/dmd/expression.d b/src/dmd/expression.d index e9ed2c296abd..eaa0a0e25354 100644 --- a/src/dmd/expression.d +++ b/src/dmd/expression.d @@ -641,6 +641,7 @@ enum OwnedBy : ubyte enum WANTvalue = 0; // default enum WANTexpand = 1; // expand const/immutable variables if possible +enum WANTnoctfe = 2; // do not run CTFE on pure functions /*********************************************************** * http://dlang.org/spec/expression.html#expression diff --git a/src/dmd/optimize.d b/src/dmd/optimize.d index 1b96bd3fe3b8..a099d99ffc2b 100644 --- a/src/dmd/optimize.d +++ b/src/dmd/optimize.d @@ -13,10 +13,12 @@ module dmd.optimize; import core.stdc.stdio; +import dmd.printast; import dmd.constfold; import dmd.ctfeexpr; import dmd.dclass; import dmd.declaration; +import dmd.dinterpret; import dmd.dsymbol; import dmd.dsymbolsem; import dmd.errors; @@ -270,12 +272,14 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) Expression ret; private const int result; + private const int want; private const bool keepLvalue; extern (D) this(Expression e, int result, bool keepLvalue) { this.ret = e; // default result is original expression this.result = result; + this.want = result & WANTnoctfe; this.keepLvalue = keepLvalue; } @@ -331,10 +335,10 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) override void visit(TupleExp e) { - expOptimize(e.e0, WANTvalue); + expOptimize(e.e0, want); for (size_t i = 0; i < e.exps.dim; i++) { - expOptimize((*e.exps)[i], WANTvalue); + expOptimize((*e.exps)[i], want); } } @@ -575,42 +579,84 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) override void visit(NewExp e) { - expOptimize(e.thisexp, WANTvalue); + expOptimize(e.thisexp, want); // Optimize parameters if (e.newargs) { for (size_t i = 0; i < e.newargs.dim; i++) { - expOptimize((*e.newargs)[i], WANTvalue); + expOptimize((*e.newargs)[i], want); } } if (e.arguments) { for (size_t i = 0; i < e.arguments.dim; i++) { - expOptimize((*e.arguments)[i], WANTvalue); + expOptimize((*e.arguments)[i], want); } } } override void visit(CallExp e) { - //printf("CallExp::optimize(result = %d) %s\n", result, e.toChars()); + enum log = false; + //if (log) printf("CallExp::optimize(result = %d) %s\n", result, e.toChars()); // Optimize parameters with keeping lvalue-ness if (expOptimize(e.e1, result)) return; + Type t1 = e.e1.type.toBasetype(); + if (t1.ty == Tdelegate) + t1 = t1.nextOf(); + auto tf = t1.isTypeFunction(); + bool tryCTFE = !(want & WANTnoctfe) && + (tf.purity == PURE.strong || tf.purity == PURE.weak || tf.purity == PURE.const_); + + /* A @nogc function that tries to return an array literal is going + * go cause an error in checkGC() + */ + if (tf.isnogc) + { + const ty = e.type.toBasetype().ty; + if (ty == Tarray || ty == Taarray) + tryCTFE = false; + } + if (e.arguments) { - Type t1 = e.e1.type.toBasetype(); - if (t1.ty == Tdelegate) - t1 = t1.nextOf(); - assert(t1.ty == Tfunction); - TypeFunction tf = cast(TypeFunction)t1; - for (size_t i = 0; i < e.arguments.dim; i++) + foreach (i; 0 .. e.arguments.dim) { Parameter p = tf.parameterList[i]; bool keep = p && p.isReference(); - expOptimize((*e.arguments)[i], WANTvalue, keep); + expOptimize((*e.arguments)[i], want, keep); + auto ea = (*e.arguments)[i]; + + /* Attempting CTFE is slow, so try to quickly eliminate + * cases where it won't work anyway + */ + tryCTFE = tryCTFE && !keep && + (ea.op == TOK.int64 || + ea.op == TOK.null_ || + ea.op == TOK.string_ || + ea.op == TOK.arrayLiteral || + ea.op == TOK.assocArrayLiteral || + ea.op == TOK.structLiteral || + ea.op == TOK.float64 || + ea.op == TOK.complex80); + } + } + if (tryCTFE) + { + if (log) printf("trying CTFE %d %s %s\n", tf.purity, tf.toChars(), e.toChars()); + const gaggedErrorsSave = global.startGagging(); + auto ex = ctfeInterpret(e); + if (!global.endGagging(gaggedErrorsSave) && ex.op != TOK.error) + { + if (log) printf("succeeded %p %d\n", ex, ex.op); + ret = ex; + } + else + { + if (log) printf("failed\n"); } } } @@ -917,7 +963,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) // otherwise we must NOT attempt to constant-fold them. // In particular, if the comma returns a temporary variable, it needs // to be an lvalue (this is particularly important for struct constructors) - expOptimize(e.e1, WANTvalue); + expOptimize(e.e1, want); expOptimize(e.e2, result, keepLvalue); if (ret.op == TOK.error) return; @@ -954,7 +1000,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) override void visit(EqualExp e) { //printf("EqualExp::optimize(result = %x) %s\n", result, e.toChars()); - if (binOptimize(e, WANTvalue)) + if (binOptimize(e, want)) return; Expression e1 = fromConstInitializer(result, e.e1); Expression e2 = fromConstInitializer(result, e.e2); @@ -976,7 +1022,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) override void visit(IdentityExp e) { //printf("IdentityExp::optimize(result = %d) %s\n", result, e.toChars()); - if (binOptimize(e, WANTvalue)) + if (binOptimize(e, want)) return; if ((e.e1.isConst() && e.e2.isConst()) || (e.e1.op == TOK.null_ && e.e2.op == TOK.null_)) { @@ -994,7 +1040,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) Expression ex = fromConstInitializer(result, e.e1); // We might know $ now setLengthVarIfKnown(e.lengthVar, ex); - if (expOptimize(e.e2, WANTvalue)) + if (expOptimize(e.e2, want)) return; // Don't optimize to an array literal element directly in case an lvalue is requested if (keepLvalue && ex.op == TOK.arrayLiteral) @@ -1024,8 +1070,8 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) e.e1 = fromConstInitializer(result, e.e1); // We might know $ now setLengthVarIfKnown(e.lengthVar, e.e1); - expOptimize(e.lwr, WANTvalue); - expOptimize(e.upr, WANTvalue); + expOptimize(e.lwr, want); + expOptimize(e.upr, want); if (ret.op == TOK.error) return; ret = Slice(e.type, e.e1, e.lwr, e.upr).copy(); @@ -1050,7 +1096,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) override void visit(LogicalExp e) { //printf("LogicalExp::optimize(%d) %s\n", result, e.toChars()); - if (expOptimize(e.e1, WANTvalue)) + if (expOptimize(e.e1, want)) return; const oror = e.op == TOK.orOr; if (e.e1.isBool(oror)) @@ -1066,7 +1112,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) ret = Expression_optimize(ret, result, false); return; } - if (expOptimize(e.e2, WANTvalue)) + if (expOptimize(e.e2, want)) return; if (e.e1.isConst()) { @@ -1092,7 +1138,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) override void visit(CmpExp e) { //printf("CmpExp::optimize() %s\n", e.toChars()); - if (binOptimize(e, WANTvalue)) + if (binOptimize(e, want)) return; Expression e1 = fromConstInitializer(result, e.e1); Expression e2 = fromConstInitializer(result, e.e2); @@ -1140,7 +1186,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) override void visit(CondExp e) { - if (expOptimize(e.econd, WANTvalue)) + if (expOptimize(e.econd, want)) return; if (e.econd.isBool(true)) ret = Expression_optimize(e.e1, result, keepLvalue); diff --git a/src/dmd/statementsem.d b/src/dmd/statementsem.d index f69203974143..f9646cc4b14c 100644 --- a/src/dmd/statementsem.d +++ b/src/dmd/statementsem.d @@ -202,8 +202,8 @@ private extern (C++) final class StatementSemanticVisitor : Visitor if (discardValue(s.exp)) s.exp = ErrorExp.get(); - s.exp = s.exp.optimize(WANTvalue); s.exp = checkGC(sc, s.exp); + s.exp = s.exp.optimize(WANTvalue); if (s.exp.op == TOK.error) return setError(); } @@ -529,8 +529,8 @@ private extern (C++) final class StatementSemanticVisitor : Visitor ds.condition = resolveProperties(sc, ds.condition); if (checkNonAssignmentArrayOp(ds.condition)) ds.condition = ErrorExp.get(); - ds.condition = ds.condition.optimize(WANTvalue); ds.condition = checkGC(sc, ds.condition); + ds.condition = ds.condition.optimize(WANTvalue); ds.condition = ds.condition.toBoolean(sc); @@ -602,8 +602,8 @@ private extern (C++) final class StatementSemanticVisitor : Visitor fs.condition = resolveProperties(sc, fs.condition); if (checkNonAssignmentArrayOp(fs.condition)) fs.condition = ErrorExp.get(); - fs.condition = fs.condition.optimize(WANTvalue); fs.condition = checkGC(sc, fs.condition); + fs.condition = fs.condition.optimize(WANTvalue); fs.condition = fs.condition.toBoolean(sc); } @@ -614,8 +614,8 @@ private extern (C++) final class StatementSemanticVisitor : Visitor fs.increment = resolveProperties(sc, fs.increment); if (checkNonAssignmentArrayOp(fs.increment)) fs.increment = ErrorExp.get(); - fs.increment = fs.increment.optimize(WANTvalue); fs.increment = checkGC(sc, fs.increment); + fs.increment = fs.increment.optimize(WANTvalue); } sc.sbreak = fs; @@ -2566,8 +2566,8 @@ else } if (checkNonAssignmentArrayOp(ss.condition)) ss.condition = ErrorExp.get(); - ss.condition = ss.condition.optimize(WANTvalue); ss.condition = checkGC(sc, ss.condition); + ss.condition = ss.condition.optimize(WANTvalue); if (ss.condition.op == TOK.error) conditionError = true; @@ -3214,7 +3214,10 @@ else rs.exp = valueNoDtor(rs.exp); if (e0) + { + e0 = checkGC(sc, e0); e0 = e0.optimize(WANTvalue); + } /* Void-return function can have void typed expression * on return statement. @@ -3228,6 +3231,7 @@ else rs.exp = new CastExp(rs.loc, rs.exp, Type.tvoid); rs.exp = rs.exp.expressionSemantic(sc); } + rs.exp = checkGC(sc, rs.exp); /* Replace: * return exp; @@ -3237,8 +3241,6 @@ else e0 = Expression.combine(e0, rs.exp); rs.exp = null; } - if (e0) - e0 = checkGC(sc, e0); } if (rs.exp) @@ -3624,8 +3626,8 @@ else { ss.exp = ss.exp.expressionSemantic(sc); ss.exp = resolveProperties(sc, ss.exp); - ss.exp = ss.exp.optimize(WANTvalue); ss.exp = checkGC(sc, ss.exp); + ss.exp = ss.exp.optimize(WANTvalue); if (ss.exp.op == TOK.error) { if (ss._body) @@ -3745,8 +3747,8 @@ else //printf("WithStatement::semantic()\n"); ws.exp = ws.exp.expressionSemantic(sc); ws.exp = resolveProperties(sc, ws.exp); - ws.exp = ws.exp.optimize(WANTvalue); ws.exp = checkGC(sc, ws.exp); + ws.exp = ws.exp.optimize(WANTvalue); if (ws.exp.op == TOK.error) return setError(); if (ws.exp.op == TOK.scope_) diff --git a/src/dmd/traits.d b/src/dmd/traits.d index ff11e0a60fcc..e41a01e18034 100644 --- a/src/dmd/traits.d +++ b/src/dmd/traits.d @@ -1741,13 +1741,13 @@ Expression semanticTraits(TraitsExp e, Scope* sc) { ex = ex.expressionSemantic(sc2); ex = resolvePropertiesOnly(sc2, ex); + ex = checkGC(sc2, ex); ex = ex.optimize(WANTvalue); if (sc2.func && sc2.func.type.ty == Tfunction) { const tf = cast(TypeFunction)sc2.func.type; err |= tf.isnothrow && canThrow(ex, sc2.func, false); } - ex = checkGC(sc2, ex); if (ex.op == TOK.error) err = true; }