From 46096c0e00b2836de5f225ada0b28f30d972c6ca Mon Sep 17 00:00:00 2001 From: Clement Courbet Date: Sun, 12 Apr 2015 21:42:38 +0200 Subject: [PATCH 1/2] Add test for https://issues.dlang.org/show_bug.cgi?id=3841, fix it --- src/expression.c | 68 +++++++++++++++++------------- src/expression.h | 2 +- test/fail_compilation/b3841.d | 78 +++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 29 deletions(-) create mode 100644 test/fail_compilation/b3841.d diff --git a/src/expression.c b/src/expression.c index b77d4904ee72..b8fe7e1afeda 100644 --- a/src/expression.c +++ b/src/expression.c @@ -6595,33 +6595,48 @@ Expression *BinExp::binSemanticProp(Scope *sc) return NULL; } -Expression *BinExp::checkComplexOpAssign(Scope *sc) +Expression *BinExp::checkOpAssignTypes(Scope *sc) { + // At that point t1 and t2 are the merged types. type is the original type of the lhs. + Type *t1 = e1->type; + Type *t2 = e2->type; + + // T opAssign floating yields a floating. Prevent truncating conversions (float to int). + // See issue 3841. + // Should we also prevent double to float (type->isfloating() && type->size() < t2 ->size()) ? + if (op == TOKmulass || op == TOKdivass || op == TOKmodass || TOKaddass || op == TOKminass || op == TOKpowass) + { + if ((type->isintegral() && t2->isfloating())) + { + warning("%s %s %s is performing truncating conversion", + type->toChars(), Token::toChars(op), t2->toChars()); + } + } + // generate an error if this is a nonsensical *=,/=, or %=, eg real *= imaginary if (op == TOKmulass || op == TOKdivass || op == TOKmodass) { // Any multiplication by an imaginary or complex number yields a complex result. // r *= c, i*=c, r*=i, i*=i are all forbidden operations. const char *opstr = Token::toChars(op); - if (e1->type->isreal() && e2->type->iscomplex()) + if (t1->isreal() && t2->iscomplex()) { error("%s %s %s is undefined. Did you mean %s %s %s.re ?", - e1->type->toChars(), opstr, e2->type->toChars(), - e1->type->toChars(), opstr, e2->type->toChars()); + t1->toChars(), opstr, t2->toChars(), + t1->toChars(), opstr, t2->toChars()); return new ErrorExp(); } - else if (e1->type->isimaginary() && e2->type->iscomplex()) + else if (t1->isimaginary() && t2->iscomplex()) { error("%s %s %s is undefined. Did you mean %s %s %s.im ?", - e1->type->toChars(), opstr, e2->type->toChars(), - e1->type->toChars(), opstr, e2->type->toChars()); + t1->toChars(), opstr, t2->toChars(), + t1->toChars(), opstr, t2->toChars()); return new ErrorExp(); } - else if ((e1->type->isreal() || e1->type->isimaginary()) && - e2->type->isimaginary()) + else if ((t1->isreal() || t1->isimaginary()) && + t2->isimaginary()) { - error("%s %s %s is an undefined operation", e1->type->toChars(), - opstr, e2->type->toChars()); + error("%s %s %s is an undefined operation", t1->toChars(), opstr, t2->toChars()); return new ErrorExp(); } } @@ -6631,26 +6646,24 @@ Expression *BinExp::checkComplexOpAssign(Scope *sc) { // Addition or subtraction of a real and an imaginary is a complex result. // Thus, r+=i, r+=c, i+=r, i+=c are all forbidden operations. - if ((e1->type->isreal() && (e2->type->isimaginary() || e2->type->iscomplex())) || - (e1->type->isimaginary() && (e2->type->isreal() || e2->type->iscomplex()))) + if ((t1->isreal() && (t2->isimaginary() || t2->iscomplex())) || + (t1->isimaginary() && (t2->isreal() || t2->iscomplex()))) { error("%s %s %s is undefined (result is complex)", - e1->type->toChars(), Token::toChars(op), e2->type->toChars()); + t1->toChars(), Token::toChars(op), t2->toChars()); return new ErrorExp(); } if (type->isreal() || type->isimaginary()) { - assert(global.errors || e2->type->isfloating()); - e2 = e2->castTo(sc, e1->type); + assert(global.errors || t2->isfloating()); + e2 = e2->castTo(sc, t1); } } if (op == TOKmulass) { - if (e2->type->isfloating()) + if (t2->isfloating()) { - Type *t1 = e1->type; - Type *t2 = e2->type; if (t1->isreal()) { if (t2->isimaginary() || t2->iscomplex()) @@ -6677,9 +6690,8 @@ Expression *BinExp::checkComplexOpAssign(Scope *sc) } else if (op == TOKdivass) { - if (e2->type->isimaginary()) + if (t2->isimaginary()) { - Type *t1 = e1->type; if (t1->isreal()) { // x/iv = i(-x/v) @@ -6692,16 +6704,16 @@ Expression *BinExp::checkComplexOpAssign(Scope *sc) } else if (t1->isimaginary()) { - Type *t2; + Type *t3; switch (t1->ty) { - case Timaginary32: t2 = Type::tfloat32; break; - case Timaginary64: t2 = Type::tfloat64; break; - case Timaginary80: t2 = Type::tfloat80; break; + case Timaginary32: t3 = Type::tfloat32; break; + case Timaginary64: t3 = Type::tfloat64; break; + case Timaginary80: t3 = Type::tfloat80; break; default: assert(0); } - e2 = e2->castTo(sc, t2); + e2 = e2->castTo(sc, t3); Expression *e = new AssignExp(loc, e1, e2); e->type = t1; return e; @@ -6710,7 +6722,7 @@ Expression *BinExp::checkComplexOpAssign(Scope *sc) } else if (op == TOKmodass) { - if (e2->type->iscomplex()) + if (t2->iscomplex()) { error("cannot perform modulo complex arithmetic"); return new ErrorExp(); @@ -6846,7 +6858,7 @@ Expression *BinAssignExp::semantic(Scope *sc) if (e1->op == TOKerror || e2->op == TOKerror) return new ErrorExp(); - e = checkComplexOpAssign(sc); + e = checkOpAssignTypes(sc); if (e->op == TOKerror) return e; diff --git a/src/expression.h b/src/expression.h index cd49f1df77fa..558b6bc86b55 100644 --- a/src/expression.h +++ b/src/expression.h @@ -752,8 +752,8 @@ class BinExp : public Expression Expression *semantic(Scope *sc) = 0; Expression *binSemantic(Scope *sc); Expression *binSemanticProp(Scope *sc); - Expression *checkComplexOpAssign(Scope *sc); Expression *incompatibleTypes(); + Expression *checkOpAssignTypes(Scope *sc); bool checkIntegralBin(); bool checkArithmeticBin(); diff --git a/test/fail_compilation/b3841.d b/test/fail_compilation/b3841.d new file mode 100644 index 000000000000..4a99e9a69639 --- /dev/null +++ b/test/fail_compilation/b3841.d @@ -0,0 +1,78 @@ +// PERMUTE_ARGS: +// REQUIRED_ARGS: -w -o- + +/* +TEST_OUTPUT: +--- +fail_compilation/b3841.d-mixin-31(31): Warning: char += float is performing truncating conversion +fail_compilation/b3841.d-mixin-31(31): Warning: int += float is performing truncating conversion +fail_compilation/b3841.d-mixin-31(31): Warning: long += double is performing truncating conversion +fail_compilation/b3841.d-mixin-31(31): Warning: char -= float is performing truncating conversion +fail_compilation/b3841.d-mixin-31(31): Warning: int -= float is performing truncating conversion +fail_compilation/b3841.d-mixin-31(31): Warning: long -= double is performing truncating conversion +fail_compilation/b3841.d-mixin-31(31): Warning: char *= float is performing truncating conversion +fail_compilation/b3841.d-mixin-31(31): Warning: int *= float is performing truncating conversion +fail_compilation/b3841.d-mixin-31(31): Warning: long *= double is performing truncating conversion +fail_compilation/b3841.d-mixin-31(31): Warning: char /= float is performing truncating conversion +fail_compilation/b3841.d-mixin-31(31): Warning: int /= float is performing truncating conversion +fail_compilation/b3841.d-mixin-31(31): Warning: long /= double is performing truncating conversion +fail_compilation/b3841.d-mixin-31(31): Warning: char %= float is performing truncating conversion +fail_compilation/b3841.d-mixin-31(31): Warning: int %= float is performing truncating conversion +fail_compilation/b3841.d-mixin-31(31): Warning: long %= double is performing truncating conversion +--- +*/ + + +void f(string op, LHS, RHS)() +{ + // pragma(msg, LHS, " += ", RHS); + LHS a; + RHS b; + mixin("a "~op~" b;"); +} + +template Ops(T...) +{ + alias Ops = T; +} + +void main() +{ + foreach (string op; Ops!("+=", "-=", "*=", "/=", "%=")) + { + // OK + f!(op, int, int)(); + f!(op, long, int)(); + f!(op, long, short)(); + f!(op, float, long)(); + f!(op, cfloat, long)(); + f!(op, double, float)(); + + // Should that really be OK ? + f!(op, short, int)(); + f!(op, float, double)(); + + // Not OK, truncating conversion. + f!(op, char, float)(); + f!(op, int, float)(); + f!(op, long, double)(); + } + + foreach (string op; Ops!("+=", "-=")) + { + // OK + f!(op, idouble, ifloat)(); + + // Should that really be OK ? + f!(op, ifloat, idouble)(); + } + + // OK + f!("^^=", int, int)(); + f!("^^=", long, int)(); + f!("^^=", long, short)(); + f!("^^=", float, long)(); + f!("^^=", double, float)(); + // Should that really be OK ? + f!("^^=", float, double)(); +} \ No newline at end of file From 4fb90c780f2a342d441de77f87beeebb73d4c87b Mon Sep 17 00:00:00 2001 From: Clement Courbet Date: Tue, 12 May 2015 20:39:46 +0200 Subject: [PATCH 2/2] Fix bad usage of int *= float: generate a const shift by other means --- test/runnable/test42.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/runnable/test42.d b/test/runnable/test42.d index c975bd60f8ef..42ea960cc640 100644 --- a/test/runnable/test42.d +++ b/test/runnable/test42.d @@ -4582,11 +4582,11 @@ void test7212() void test242() { - foreach(v; long.max / 4 .. long.max / 4 + 1) + foreach(v; long.max / 8 .. long.max / 8 + 1) { immutable long t1 = v; long t2 = t1 + t1; - t2 *= 1.0; + t2 *= 1L << 1; assert(t2 > long.max / 4); } }