diff --git a/compiler/src/dmd/expression.d b/compiler/src/dmd/expression.d index d65b163ee6..a51f918cca 100644 --- a/compiler/src/dmd/expression.d +++ b/compiler/src/dmd/expression.d @@ -3851,6 +3851,9 @@ extern (C++) final class CommaExp : BinExp /// false will be passed will be from the parser. bool allowCommaExp; + /// The original expression before any rewriting occurs. + /// This is used in error messages. + Expression originalExp; extern (D) this(Loc loc, Expression e1, Expression e2, bool generated = true) @safe { @@ -3858,6 +3861,12 @@ extern (C++) final class CommaExp : BinExp allowCommaExp = isGenerated = generated; } + extern (D) this(Loc loc, Expression e1, Expression e2, Expression oe) @safe + { + this(loc, e1, e2); + originalExp = oe; + } + override bool isLvalue() { return !rvalue && e2.isLvalue(); diff --git a/compiler/src/dmd/expression.h b/compiler/src/dmd/expression.h index 3c8d90dd7e..4a232fe7ae 100644 --- a/compiler/src/dmd/expression.h +++ b/compiler/src/dmd/expression.h @@ -990,6 +990,7 @@ class CommaExp final : public BinExp public: d_bool isGenerated; d_bool allowCommaExp; + Expression* originalExp; bool isLvalue() override; Optional toBool() override; void accept(Visitor *v) override { v->visit(this); } diff --git a/compiler/src/dmd/expressionsem.d b/compiler/src/dmd/expressionsem.d index 04efa1f86f..f71c888c62 100644 --- a/compiler/src/dmd/expressionsem.d +++ b/compiler/src/dmd/expressionsem.d @@ -12796,6 +12796,58 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } + // Inline the expression, if possible. + PowExp pe = cast(PowExp)e; + if (pe.e1.type.isScalar() && pe.e2.isIntegerExp()) + { + Expression one; + if (pe.e1.type.isIntegral()) { + one = new IntegerExp(e.loc, 1, pe.e1.type); + } else { + one = new RealExp(e.loc, CTFloat.one, pe.e1.type); + } + + const expo = cast(sinteger_t)pe.e2.toInteger(); + // Replace e1 ^^ -1 with 1 / e1 + if (expo == -1) + { + Expression ex = new DivExp(exp.loc, one, pe.e1); + ex = ex.expressionSemantic(sc); + result = ex; + return; + } + // Replace e1 ^^ 0 with 1 + else if (expo == 0) + { + Expression ex = one; + ex.loc = exp.loc; + ex = ex.expressionSemantic(sc); + result = ex; + return; + } + // Replace e1 ^^ 1 with e1 + else if (expo == 1) + { + Expression ex = pe.e1; + ex.loc = exp.loc; + ex = ex.expressionSemantic(sc); + result = ex; + return; + } + // Replace e1 ^^ 2 with e1 * e1 + else if (expo == 2) + { + auto v = copyToTemp(STC.const_, "__powtmp", pe.e1); + auto ve = new VarExp(exp.loc, v); + auto de = new DeclarationExp(exp.e1.loc, v); + auto me = new MulExp(exp.e2.loc, ve, ve); + Expression ex = new CommaExp(exp.loc, de, me, exp); + ex = ex.expressionSemantic(sc); + result = ex; + return; + } + } + Module mmath = Module.loadStdMath(); if (!mmath) { diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index 730d53e489..c152c724ce 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -2766,6 +2766,7 @@ class CommaExp final : public BinExp public: const bool isGenerated; bool allowCommaExp; + Expression* originalExp; bool isLvalue() override; Optional toBool() override; void accept(Visitor* v) override; diff --git a/compiler/src/dmd/hdrgen.d b/compiler/src/dmd/hdrgen.d index 61ff273c6b..97e348350b 100644 --- a/compiler/src/dmd/hdrgen.d +++ b/compiler/src/dmd/hdrgen.d @@ -2724,6 +2724,12 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt void visitComma(CommaExp e) { + if (e.originalExp !is null) + { + e.originalExp.expressionPrettyPrint(buf, hgs); + return; + } + // CommaExp is generated by the compiler so it shouldn't // appear in error messages or header files. // For now, this treats the case where the compiler diff --git a/compiler/test/fail_compilation/powinline.d b/compiler/test/fail_compilation/powinline.d new file mode 100644 index 0000000000..11ebacd16a --- /dev/null +++ b/compiler/test/fail_compilation/powinline.d @@ -0,0 +1,38 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/powinline.d(25): Error: cannot implicitly convert expression `(a + 5.0) ^^ 2L` of type `double` to `int` +fail_compilation/powinline.d(26): Error: cannot implicitly convert expression `(1.0 / foo()) ^^ 2L` of type `double` to `int` +fail_compilation/powinline.d(31): Error: void has no value +fail_compilation/powinline.d(31): Error: incompatible types for `(5.0) * (bar())`: `double` and `void` +fail_compilation/powinline.d(37): Error: cannot modify `immutable` expression `a` +--- +*/ + +double foo() +{ + return 5.0; +} + +void bar() +{ + return; +} + +void test1() +{ + double a = 2.0; + int b = (a + 5.0) ^^ 2.0; + b = (1 / foo()) ^^ 2.0; +} + +void test2() +{ + double a = (5.0 * bar()) ^^ 2.0; +} + +void test3() +{ + immutable double a = 3.0; + (a ^^= 2.0) = 6; +} diff --git a/compiler/test/runnable/powinline.d b/compiler/test/runnable/powinline.d new file mode 100644 index 0000000000..7ec512c526 --- /dev/null +++ b/compiler/test/runnable/powinline.d @@ -0,0 +1,44 @@ +/* +REQUIRED_ARGS: -betterC +RUN_OUTPUT: +--- +Success +--- +*/ +import core.stdc.stdio; + +void test1() +{ + enum real Two = 2.0; + static assert(Two^^3 == 8.0); +} + +void test2() +{ + double x = 5.0; + assert(x^^-1 == 1/x); + x = -1.0; + assert(x^^1 == x); + assert((x += 3) ^^ 2.0 == 4.0); + assert((x) ^^ 2.0 == 4.0); + assert((x *= 5) ^^ 2.0 == (x * x)); + assert(x^^-1 == 1.0 / x); + assert((x^^-1) ^^ 0.0 == 1.0); +} + +void test3() +{ + int x = 6; + assert(x ^^ 0 == 1); + assert((x += 3) ^^ 2 == 81); + assert(x ^^ 2 == (x ^^ 1) * (x ^^ 1)); + static assert(4.0 ^^ -1 == 0.25); +} + +extern(C) void main() +{ + test1(); + test2(); + test3(); + printf("Success\n"); +}