diff --git a/compiler/src/dmd/backend/drtlsym.d b/compiler/src/dmd/backend/drtlsym.d index 2aec454f1e43..d00cf9630950 100644 --- a/compiler/src/dmd/backend/drtlsym.d +++ b/compiler/src/dmd/backend/drtlsym.d @@ -115,7 +115,6 @@ Symbol* getRtlsym(RTLSYM i) @trusted case RTLSYM.ARRAYCOPY: symbolz(ps,FL.func,FREGSAVED,"_d_arraycopy", 0, t); break; case RTLSYM.ARRAYASSIGN_R: symbolz(ps,FL.func,FREGSAVED,"_d_arrayassign_r", 0, t); break; case RTLSYM.ARRAYASSIGN_L: symbolz(ps,FL.func,FREGSAVED,"_d_arrayassign_l", 0, t); break; - case RTLSYM.ARRAYEQ2: symbolz(ps,FL.func,FREGSAVED,"_adEq2", 0, t); break; /* Associative Arrays https://github.com/dlang/dmd/blob/master/druntime/src/rt/aaA.d */ case RTLSYM.AANEW: symbolz(ps,FL.func,FREGSAVED,"_aaNew", 0, t); break; diff --git a/compiler/src/dmd/e2ir.d b/compiler/src/dmd/e2ir.d index c0850af326f5..ce609b8e2a71 100644 --- a/compiler/src/dmd/e2ir.d +++ b/compiler/src/dmd/e2ir.d @@ -2105,14 +2105,16 @@ elem* toElem(Expression e, ref IRState irs) } else if (t1.isStaticOrDynamicArray() && t2.isStaticOrDynamicArray()) { - Type telement = t1.nextOf().toBasetype(); - Type telement2 = t2.nextOf().toBasetype(); - - if ((telement.isIntegral() || telement.ty == Tvoid) && telement.ty == telement2.ty) + if (auto lowering = ee.lowering) + { + e = toElem(lowering, irs); + elem_setLoc(e, ee.loc); + } + else { // Optimize comparisons of arrays of basic types - // For arrays of integers/characters, and void[], - // replace druntime call with: + // For arrays of scalars (except floating types) of same size & signedness, void[], + // and structs with no custom equality operator, replace druntime call with: // For a==b: a.length==b.length && (a.length == 0 || memcmp(a.ptr, b.ptr, size)==0) // For a!=b: a.length!=b.length || (a.length != 0 || memcmp(a.ptr, b.ptr, size)!=0) // size is a.length*sizeof(a[0]) for dynamic arrays, or sizeof(a) for static arrays. @@ -2122,7 +2124,7 @@ elem* toElem(Expression e, ref IRState irs) elem* eptr1, eptr2; // Pointer to data, to pass to memcmp elem* elen1, elen2; // Length, for comparison elem* esiz1, esiz2; // Data size, to pass to memcmp - const sz = telement.size(); // Size of one element + const sz = t1.nextOf().toBasetype().size(); // Size of one element bool is64 = target.isX86_64 || target.isAArch64; if (t1.ty == Tarray) @@ -2175,19 +2177,7 @@ elem* toElem(Expression e, ref IRState irs) e = el_combine(earr2, e); e = el_combine(earr1, e); elem_setLoc(e, ee.loc); - return e; } - - elem* ea1 = eval_Darray(ee.e1); - elem* ea2 = eval_Darray(ee.e2); - - elem* ep = el_params(getTypeInfo(ee, telement.arrayOf(), irs), - ea2, ea1, null); - const rtlfunc = RTLSYM.ARRAYEQ2; - e = el_bin(OPcall, TYint, el_var(getRtlsym(rtlfunc)), ep); - if (ee.op == EXP.notEqual) - e = el_bin(OPxor, TYint, e, el_long(TYint, 1)); - elem_setLoc(e,ee.loc); } else if (t1.ty == Taarray && t2.ty == Taarray) { diff --git a/compiler/src/dmd/expression.d b/compiler/src/dmd/expression.d index a7fcb428daaa..ff619b7ab39b 100644 --- a/compiler/src/dmd/expression.d +++ b/compiler/src/dmd/expression.d @@ -4802,6 +4802,7 @@ extern (C++) final class RemoveExp : BinExp */ extern (C++) final class EqualExp : BinExp { + Expression lowering; extern (D) this(EXP op, Loc loc, Expression e1, Expression e2) @safe { super(loc, op, e1, e2); diff --git a/compiler/src/dmd/expression.h b/compiler/src/dmd/expression.h index b2bf806c71d1..9deb6c38fd1c 100644 --- a/compiler/src/dmd/expression.h +++ b/compiler/src/dmd/expression.h @@ -1280,6 +1280,8 @@ class RemoveExp final : public BinExp class EqualExp final : public BinExp { public: + Expression* lowering; + void accept(Visitor *v) override { v->visit(this); } }; diff --git a/compiler/src/dmd/expressionsem.d b/compiler/src/dmd/expressionsem.d index 6f9ac8674622..dac987cd292f 100644 --- a/compiler/src/dmd/expressionsem.d +++ b/compiler/src/dmd/expressionsem.d @@ -3860,6 +3860,78 @@ private void lowerCastExp(CastExp cex, Scope* sc) cex.lowering = lowering.expressionSemantic(sc); } +/** + * Visitor to check if a type is suitable for comparison using `memcmp`. + * Currently used when lowering `EqualExp`. + */ +private extern(C++) final class IsMemcmpableVisitor : Visitor +{ + alias visit = Visitor.visit; + public: + bool result = false; + + override void visit(Type t) + { + result = t.ty == Tvoid || (t.isScalar() && !t.isFloating()); + } + + override void visit(TypeStruct ts) + { + result = false; + + if (ts.sym.hasIdentityEquals) + return; // has custom opEquals + + if (!ts.sym.members) + { + result = true; + return; + } + + /* We recursively check all variable declaration within the struct. + * The recursiveness is needed to handle cases like this: + * struct Test { + * nothrow: + * int[] contents; + * } + * Here a `StorageClassDeclaration` symbol will be created, which wraps the variable declaration. + */ + static bool visitAllMembers(Dsymbols* members, TypeStruct root, IsMemcmpableVisitor v) + { + if (members is null) + return true; + + foreach (m; *members) + { + if (auto vd = m.isVarDeclaration()) + { + if (vd.type is null) + continue; + + auto tbvd = vd.type.toBasetype(); + if (tbvd !is root) + tbvd.accept(v); + + if (!v.result) + return false; + } + else if (auto ad = m.isAttribDeclaration()) + { + if(!visitAllMembers(ad.decl, root, v)) + return false; + } + } + return true; + } + result = visitAllMembers(ts.sym.members, ts, this); + } + + override void visit(TypeSArray tsa) + { + tsa.nextOf().toBasetype().accept(this); + } +} + private extern (C++) final class ExpressionSemanticVisitor : Visitor { alias visit = Visitor.visit; @@ -13412,19 +13484,53 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Type t1 = exp.e1.type.toBasetype(); Type t2 = exp.e2.type.toBasetype(); - // Indicates whether the comparison of the 2 specified array types - // requires an object.__equals() lowering. - static bool needsDirectEq(Type t1, Type t2, Scope* sc) + static bool unifyArrayTypes(Type t1, Type t2, Scope* sc) { 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; + return true; } if (t1n.constOf() != t2n.constOf()) + { + return false; + } + + Type t = t1n; + while (t.toBasetype().nextOf()) + t = t.nextOf().toBasetype(); + if (auto ts = t.isTypeStruct()) + { + // semanticTypeInfo() makes sure hasIdentityEquals has been computed + if (global.params.useTypeInfo && Type.dtypeinfo) + semanticTypeInfo(sc, ts); + + return !ts.sym.hasIdentityEquals; // has custom opEquals + } + + return true; + } + + static bool shouldUseMemcmp(Type t1, Type t2, Scope *sc) + { + Type t1n = t1.nextOf().toBasetype(); + Type t2n = t2.nextOf().toBasetype(); + const t1nsz = t1n.size(); + const t2nsz = t2n.size(); + + if ((t1n.ty == Tvoid || (t1n.isScalar() && !t1n.isFloating())) && + (t2n.ty == Tvoid || (t2n.isScalar() && !t1n.isFloating())) && + t1nsz == t2nsz && t1n.isUnsigned() == t2n.isUnsigned()) + { return true; + } + if (t1n.constOf() != t2n.constOf()) + { + return false; + } Type t = t1n; while (t.toBasetype().nextOf()) @@ -13435,7 +13541,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (global.params.useTypeInfo && Type.dtypeinfo) semanticTypeInfo(sc, ts); - return ts.sym.hasIdentityEquals; // has custom opEquals + auto v = new IsMemcmpableVisitor(); + ts.accept(v); + return v.result; } return false; @@ -13448,9 +13556,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } const isArrayComparison = t1.isStaticOrDynamicArray() && t2.isStaticOrDynamicArray(); - const needsArrayLowering = isArrayComparison && needsDirectEq(t1, t2, sc); - if (!needsArrayLowering) + if (!isArrayComparison || unifyArrayTypes(t1, t2, sc)) { // https://issues.dlang.org/show_bug.cgi?id=23783 if (exp.e1.checkSharedAccess(sc) || exp.e2.checkSharedAccess(sc)) @@ -13480,7 +13587,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } // lower some array comparisons to object.__equals(e1, e2) - if (needsArrayLowering || (t1.ty == Tarray && t2.ty == Tarray)) + if (isArrayComparison && !shouldUseMemcmp(t1, t2, sc)) { //printf("Lowering to __equals %s %s\n", exp.e1.toChars(), exp.e2.toChars()); @@ -13495,13 +13602,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } - if (!verifyHookExist(exp.loc, *sc, Id.__equals, "equal checks on arrays")) + Identifier hook = Id.__equals; + if (!verifyHookExist(exp.loc, *sc, hook, "equal checks on arrays")) return setError(); - Expression __equals = new IdentifierExp(exp.loc, Id.empty); - Identifier id = Identifier.idPool("__equals"); - __equals = new DotIdExp(exp.loc, __equals, Id.object); - __equals = new DotIdExp(exp.loc, __equals, id); + Expression lowering = new IdentifierExp(exp.loc, Id.empty); + lowering = new DotIdExp(exp.loc, lowering, Id.object); + lowering = new DotIdExp(exp.loc, lowering, hook); /* https://issues.dlang.org/show_bug.cgi?id=23674 * @@ -13512,30 +13619,71 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.e1 = exp.e1.optimize(WANTvalue); exp.e2 = exp.e2.optimize(WANTvalue); + auto e1c = exp.e1.copy(); + auto e2c = exp.e2.copy(); + + /* Remove qualifiers from the types of the arguments to reduce the number + * of generated `__equals` instances. + */ + static void unqualifyExp(Expression e) + { + e.type = e.type.unqualify(MODFlags.wild | MODFlags.immutable_ | MODFlags.shared_); + auto eNext = e.type.nextOf(); + if (eNext && !eNext.toBasetype().isTypeStruct()) + e.type = e.type.unqualify(MODFlags.const_); + + if (!e.isArrayLiteralExp()) + return; + + if (auto elems = e.isArrayLiteralExp().elements) + foreach(elem; *elems) + if (elem) + unqualifyExp(elem); + } + unqualifyExp(e1c); + unqualifyExp(e2c); + auto arguments = new Expressions(2); - (*arguments)[0] = exp.e1; - (*arguments)[1] = exp.e2; + (*arguments)[0] = e1c; + (*arguments)[1] = e2c; - __equals = new CallExp(exp.loc, __equals, arguments); + lowering = new CallExp(exp.loc, lowering, arguments); if (exp.op == EXP.notEqual) { - __equals = new NotExp(exp.loc, __equals); + lowering = new NotExp(exp.loc, lowering); } - __equals = __equals.trySemantic(sc); // for better error message - if (!__equals) + lowering = lowering.trySemantic(sc); // for better error message + if (!lowering) { if (sc.func) error(exp.loc, "can't infer return type in function `%s`", sc.func.toChars()); else error(exp.loc, "incompatible types for array comparison: `%s` and `%s`", exp.e1.type.toChars(), exp.e2.type.toChars()); - __equals = ErrorExp.get(); + lowering = ErrorExp.get(); } - - result = __equals; + exp.lowering = lowering; + result = exp; return; } + // When array comparison is not lowered to `__equals`, `memcmp` is used, but + // GC checks occur before the expression is lowered to `memcmp` in e2ir.d. + // Thus, we will consider the literal arrays as on-stack arrays to avoid issues + // during GC checks. + if (isArrayComparison) + { + if (auto ale1 = exp.e1.isArrayLiteralExp()) + { + ale1.onstack = true; + } + + if (auto ale2 = exp.e2.isArrayLiteralExp()) + { + ale2.onstack = true; + } + } + if (exp.e1.type.toBasetype().ty == Taarray) { semanticTypeInfo(sc, exp.e1.type.toBasetype()); diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index a14b0f3f53eb..922cee78b810 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -2987,6 +2987,7 @@ class DsymbolExp final : public Expression class EqualExp final : public BinExp { public: + Expression* lowering; void accept(Visitor* v) override; }; diff --git a/compiler/src/dmd/inline.d b/compiler/src/dmd/inline.d index 47b94926eb24..1b246ca9c816 100644 --- a/compiler/src/dmd/inline.d +++ b/compiler/src/dmd/inline.d @@ -836,7 +836,16 @@ public: override void visit(EqualExp e) { - visit(cast(BinExp)e); + auto ee = cast(EqualExp)e.copy(); + if (auto lowering = ee.lowering) + { + ee.lowering = doInlineAs!Expression(lowering, ids); + } + + ee.e1 = doInlineAs!Expression(e.e1, ids); + ee.e2 = doInlineAs!Expression(e.e2, ids); + + result = ee; Type t1 = e.e1.type.toBasetype(); if (t1.isStaticOrDynamicArray()) @@ -1336,6 +1345,18 @@ public: inlineScan(e.e2); } + override void visit(EqualExp e) + { + if (auto lowering = e.lowering) + { + inlineScan(lowering); + } + else + { + visit(cast(BinExp)e); + } + } + override void visit(AssignExp e) { // Look for NRVO, as inlining NRVO function returns require special handling diff --git a/compiler/src/dmd/optimize.d b/compiler/src/dmd/optimize.d index b13f8c64584f..88a947f6b02a 100644 --- a/compiler/src/dmd/optimize.d +++ b/compiler/src/dmd/optimize.d @@ -1138,9 +1138,16 @@ Expression optimize(Expression e, int result, bool keepLvalue = false) void visitEqual(EqualExp e) { - //printf("EqualExp::optimize(result = %x) %s\n", result, e.toChars()); + //printf("EqualExp::optimize(result = %d) %s\n", result, e.toChars()); + if (auto lowering = e.lowering) + { + optimize(lowering, result, keepLvalue); + return; + } + if (binOptimize(e, WANTvalue)) return; + Expression e1 = fromConstInitializer(result, e.e1); Expression e2 = fromConstInitializer(result, e.e2); if (e1.op == EXP.error) diff --git a/compiler/src/dmd/visitor/postorder.d b/compiler/src/dmd/visitor/postorder.d index 22549da45d5f..26496adb8c12 100644 --- a/compiler/src/dmd/visitor/postorder.d +++ b/compiler/src/dmd/visitor/postorder.d @@ -106,6 +106,18 @@ public: doCond(e.e1) || doCond(e.e2) || applyTo(e); } + override void visit(EqualExp e) + { + if (auto lowering = e.lowering) + { + doCond(lowering) || applyTo(e); + } + else + { + visit(cast(BinExp)e); + } + } + override void visit(AssertExp e) { //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); diff --git a/compiler/test/compilable/makedeps_exe.d b/compiler/test/compilable/makedeps_exe.d index d137a754000c..1dc7ad21ed28 100644 --- a/compiler/test/compilable/makedeps_exe.d +++ b/compiler/test/compilable/makedeps_exe.d @@ -9,7 +9,7 @@ $r:.*makedeps_exe_$0$?:windows=.exe$: \ $p:makedeps_exe.d$ \ $p:makedeps_a.d$ \ $p:makedeps-import.txt$ \ - $p:makedeps-import-codemixin.txt$ \ + $p:makedeps-import-codemixin.txt$ --- **/ module makedeps_exe; diff --git a/compiler/test/compilable/makedeps_file.d b/compiler/test/compilable/makedeps_file.d index ead97c2d1d91..f4ad827b0ad2 100644 --- a/compiler/test/compilable/makedeps_file.d +++ b/compiler/test/compilable/makedeps_file.d @@ -8,7 +8,7 @@ TEST_OUTPUT: $r:.*makedeps_file_$0.o$?:windows=bj$: \ $p:makedeps_file.d$ \ $p:makedeps_a.d$ \ - $p:makedeps-import.txt$ \ + $p:makedeps-import.txt$ --- **/ module makedeps_file; diff --git a/compiler/test/compilable/makedeps_lib.d b/compiler/test/compilable/makedeps_lib.d index fc8d5140ba88..4460027b0c76 100644 --- a/compiler/test/compilable/makedeps_lib.d +++ b/compiler/test/compilable/makedeps_lib.d @@ -8,7 +8,7 @@ TEST_OUTPUT: $r:.*makedeps_lib_$0.$?:windows=lib|a$: \ $p:makedeps_lib.d$ \ $p:imports/makedeps_a.d$ \ - $p:makedeps-import.txt$ \ + $p:makedeps-import.txt$ --- **/ // Disabling on windows because default naming of -lib seems broken (names to .exe) diff --git a/compiler/test/compilable/makedeps_obj.d b/compiler/test/compilable/makedeps_obj.d index d121d0253558..d14b4533626e 100644 --- a/compiler/test/compilable/makedeps_obj.d +++ b/compiler/test/compilable/makedeps_obj.d @@ -7,7 +7,7 @@ $r:.*makedeps_obj_$0.o$?:windows=bj$: \ $p:makedeps_obj.d$ \ $p:makedeps_a.d$ \ $p:makedeps-import-codemixin.txt$ \ - $p:makedeps-import.txt$ \ + $p:makedeps-import.txt$ --- **/ module makedeps_obj; diff --git a/compiler/test/fail_compilation/diag7420.d b/compiler/test/fail_compilation/diag7420.d index 3267e6692432..a19796d5d9b6 100644 --- a/compiler/test/fail_compilation/diag7420.d +++ b/compiler/test/fail_compilation/diag7420.d @@ -2,17 +2,16 @@ /* TEST_OUTPUT: --- -fail_compilation/diag7420.d(21): Error: static variable `x` cannot be read at compile time -fail_compilation/diag7420.d(21): while evaluating: `static assert(x < 4)` +fail_compilation/diag7420.d(20): Error: static variable `x` cannot be read at compile time +fail_compilation/diag7420.d(20): while evaluating: `static assert(x < 4)` +fail_compilation/diag7420.d(21): Error: static variable `y` cannot be read at compile time +fail_compilation/diag7420.d(21): while evaluating: `static assert(y == "abc")` fail_compilation/diag7420.d(22): Error: static variable `y` cannot be read at compile time -fail_compilation/diag7420.d(22): called from here: `__equals(y, "abc")` -fail_compilation/diag7420.d(22): while evaluating: `static assert(y == "abc")` +fail_compilation/diag7420.d(22): while evaluating: `static assert(cast(ubyte[])y != null)` fail_compilation/diag7420.d(23): Error: static variable `y` cannot be read at compile time -fail_compilation/diag7420.d(23): while evaluating: `static assert(cast(ubyte[])y != null)` +fail_compilation/diag7420.d(23): while evaluating: `static assert(cast(int)y[0] == 1)` fail_compilation/diag7420.d(24): Error: static variable `y` cannot be read at compile time -fail_compilation/diag7420.d(24): while evaluating: `static assert(cast(int)y[0] == 1)` -fail_compilation/diag7420.d(25): Error: static variable `y` cannot be read at compile time -fail_compilation/diag7420.d(25): while evaluating: `static assert(y[0..1].length == 1u)` +fail_compilation/diag7420.d(24): while evaluating: `static assert(y[0..1].length == 1u)` --- */ diff --git a/compiler/test/fail_compilation/discard_value.d b/compiler/test/fail_compilation/discard_value.d index 7fe30a687065..848fb19536c2 100644 --- a/compiler/test/fail_compilation/discard_value.d +++ b/compiler/test/fail_compilation/discard_value.d @@ -5,7 +5,7 @@ fail_compilation/discard_value.d(24): Error: the result of the equality expressi fail_compilation/discard_value.d(25): Error: the result of the equality expression `null !is null` is discarded fail_compilation/discard_value.d(26): Error: the result of the equality expression `v == 0` is discarded fail_compilation/discard_value.d(27): Error: the result of the equality expression `v == 0` is discarded -fail_compilation/discard_value.d(28): Error: `!__equals("", "")` has no effect +fail_compilation/discard_value.d(28): Error: the result of the equality expression `"" != ""` is discarded fail_compilation/discard_value.d(29): Error: the result of the equality expression `"" == ""` is discarded fail_compilation/discard_value.d(30): Error: the result of the equality expression `fun().i == 4` is discarded fail_compilation/discard_value.d(30): note that `fun().i` may have a side effect diff --git a/compiler/test/fail_compilation/verifyhookexist.d b/compiler/test/fail_compilation/verifyhookexist.d index d7b8f6646c3b..a05641ebd51c 100644 --- a/compiler/test/fail_compilation/verifyhookexist.d +++ b/compiler/test/fail_compilation/verifyhookexist.d @@ -8,12 +8,11 @@ EXTRA_SOURCES: extra-files/minimal/object.d /* TEST_OUTPUT: --- -fail_compilation/verifyhookexist.d(22): Error: `object.__ArrayCast` not found. The current runtime does not support casting array of structs, or the runtime is corrupt. -fail_compilation/verifyhookexist.d(28): Error: `object.__equals` not found. The current runtime does not support equal checks on arrays, or the runtime is corrupt. -fail_compilation/verifyhookexist.d(29): Error: `object.__cmp` not found. The current runtime does not support comparing arrays, or the runtime is corrupt. -fail_compilation/verifyhookexist.d(33): Error: `object._d_assert_fail` not found. The current runtime does not support generating assert messages, or the runtime is corrupt. -fail_compilation/verifyhookexist.d(36): Error: `object.__switch` not found. The current runtime does not support switch cases on strings, or the runtime is corrupt. -fail_compilation/verifyhookexist.d(41): Error: `object.__switch_error` not found. The current runtime does not support generating assert messages, or the runtime is corrupt. +fail_compilation/verifyhookexist.d(21): Error: `object.__ArrayCast` not found. The current runtime does not support casting array of structs, or the runtime is corrupt. +fail_compilation/verifyhookexist.d(28): Error: `object.__cmp` not found. The current runtime does not support comparing arrays, or the runtime is corrupt. +fail_compilation/verifyhookexist.d(32): Error: `object._d_assert_fail` not found. The current runtime does not support generating assert messages, or the runtime is corrupt. +fail_compilation/verifyhookexist.d(35): Error: `object.__switch` not found. The current runtime does not support switch cases on strings, or the runtime is corrupt. +fail_compilation/verifyhookexist.d(40): Error: `object.__switch_error` not found. The current runtime does not support generating assert messages, or the runtime is corrupt. --- */ diff --git a/druntime/mak/DOCS b/druntime/mak/DOCS index d61d53323b12..adb685dc0bf7 100644 --- a/druntime/mak/DOCS +++ b/druntime/mak/DOCS @@ -539,7 +539,6 @@ DOCS=\ \ $(DOCDIR)\rt_aApply.html \ $(DOCDIR)\rt_aApplyR.html \ - $(DOCDIR)\rt_adi.html \ $(DOCDIR)\rt_alloca.html \ $(DOCDIR)\rt_arraycat.html \ $(DOCDIR)\rt_config.html \ diff --git a/druntime/mak/SRCS b/druntime/mak/SRCS index b9d3bdc738cc..5da022f7d8c7 100644 --- a/druntime/mak/SRCS +++ b/druntime/mak/SRCS @@ -562,7 +562,6 @@ SRCS=\ src\rt\aApply.d \ src\rt\aApplyR.d \ src\rt\aaA.d \ - src\rt\adi.d \ src\rt\alloca.d \ src\rt\arraycat.d \ src\rt\cmath2.d \ diff --git a/druntime/src/core/internal/array/equality.d b/druntime/src/core/internal/array/equality.d index c110d64069f2..e0a811bf0722 100644 --- a/druntime/src/core/internal/array/equality.d +++ b/druntime/src/core/internal/array/equality.d @@ -14,41 +14,7 @@ module core.internal.array.equality; // * dynamic arrays, // * (most) arrays of different (unqualified) element types, and // * arrays of structs with custom opEquals. - - // The scalar-only overload takes advantage of known properties of scalars to - // reduce template instantiation. This is expected to be the most common case. -bool __equals(T1, T2)(scope const T1[] lhs, scope const T2[] rhs) -@nogc nothrow pure @trusted -if (__traits(isScalar, T1) && __traits(isScalar, T2)) -{ - const length = lhs.length; - - static if (T1.sizeof == T2.sizeof - // Signedness needs to match for types that promote to int. - // (Actually it would be okay to memcmp bool[] and byte[] but that is - // probably too uncommon to be worth checking for.) - && (T1.sizeof >= 4 || __traits(isUnsigned, T1) == __traits(isUnsigned, T2)) - && !__traits(isFloating, T1) && !__traits(isFloating, T2)) - { - if (__ctfe) - return length == rhs.length && isEqual(lhs.ptr, rhs.ptr, length); - else - { - // This would improperly allow equality of integers and pointers - // but the CTFE branch will stop this function from compiling then. - import core.stdc.string : memcmp; - return length == rhs.length && - (!length || 0 == memcmp(cast(const void*) lhs.ptr, cast(const void*) rhs.ptr, length * T1.sizeof)); - } - } - else - { - return length == rhs.length && isEqual(lhs.ptr, rhs.ptr, length); - } -} - -bool __equals(T1, T2)(scope T1[] lhs, scope T2[] rhs) -if (!__traits(isScalar, T1) || !__traits(isScalar, T2)) +bool __equals(T1, T2)(scope T1[] lhs, scope T2[] rhs) @trusted { if (lhs.length != rhs.length) return false; @@ -56,37 +22,9 @@ if (!__traits(isScalar, T1) || !__traits(isScalar, T2)) if (lhs.length == 0) return true; - static if (useMemcmp!(T1, T2)) - { - if (!__ctfe) - { - static bool trustedMemcmp(scope T1[] lhs, scope T2[] rhs) @trusted @nogc nothrow pure - { - pragma(inline, true); - import core.stdc.string : memcmp; - return memcmp(cast(void*) lhs.ptr, cast(void*) rhs.ptr, lhs.length * T1.sizeof) == 0; - } - return trustedMemcmp(lhs, rhs); - } - else - { - foreach (const i; 0 .. lhs.length) - { - if (at(lhs, i) != at(rhs, i)) - return false; - } - return true; - } - } - else - { - foreach (const i; 0 .. lhs.length) - { - if (at(lhs, i) != at(rhs, i)) - return false; - } - return true; - } + alias PureType = bool function(scope T1[], scope T2[], size_t) @safe pure nothrow @nogc; + + return (cast(PureType)&isEqual!(T1,T2))(lhs, rhs, lhs.length); } /****************************** @@ -94,11 +32,26 @@ if (!__traits(isScalar, T1) || !__traits(isScalar, T2)) * Outlined to enable __equals() to be inlined, as dmd cannot inline loops. */ private -bool isEqual(T1, T2)(scope const T1* t1, scope const T2* t2, size_t length) +bool isEqual(T1, T2)(scope T1[] lhs, scope T2[] rhs, size_t length) { + // Returns a reference to an array element, eliding bounds check and + // casting void to ubyte. + pragma(inline, true) + static ref at(T)(scope T[] r, size_t i) @trusted + // exclude opaque structs due to https://issues.dlang.org/show_bug.cgi?id=20959 + if (!(is(T == struct) && !is(typeof(T.sizeof)))) + { + static if (is(T == void)) + return (cast(ubyte[]) r)[i]; + else + return r[i]; + } + foreach (const i; 0 .. length) - if (t1[i] != t2[i]) + { + if (at(lhs, i) != at(rhs, i)) return false; + } return true; } @@ -173,69 +126,6 @@ bool isEqual(T1, T2)(scope const T1* t1, scope const T2* t2, size_t length) assert(a1 != a2); } - -private: - -// - Recursively folds static array types to their element type, -// - maps void to ubyte, and -// - pointers to size_t. -template BaseType(T) -{ - static if (__traits(isStaticArray, T)) - alias BaseType = BaseType!(typeof(T.init[0])); - else static if (is(immutable T == immutable void)) - alias BaseType = ubyte; - else static if (is(T == E*, E)) - alias BaseType = size_t; - else - alias BaseType = T; -} - -// Use memcmp if the element sizes match and both base element types are integral. -// Due to int promotion, disallow small integers of diverging signed-ness though. -template useMemcmp(T1, T2) -{ - static if (T1.sizeof != T2.sizeof) - enum useMemcmp = false; - else - { - alias B1 = BaseType!T1; - alias B2 = BaseType!T2; - enum useMemcmp = __traits(isIntegral, B1) && __traits(isIntegral, B2) - && !( (B1.sizeof < 4 || B2.sizeof < 4) && __traits(isUnsigned, B1) != __traits(isUnsigned, B2) ); - } -} - -unittest -{ - enum E { foo, bar } - - static assert(useMemcmp!(byte, byte)); - static assert(useMemcmp!(ubyte, ubyte)); - static assert(useMemcmp!(void, const void)); - static assert(useMemcmp!(void, immutable bool)); - static assert(useMemcmp!(void, inout char)); - static assert(useMemcmp!(void, shared ubyte)); - static assert(!useMemcmp!(void, byte)); // differing signed-ness - static assert(!useMemcmp!(char[8], byte[8])); // ditto - - static assert(useMemcmp!(short, short)); - static assert(useMemcmp!(wchar, ushort)); - static assert(!useMemcmp!(wchar, short)); // differing signed-ness - - static assert(useMemcmp!(int, uint)); // no promotion, ignoring signed-ness - static assert(useMemcmp!(dchar, E)); - - static assert(useMemcmp!(immutable void*, size_t)); - static assert(useMemcmp!(double*, ptrdiff_t)); - static assert(useMemcmp!(long[2][3], const(ulong)[2][3])); - - static assert(!useMemcmp!(float, float)); - static assert(!useMemcmp!(double[2], double[2])); - static assert(!useMemcmp!(Object, Object)); - static assert(!useMemcmp!(int[], int[])); -} - // https://issues.dlang.org/show_bug.cgi?id=21094 unittest { @@ -262,16 +152,3 @@ unittest return lhs == rhs; } } - -// Returns a reference to an array element, eliding bounds check and -// casting void to ubyte. -pragma(inline, true) -ref at(T)(T[] r, size_t i) @trusted - // exclude opaque structs due to https://issues.dlang.org/show_bug.cgi?id=20959 - if (!(is(T == struct) && !is(typeof(T.sizeof)))) -{ - static if (is(immutable T == immutable void)) - return (cast(ubyte*) r.ptr)[i]; - else - return r.ptr[i]; -} diff --git a/druntime/src/rt/adi.d b/druntime/src/rt/adi.d deleted file mode 100644 index 02e4d7719aac..000000000000 --- a/druntime/src/rt/adi.d +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Implementation of dynamic array property support routines. - * - * Copyright: Copyright Digital Mars 2000 - 2015. - * License: Distributed under the - * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). - * (See accompanying file LICENSE) - * Authors: Walter Bright - * Source: $(DRUNTIMESRC rt/_adi.d) - */ - -module rt.adi; - -// debug = adi; // uncomment to turn on debugging printf's - -debug (adi) import core.stdc.stdio : printf; - -/*************************************** - * Support for array equality test. - * Returns: - * 1 equal - * 0 not equal - */ - -extern (C) int _adEq2(void[] a1, void[] a2, TypeInfo ti) -{ - debug(adi) printf("_adEq2(a1.length = %zd, a2.length = %zd)\n", a1.length, a2.length); - if (a1.length != a2.length) - return 0; // not equal - if (!ti.equals(&a1, &a2)) - return 0; - return 1; -} - -@safe unittest -{ - debug(adi) printf("array.Eq unittest\n"); - - struct S(T) { T val; } - alias String = S!string; - alias Float = S!float; - - String[1] a = [String("hello"c)]; - - assert(a != [String("hel")]); - assert(a != [String("helloo")]); - assert(a != [String("betty")]); - assert(a == [String("hello")]); - assert(a != [String("hxxxx")]); - - Float[1] fa = [Float(float.nan)]; - assert(fa != fa); -} - -unittest -{ - debug(adi) printf("struct.Eq unittest\n"); - - static struct TestStruct - { - int value; - - bool opEquals(const TestStruct rhs) const - { - return value == rhs.value; - } - } - - TestStruct[] b = [TestStruct(5)]; - TestStruct[] c = [TestStruct(6)]; - assert(_adEq2(*cast(void[]*)&b, *cast(void[]*)&c, typeid(TestStruct[])) == false); - assert(_adEq2(*cast(void[]*)&b, *cast(void[]*)&b, typeid(TestStruct[])) == true); -}