diff --git a/src/dmd/dcast.d b/src/dmd/dcast.d index b6d0ff7317b1..3f9ff1319d17 100644 --- a/src/dmd/dcast.d +++ b/src/dmd/dcast.d @@ -3523,29 +3523,6 @@ void fix16997(Scope* sc, UnaExp ue) } } -/*********************************** - * See if both types are arrays that can be compared - * for equality. Return true if so. - * If they are arrays, but incompatible, issue error. - * This is to enable comparing things like an immutable - * array with a mutable one. - */ -extern (C++) bool arrayTypeCompatible(Loc loc, Type t1, Type t2) -{ - t1 = t1.toBasetype().merge2(); - t2 = t2.toBasetype().merge2(); - - if ((t1.ty == Tarray || t1.ty == Tsarray || t1.ty == Tpointer) && (t2.ty == Tarray || t2.ty == Tsarray || t2.ty == Tpointer)) - { - if (t1.nextOf().implicitConvTo(t2.nextOf()) < MATCH.constant && t2.nextOf().implicitConvTo(t1.nextOf()) < MATCH.constant && (t1.nextOf().ty != Tvoid && t2.nextOf().ty != Tvoid)) - { - error(loc, "array equality comparison type mismatch, `%s` vs `%s`", t1.toChars(), t2.toChars()); - } - return true; - } - return false; -} - /*********************************** * See if both types are arrays that can be compared * for equality without any casting. Return true if so. diff --git a/src/dmd/expressionsem.d b/src/dmd/expressionsem.d index efd63b74f948..20425cccf1d5 100644 --- a/src/dmd/expressionsem.d +++ b/src/dmd/expressionsem.d @@ -11044,7 +11044,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Type t1 = exp.e1.type.toBasetype(); Type t2 = exp.e2.type.toBasetype(); - bool needsDirectEq(Type t1, Type t2) + // Indicates whether the comparison of the 2 specified array types + // requires an object.__equals() lowering. + static bool needsDirectEq(Type t1, Type t2, Scope* sc) { Type t1n = t1.nextOf().toBasetype(); Type t2n = t2.nextOf().toBasetype(); @@ -11059,13 +11061,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Type t = t1n; while (t.toBasetype().nextOf()) t = t.nextOf().toBasetype(); - if (t.ty != Tstruct) - return false; + if (auto ts = t.isTypeStruct()) + { + // semanticTypeInfo() makes sure hasIdentityEquals has been computed + if (global.params.useTypeInfo && Type.dtypeinfo) + semanticTypeInfo(sc, ts); - if (global.params.useTypeInfo && Type.dtypeinfo) - semanticTypeInfo(sc, t); + return ts.sym.hasIdentityEquals; // has custom opEquals + } - return (cast(TypeStruct)t).sym.hasIdentityEquals; + return false; } if (auto e = exp.op_overload(sc)) @@ -11075,7 +11080,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } - if (!(t1.ty == Tarray && t2.ty == Tarray && needsDirectEq(t1, t2))) + const isArrayComparison = (t1.ty == Tarray || t1.ty == Tsarray) && + (t2.ty == Tarray || t2.ty == Tsarray); + const needsArrayLowering = isArrayComparison && needsDirectEq(t1, t2, sc); + + if (!needsArrayLowering) { if (auto e = typeCombine(exp, sc)) { @@ -11091,26 +11100,20 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.type = Type.tbool; - // Special handling for array comparisons - if (!(t1.ty == Tarray && t2.ty == Tarray && needsDirectEq(t1, t2))) + if (!isArrayComparison) { - if (!arrayTypeCompatible(exp.loc, exp.e1.type, exp.e2.type)) + if (exp.e1.type != exp.e2.type && exp.e1.type.isfloating() && exp.e2.type.isfloating()) { - if (exp.e1.type != exp.e2.type && exp.e1.type.isfloating() && exp.e2.type.isfloating()) - { - // Cast both to complex - exp.e1 = exp.e1.castTo(sc, Type.tcomplex80); - exp.e2 = exp.e2.castTo(sc, Type.tcomplex80); - } + // Cast both to complex + exp.e1 = exp.e1.castTo(sc, Type.tcomplex80); + exp.e2 = exp.e2.castTo(sc, Type.tcomplex80); } } - if (t1.ty == Tarray && t2.ty == Tarray) + // lower some array comparisons to object.__equals(e1, e2) + if (needsArrayLowering || (t1.ty == Tarray && t2.ty == Tarray)) { - //printf("Lowering to __equals %s %s\n", e1.toChars(), e2.toChars()); - - // For e1 and e2 of struct type, lowers e1 == e2 to object.__equals(e1, e2) - // and e1 != e2 to !(object.__equals(e1, e2)). + //printf("Lowering to __equals %s %s\n", exp.e1.toChars(), exp.e2.toChars()); if (!verifyHookExist(exp.loc, *sc, Id.__equals, "equal checks on arrays")) return setError(); @@ -11129,7 +11132,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { __equals = new NotExp(exp.loc, __equals); } - __equals = __equals.expressionSemantic(sc); + __equals = __equals.trySemantic(sc); // for better error message + if (!__equals) + { + exp.error("incompatible types for array comparison: `%s` and `%s`", + exp.e1.type.toChars(), exp.e2.type.toChars()); + __equals = new ErrorExp(); + } result = __equals; return; diff --git a/src/dmd/id.d b/src/dmd/id.d index 583575f49c93..05e70ab8e82a 100644 --- a/src/dmd/id.d +++ b/src/dmd/id.d @@ -306,7 +306,6 @@ immutable Msgtable[] msgtable = { "monitorexit", "_d_monitorexit" }, { "criticalenter", "_d_criticalenter" }, { "criticalexit", "_d_criticalexit" }, - { "__ArrayEq" }, { "__ArrayPostblit" }, { "__ArrayDtor" }, { "_d_delThrowable" }, diff --git a/src/dmd/mtype.h b/src/dmd/mtype.h index 2f4c35646eed..95317956e6fb 100644 --- a/src/dmd/mtype.h +++ b/src/dmd/mtype.h @@ -874,7 +874,6 @@ class TypeNull : public Type /**************************************************************/ -bool arrayTypeCompatible(Loc loc, Type *t1, Type *t2); bool arrayTypeCompatibleWithoutCasting(Type *t1, Type *t2); // If the type is a class or struct, returns the symbol for it, else null. diff --git a/src/dmd/opover.d b/src/dmd/opover.d index ceaa3d6198dc..961b8dd6bae7 100644 --- a/src/dmd/opover.d +++ b/src/dmd/opover.d @@ -262,7 +262,7 @@ private Expression checkAliasThisForRhs(AggregateDeclaration ad, Scope* sc, BinE * sc = context * pop = if not null, is set to the operator that was actually overloaded, * which may not be `e.op`. Happens when operands are reversed to - * to match an overload + * match an overload * Returns: * `null` if not an operator overload, * otherwise the lowered expression @@ -917,52 +917,14 @@ Expression op_overload(Expression e, Scope* sc, TOK* pop = null) Type t1 = e.e1.type.toBasetype(); Type t2 = e.e2.type.toBasetype(); - /* Check for array equality. + /* Array equality is handled by expressionSemantic() potentially + * lowering to object.__equals(), which takes care of overloaded + * operators for the element types. */ if ((t1.ty == Tarray || t1.ty == Tsarray) && (t2.ty == Tarray || t2.ty == Tsarray)) { - bool needsDirectEq() - { - Type t1n = t1.nextOf().toBasetype(); - Type t2n = t2.nextOf().toBasetype(); - if ((t1n.ty.isSomeChar && t2n.ty.isSomeChar) || - (t1n.ty == Tvoid || t2n.ty == Tvoid)) - { - return false; - } - if (t1n.constOf() != t2n.constOf()) - return true; - - Type t = t1n; - while (t.toBasetype().nextOf()) - t = t.nextOf().toBasetype(); - if (t.ty != Tstruct) - return false; - - if (global.params.useTypeInfo && Type.dtypeinfo) - semanticTypeInfo(sc, t); - - return (cast(TypeStruct)t).sym.hasIdentityEquals; - } - - if (needsDirectEq() && !(t1.ty == Tarray && t2.ty == Tarray)) - { - /* Rewrite as: - * __ArrayEq(e1, e2) - */ - Expression eeq = new IdentifierExp(e.loc, Id.__ArrayEq); - result = new CallExp(e.loc, eeq, e.e1, e.e2); - if (e.op == TOK.notEqual) - result = new NotExp(e.loc, result); - result = result.trySemantic(sc); // for better error message - if (!result) - { - e.error("cannot compare `%s` and `%s`", t1.toChars(), t2.toChars()); - result = new ErrorExp(); - } - return; - } + return; } /* Check for class equality with null literal or typeof(null). @@ -1028,9 +990,6 @@ Expression op_overload(Expression e, Scope* sc, TOK* pop = null) return; } - if (t1.ty == Tarray && t2.ty == Tarray) - return; - /* Check for pointer equality. */ if (t1.ty == Tpointer || t2.ty == Tpointer)