diff options
author | Iain Buclaw <ibuclaw@gdcproject.org> | 2025-03-12 17:19:49 +0100 |
---|---|---|
committer | Iain Buclaw <ibuclaw@gdcproject.org> | 2025-03-15 16:33:00 +0100 |
commit | e8c9f4ab8f0c8ad8da5f7fb0f1a4956507fe64f5 (patch) | |
tree | 46099e73f24300a919394d9f1972fdf5941f672d /gcc/d | |
parent | 254549d2bb9bb3c2719dec597427919c59514fc3 (diff) | |
download | gcc-e8c9f4ab8f0c8ad8da5f7fb0f1a4956507fe64f5.zip gcc-e8c9f4ab8f0c8ad8da5f7fb0f1a4956507fe64f5.tar.gz gcc-e8c9f4ab8f0c8ad8da5f7fb0f1a4956507fe64f5.tar.bz2 |
d: Merge upstream dmd, druntime b7e3b3b617
D front-end changes:
- `delete' is no longer a keyword.
- Initializing a field with itself has been deprecated.
D runtime changes:
- Add Windows BCrypt bindings under `core.sys.windows.bcrypt'.
gcc/d/ChangeLog:
* dmd/MERGE: Merge upstream b7e3b3b617.
libphobos/ChangeLog:
* libdruntime/MERGE: Merge upstream b7e3b3b617.
* libdruntime/Makefile.am (DRUNTIME_DSOURCES_WINDOWS): Add
core/sys/windows/bcrypt.d.
* libdruntime/Makefile.in: Regenerate.
* libdruntime/gcc/sections/elf.d (sizeofTLS): Give function the same
mangling as gcc.sections.sizeofTLS.
* libdruntime/gcc/sections/package.d: Import core.internal.traits.
(pinLoadedLibraries): Mangle as function from rt.sections_elf_shared.
(unpinLoadedLibraries): Likewise.
(inheritLoadedLibraries): Likewise.
(cleanupLoadedLibraries): Likewise.
(sizeOfTLS): Add forward declaration.
Diffstat (limited to 'gcc/d')
-rw-r--r-- | gcc/d/dmd/MERGE | 2 | ||||
-rw-r--r-- | gcc/d/dmd/clone.d | 18 | ||||
-rw-r--r-- | gcc/d/dmd/ctfeexpr.d | 8 | ||||
-rw-r--r-- | gcc/d/dmd/dcast.d | 14 | ||||
-rw-r--r-- | gcc/d/dmd/dsymbolsem.d | 2 | ||||
-rw-r--r-- | gcc/d/dmd/dtemplate.d | 23 | ||||
-rw-r--r-- | gcc/d/dmd/expression.d | 108 | ||||
-rw-r--r-- | gcc/d/dmd/expression.h | 3 | ||||
-rw-r--r-- | gcc/d/dmd/expressionsem.d | 617 | ||||
-rw-r--r-- | gcc/d/dmd/func.d | 3 | ||||
-rw-r--r-- | gcc/d/dmd/hdrgen.d | 2 | ||||
-rw-r--r-- | gcc/d/dmd/id.d | 66 | ||||
-rw-r--r-- | gcc/d/dmd/initsem.d | 4 | ||||
-rw-r--r-- | gcc/d/dmd/mangle/cpp.d | 43 | ||||
-rw-r--r-- | gcc/d/dmd/mtype.d | 12 | ||||
-rw-r--r-- | gcc/d/dmd/mustuse.d | 23 | ||||
-rw-r--r-- | gcc/d/dmd/objc.d | 11 | ||||
-rw-r--r-- | gcc/d/dmd/opover.d | 1757 | ||||
-rw-r--r-- | gcc/d/dmd/parse.d | 44 | ||||
-rw-r--r-- | gcc/d/dmd/semantic3.d | 2 | ||||
-rw-r--r-- | gcc/d/dmd/tokens.d | 3 | ||||
-rw-r--r-- | gcc/d/dmd/tokens.h | 1 | ||||
-rw-r--r-- | gcc/d/dmd/typesem.d | 27 |
23 files changed, 1079 insertions, 1714 deletions
diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index 4172630..a91f40b 100644 --- a/gcc/d/dmd/MERGE +++ b/gcc/d/dmd/MERGE @@ -1,4 +1,4 @@ -d1157134103a209d36d6ee9c1df1d61d5929ec6d +b7e3b3b61711bf6c6cad27c7b5b73df0e570c215 The first line of this file holds the git revision number of the last merge done from the dlang/dmd repository. diff --git a/gcc/d/dmd/clone.d b/gcc/d/dmd/clone.d index d7658c6..93b6dc3 100644 --- a/gcc/d/dmd/clone.d +++ b/gcc/d/dmd/clone.d @@ -101,7 +101,7 @@ StorageClass mergeFuncAttrs(StorageClass s1, const FuncDeclaration f) pure @safe */ FuncDeclaration hasIdentityOpAssign(AggregateDeclaration ad, Scope* sc) { - Dsymbol assign = search_function(ad, Id.assign); + Dsymbol assign = search_function(ad, Id.opAssign); if (!assign) return null; @@ -303,7 +303,7 @@ FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) auto fparams = new Parameters(); fparams.push(new Parameter(loc, STC.nodtor, sd.type, Id.p, null, null)); auto tf = new TypeFunction(ParameterList(fparams), sd.handleType(), LINK.d, stc | STC.ref_); - auto fop = new FuncDeclaration(declLoc, Loc.initial, Id.assign, stc, tf); + auto fop = new FuncDeclaration(declLoc, Loc.initial, Id.opAssign, stc, tf); fop.storage_class |= STC.inference; fop.isGenerated = true; Expression e; @@ -482,7 +482,7 @@ bool needOpEquals(StructDeclaration sd) private FuncDeclaration hasIdentityOpEquals(AggregateDeclaration ad, Scope* sc) { FuncDeclaration f; - Dsymbol eq = search_function(ad, Id.eq); + Dsymbol eq = search_function(ad, Id.opEquals); if (!eq) return null; @@ -537,7 +537,7 @@ private FuncDeclaration hasIdentityOpEquals(AggregateDeclaration ad, Scope* sc) * opEquals is changed to be never implicitly generated. * Now, struct objects comparison s1 == s2 is translated to: * s1.tupleof == s2.tupleof - * to calculate structural equality. See EqualExp.op_overload. + * to calculate structural equality. See `opOverloadEquals`. */ FuncDeclaration buildOpEquals(StructDeclaration sd, Scope* sc) { @@ -564,7 +564,7 @@ FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc) return null; // bitwise comparison would work //printf("StructDeclaration::buildXopEquals() %s\n", sd.toChars()); - if (Dsymbol eq = search_function(sd, Id.eq)) + if (Dsymbol eq = search_function(sd, Id.opEquals)) { if (FuncDeclaration fd = eq.isFuncDeclaration()) { @@ -639,7 +639,7 @@ FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc) FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) { //printf("StructDeclaration::buildXopCmp() %s\n", toChars()); - if (Dsymbol cmp = search_function(sd, Id.cmp)) + if (Dsymbol cmp = search_function(sd, Id.opCmp)) { if (FuncDeclaration fd = cmp.isFuncDeclaration()) { @@ -667,7 +667,7 @@ FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) * Consider 'alias this', but except opDispatch. */ Expression e = new DsymbolExp(sd.loc, sd); - e = new DotIdExp(sd.loc, e, Id.cmp); + e = new DotIdExp(sd.loc, e, Id.opCmp); Scope* sc2 = sc.push(); e = e.trySemantic(sc2); sc2.pop(); @@ -688,7 +688,7 @@ FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) default: break; } - if (!s || s.ident != Id.cmp) + if (!s || s.ident != Id.opCmp) e = null; // there's no valid member 'opCmp' } if (!e) @@ -736,7 +736,7 @@ FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) fop.parent = sd; Expression e1 = new IdentifierExp(loc, Id.This); Expression e2 = new IdentifierExp(loc, Id.p); - Expression e = new CallExp(loc, new DotIdExp(loc, e1, Id.cmp), e2); + Expression e = new CallExp(loc, new DotIdExp(loc, e1, Id.opCmp), e2); fop.fbody = new ReturnStatement(loc, e); const errors = global.startGagging(); // Do not report errors Scope* sc2 = sc.push(); diff --git a/gcc/d/dmd/ctfeexpr.d b/gcc/d/dmd/ctfeexpr.d index a6cfe6e..6d2a954 100644 --- a/gcc/d/dmd/ctfeexpr.d +++ b/gcc/d/dmd/ctfeexpr.d @@ -1529,11 +1529,9 @@ Expression ctfeCast(UnionExp* pue, const ref Loc loc, Type type, Type to, Expres : tclass.implicitConvTo(to.mutableOf()); if (match) return paint(); - else - { - emplaceExp!(NullExp)(pue, loc, to); - return pue.exp(); - } + + emplaceExp!(NullExp)(pue, loc, to); + return pue.exp(); } // Allow TypeInfo type painting diff --git a/gcc/d/dmd/dcast.d b/gcc/d/dmd/dcast.d index 2f33301..0d95ad4 100644 --- a/gcc/d/dmd/dcast.d +++ b/gcc/d/dmd/dcast.d @@ -4144,20 +4144,6 @@ Expression typeCombine(BinExp be, Scope* sc) return ErrorExp.get(); } - Type t1 = be.e1.type.toBasetype(); - Type t2 = be.e2.type.toBasetype(); - - if (be.op == EXP.min || be.op == EXP.add) - { - // struct+struct, and class+class are errors - if (t1.ty == Tstruct && t2.ty == Tstruct) - return errorReturn(); - if (t1.ty == Tclass && t2.ty == Tclass) - return errorReturn(); - if (t1.ty == Taarray && t2.ty == Taarray) - return errorReturn(); - } - if (auto result = typeMerge(sc, be.op, be.e1, be.e2)) { if (be.type is null) diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d index 0d87f6e..c7fb26a 100644 --- a/gcc/d/dmd/dsymbolsem.d +++ b/gcc/d/dmd/dsymbolsem.d @@ -3061,7 +3061,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (sd.ctor) { - Dsymbol scall = sd.search(Loc.initial, Id.call); + Dsymbol scall = sd.search(Loc.initial, Id.opCall); if (scall) { const xerrors = global.startGagging(); diff --git a/gcc/d/dmd/dtemplate.d b/gcc/d/dmd/dtemplate.d index 7f7437c..dd9f3da 100644 --- a/gcc/d/dmd/dtemplate.d +++ b/gcc/d/dmd/dtemplate.d @@ -217,17 +217,12 @@ Dsymbol getDsymbol(RootObject oarg) return te.td; if (auto te = ea.isScopeExp()) return te.sds; - else - return null; - } - else - { - // Try to convert Type to symbol - if (auto ta = isType(oarg)) - return ta.toDsymbol(null); - else - return isDsymbol(oarg); // if already a symbol + return null; } + // Try to convert Type to symbol + if (auto ta = isType(oarg)) + return ta.toDsymbol(null); + return isDsymbol(oarg); // if already a symbol } @@ -5752,8 +5747,8 @@ MATCH matchArg(TemplateParameter tp, Loc instLoc, Scope* sc, Objects* tiargs, si if (auto ttp = tp.isTemplateTupleParameter()) return matchArgTuple(ttp); - else - return matchArgParameter(); + + return matchArgParameter(); } MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, TemplateParameters* parameters, ref Objects dedtypes, Declaration* psparam) @@ -6221,8 +6216,8 @@ void printTemplateStats(bool listInstances, ErrorSink eSink) auto diff = b.ts.uniqueInstantiations - a.ts.uniqueInstantiations; if (diff) return diff; - else - return b.ts.numInstantiations - a.ts.numInstantiations; + + return b.ts.numInstantiations - a.ts.numInstantiations; } } diff --git a/gcc/d/dmd/expression.d b/gcc/d/dmd/expression.d index 75d6468..5b8e010 100644 --- a/gcc/d/dmd/expression.d +++ b/gcc/d/dmd/expression.d @@ -535,98 +535,6 @@ extern (C++) abstract class Expression : ASTNode return false; } - extern (D) final bool checkScalar() - { - if (op == EXP.error) - return true; - if (type.toBasetype().ty == Terror) - return true; - if (!type.isScalar()) - { - error(loc, "`%s` is not a scalar, it is a `%s`", toChars(), type.toChars()); - return true; - } - return checkValue(); - } - - extern (D) final bool checkNoBool() - { - if (op == EXP.error) - return true; - if (type.toBasetype().ty == Terror) - return true; - if (type.toBasetype().ty == Tbool) - { - error(loc, "operation not allowed on `bool` `%s`", toChars()); - return true; - } - return false; - } - - extern (D) final bool checkIntegral() - { - if (op == EXP.error) - return true; - if (type.toBasetype().ty == Terror) - return true; - if (!type.isIntegral()) - { - error(loc, "`%s` is not of integral type, it is a `%s`", toChars(), type.toChars()); - return true; - } - return checkValue(); - } - - extern (D) final bool checkArithmetic(EXP op) - { - if (op == EXP.error) - return true; - if (type.toBasetype().ty == Terror) - return true; - if (!type.isIntegral() && !type.isFloating()) - { - // unary aggregate ops error here - const char* msg = type.isAggregate() ? - "operator `%s` is not defined for `%s` of type `%s`" : - "illegal operator `%s` for `%s` of type `%s`"; - error(loc, msg, EXPtoString(op).ptr, toChars(), type.toChars()); - return true; - } - return checkValue(); - } - - /******************************* - * Check whether the expression allows RMW operations, error with rmw operator diagnostic if not. - * ex is the RHS expression, or NULL if ++/-- is used (for diagnostics) - * Returns true if error occurs. - */ - extern (D) final bool checkReadModifyWrite(EXP rmwOp, Expression ex = null) - { - //printf("Expression::checkReadModifyWrite() %s %s", toChars(), ex ? ex.toChars() : ""); - if (!type || !type.isShared() || type.isTypeStruct() || type.isTypeClass()) - return false; - - // atomicOp uses opAssign (+=/-=) rather than opOp (++/--) for the CT string literal. - switch (rmwOp) - { - case EXP.plusPlus: - case EXP.prePlusPlus: - rmwOp = EXP.addAssign; - break; - case EXP.minusMinus: - case EXP.preMinusMinus: - rmwOp = EXP.minAssign; - break; - default: - break; - } - - error(loc, "read-modify-write operations are not allowed for `shared` variables"); - errorSupplemental(loc, "Use `core.atomic.atomicOp!\"%s\"(%s, %s)` instead", - EXPtoString(rmwOp).ptr, toChars(), ex ? ex.toChars() : "1"); - return true; - } - /****************************** * Take address of expression. */ @@ -3039,8 +2947,6 @@ extern (C++) abstract class BinExp : Expression { Expression e1; Expression e2; - Type att1; // Save alias this type to detect recursion - Type att2; // Save alias this type to detect recursion extern (D) this(const ref Loc loc, EXP op, Expression e1, Expression e2) scope @safe { @@ -3058,20 +2964,6 @@ extern (C++) abstract class BinExp : Expression return e; } - extern (D) final bool checkIntegralBin() - { - bool r1 = e1.checkIntegral(); - bool r2 = e2.checkIntegral(); - return (r1 || r2); - } - - extern (D) final bool checkArithmeticBin() - { - bool r1 = e1.checkArithmetic(this.op); - bool r2 = e2.checkArithmetic(this.op); - return (r1 || r2); - } - /********************* * Mark the operands as will never be dereferenced, * which is useful info for @safe checks. diff --git a/gcc/d/dmd/expression.h b/gcc/d/dmd/expression.h index d62aea8..07b163f1 100644 --- a/gcc/d/dmd/expression.h +++ b/gcc/d/dmd/expression.h @@ -679,9 +679,6 @@ public: Expression *e1; Expression *e2; - Type *att1; // Save alias this type to detect recursion - Type *att2; // Save alias this type to detect recursion - BinExp *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d index eb4a5f8..fcb47a5 100644 --- a/gcc/d/dmd/expressionsem.d +++ b/gcc/d/dmd/expressionsem.d @@ -663,7 +663,7 @@ Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0) assert(!ae.lengthVar); *pe0 = null; AggregateDeclaration ad = isAggregate(ae.e1.type); - Dsymbol slice = search_function(ad, Id.slice); + Dsymbol slice = search_function(ad, Id.opSlice); //printf("slice = %s %s\n", slice.kind(), slice.toChars()); Expression fallback() { @@ -2403,7 +2403,7 @@ private bool checkPostblit(Type t, ref Loc loc, Scope* sc) /*************************************** * Pull out any properties. */ -private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = null, BinExp saveAtts = null) +private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = null, Type[2]* aliasThisStop = null) { //printf("resolvePropertiesX, e1 = %s %s, e2 = %s\n", EXPtoString(e1.op).ptr, e1.toChars(), e2 ? e2.toChars() : null); Loc loc = e1.loc; @@ -2478,11 +2478,9 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = if (e2) { e = new AssignExp(loc, e, e2); - if (saveAtts) - { - (cast(BinExp)e).att1 = saveAtts.att1; - (cast(BinExp)e).att2 = saveAtts.att2; - } + if (aliasThisStop) + return e.expressionSemantic(sc, *aliasThisStop); + return e.expressionSemantic(sc); } return e.expressionSemantic(sc); } @@ -2584,11 +2582,8 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = if (e2) { e = new AssignExp(loc, e, e2); - if (saveAtts) - { - (cast(BinExp)e).att1 = saveAtts.att1; - (cast(BinExp)e).att2 = saveAtts.att2; - } + if (aliasThisStop) + return e.expressionSemantic(sc, *aliasThisStop); } return e.expressionSemantic(sc); } @@ -3816,6 +3811,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Scope* sc; Expression result; + // For binary expressions, stores recursive 'alias this' types of lhs and rhs to prevent endless loops. + // See tryAliasThisSemantic + Type[2] aliasThisStop; + this(Scope* sc) scope @safe { this.sc = sc; @@ -6206,7 +6205,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } // No constructor, look for overload of opCall - if (search_function(sd, Id.call)) + if (search_function(sd, Id.opCall)) goto L1; // overload of opCall, therefore it's a call if (exp.e1.op != EXP.type) @@ -6247,7 +6246,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { L1: // Rewrite as e1.call(arguments) - Expression e = new DotIdExp(exp.loc, exp.e1, Id.call); + Expression e = new DotIdExp(exp.loc, exp.e1, Id.opCall); e = new CallExp(exp.loc, e, exp.arguments, exp.names); e = e.expressionSemantic(sc); result = e; @@ -7536,13 +7535,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(BinAssignExp exp) { - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinaryAssign(sc, aliasThisStop)) { result = e; return; } + Expression e; if (exp.e1.op == EXP.arrayLength) { // arr.length op= e2; @@ -7933,7 +7932,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { const callExpIdent = callExpFunc.ident; isEqualsCallExpression = callExpIdent == Id.__equals || - callExpIdent == Id.eq; + callExpIdent == Id.opEquals; } } if (op == EXP.equal || op == EXP.notEqual || @@ -8777,8 +8776,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("PtrExp::semantic('%s')\n", exp.toChars()); } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadUnary(sc)) { result = e; return; @@ -8835,8 +8833,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("NegExp::semantic('%s')\n", exp.toChars()); } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadUnary(sc)) { result = e; return; @@ -8876,8 +8873,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("UAddExp::semantic('%s')\n", exp.toChars()); } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadUnary(sc)) { result = e; return; @@ -8902,8 +8898,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(ComExp exp) { - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadUnary(sc)) { result = e; return; @@ -8975,17 +8970,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(DeleteExp exp) { - // @@@DEPRECATED_2.109@@@ - // 1. Deprecated since 2.079 - // 2. Error since 2.099 - // 3. Removal of keyword, "delete" can be used for other identities - if (!exp.isRAII) - { - error(exp.loc, "the `delete` keyword is obsolete"); - errorSupplemental(exp.loc, "use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead"); - return setError(); - } - Expression e = exp; if (Expression ex = unaSemantic(exp, sc)) @@ -9176,7 +9160,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!exp.to.equals(exp.e1.type) && exp.mod == cast(ubyte)~0) { - if (Expression e = exp.op_overload(sc)) + if (Expression e = exp.opOverloadCast(sc)) { result = e.implicitCastTo(sc, exp.to); return; @@ -9466,8 +9450,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor error(exp.loc, "upper and lower bounds are needed to slice a pointer"); if (auto ad = isAggregate(tp.next.toBasetype())) { - auto s = search_function(ad, Id.index); - if (!s) s = search_function(ad, Id.slice); + auto s = search_function(ad, Id.opIndex); + if (!s) s = search_function(ad, Id.opSlice); if (s) { auto fd = s.isFuncDeclaration(); @@ -9710,8 +9694,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (result) return; - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadArray(sc)) { result = e; return; @@ -10133,13 +10116,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } exp.e1 = e1x; - Expression e = exp.op_overload(sc); - if (e) - { - result = e; - return; - } - if (exp.e1.checkReadModifyWrite(exp.op)) return setError(); @@ -10182,7 +10158,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // Combine de,ea,eb,ec if (de) ea = new CommaExp(exp.loc, de, ea); - e = new CommaExp(exp.loc, ea, eb); + Expression e = new CommaExp(exp.loc, ea, eb); e = new CommaExp(exp.loc, e, ec); e = e.expressionSemantic(sc); result = e; @@ -10192,7 +10168,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.e1 = exp.e1.modifiableLvalue(sc); exp.e1 = exp.e1.optimize(WANTvalue, /*keepLvalue*/ true); - e = exp; + Expression e = exp; if (exp.e1.checkScalar() || exp.e1.checkSharedAccess(sc)) return setError(); @@ -10209,15 +10185,15 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(PreExp exp) { - Expression e = exp.op_overload(sc); // printf("PreExp::semantic('%s')\n", toChars()); - if (e) + if (Expression e = exp.opOverloadUnary(sc)) { result = e; return; } // Rewrite as e1+=1 or e1-=1 + Expression e; if (exp.op == EXP.prePlusPlus) e = new AddAssignExp(exp.loc, exp.e1, IntegerExp.literal!1); else @@ -10328,7 +10304,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor AggregateDeclaration ad = isAggregate(t1b); if (!ad) break; - if (search_function(ad, Id.indexass)) + if (search_function(ad, Id.opIndexAssign)) { // Deal with $ res = resolveOpDollar(sc, ae, &e0); @@ -10347,7 +10323,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ Expressions* a = ae.arguments.copy(); a.insert(0, exp.e2); - res = new DotIdExp(exp.loc, ae.e1, Id.indexass); + res = new DotIdExp(exp.loc, ae.e1, Id.opIndexAssign); res = new CallExp(exp.loc, res, a); if (maybeSlice) // a[] = e2 might be: a.opSliceAssign(e2) res = res.trySemantic(sc); @@ -10358,7 +10334,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } Lfallback: - if (maybeSlice && search_function(ad, Id.sliceass)) + if (maybeSlice && search_function(ad, Id.opSliceAssign)) { // Deal with $ res = resolveOpDollar(sc, ae, ie, &e0); @@ -10381,7 +10357,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor a.push(ie.lwr); a.push(ie.upr); } - res = new DotIdExp(exp.loc, ae.e1, Id.sliceass); + res = new DotIdExp(exp.loc, ae.e1, Id.opSliceAssign); res = new CallExp(exp.loc, res, a); res = res.expressionSemantic(sc); return setResult(Expression.combine(e0, res)); @@ -10471,7 +10447,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor * or: * f() = value */ - if (Expression e = resolvePropertiesX(sc, e1x, exp.e2, exp)) + if (Expression e = resolvePropertiesX(sc, e1x, exp.e2, &aliasThisStop)) return setResult(e); if (e1x.checkRightThis(sc)) @@ -10604,6 +10580,35 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (exp.op == EXP.assign && exp.e1.checkModifiable(sc) == Modifiable.initialization) { + // Check common mistake of misspelled parameters in constructors, + // e.g. `this(int feild) { this.field = field; }` + if (auto dve1 = exp.e1.isDotVarExp) + if (auto dve2 = exp.e2.isDotVarExp) + if (sc.func && sc.func.parameters && dve1.e1.isThisExp && dve2.e1.isThisExp() + && dve1.var.ident.equals(dve2.var.ident)) + { + // @@@DEPRECATED_2.121@@@ + // Deprecated in 2.111, make it an error in 2.121 + deprecation(exp.e1.loc, "cannot initialize field `%s` with itself", dve1.var.toChars()); + auto findParameter(const(char)[] s, ref int cost) + { + foreach (p; *sc.func.parameters) + { + if (p.ident.toString == s) + { + cost = 1; + return p.ident.toString; + } + } + return null; + } + import dmd.root.speller : speller; + if (auto s = speller!findParameter(dve1.var.ident.toString)) + { + deprecationSupplemental(sc.func.loc, "did you mean to use parameter `%.*s`?\n", s.fTuple.expand); + } + } + //printf("[%s] change to init - %s\n", exp.loc.toChars(), exp.toChars()); auto t = exp.type; exp = new ConstructExp(exp.loc, exp.e1, exp.e2); @@ -10826,13 +10831,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!e2x.implicitConvTo(t1)) { AggregateDeclaration ad2 = isAggregate(e2x.type); - if (ad2 && ad2.aliasthis && !isRecursiveAliasThis(exp.att2, exp.e2.type)) + if (ad2 && ad2.aliasthis && !isRecursiveAliasThis(aliasThisStop[1], exp.e2.type)) { /* Rewrite (e1 op e2) as: * (e1 op e2.aliasthis) */ exp.e2 = new DotIdExp(exp.e2.loc, exp.e2, ad2.aliasthis.ident); - result = exp.expressionSemantic(sc); + result = exp.expressionSemantic(sc, aliasThisStop); return; } } @@ -10887,14 +10892,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = e; return; } - if (search_function(sd, Id.call)) + if (search_function(sd, Id.opCall)) { /* Look for static opCall * https://issues.dlang.org/show_bug.cgi?id=2702 * Rewrite as: * e1 = typeof(e1).opCall(arguments) */ - e2x = typeDotIdExp(e2x.loc, e1x.type, Id.call); + e2x = typeDotIdExp(e2x.loc, e1x.type, Id.opCall); e2x = new CallExp(exp.loc, e2x, exp.e2); e2x = e2x.expressionSemantic(sc); @@ -10911,13 +10916,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor else // https://issues.dlang.org/show_bug.cgi?id=11355 { AggregateDeclaration ad2 = isAggregate(e2x.type); - if (ad2 && ad2.aliasthis && !isRecursiveAliasThis(exp.att2, exp.e2.type)) + if (ad2 && ad2.aliasthis && !isRecursiveAliasThis(aliasThisStop[1], exp.e2.type)) { /* Rewrite (e1 op e2) as: * (e1 op e2.aliasthis) */ exp.e2 = new DotIdExp(exp.e2.loc, exp.e2, ad2.aliasthis.ident); - result = exp.expressionSemantic(sc); + result = exp.expressionSemantic(sc, aliasThisStop); return; } } @@ -10958,8 +10963,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor ae.e1 = ae.e1.expressionSemantic(sc); ae.e1 = ae.e1.optimize(WANTvalue); ae.e2 = ev; - Expression e = ae.op_overload(sc); - if (e) + if (Expression e = ae.opOverloadAssign(sc, aliasThisStop)) { Expression ey = null; if (t2.ty == Tstruct && sd == t2.toDsymbol(sc)) @@ -11009,14 +11013,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } } - else + else if (Expression e = exp.isAssignExp().opOverloadAssign(sc, aliasThisStop)) { - Expression e = exp.op_overload(sc); - if (e) - { - result = e; - return; - } + result = e; + return; } } else @@ -11033,8 +11033,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // Disallow assignment operator overloads for same type if (exp.op == EXP.assign && !exp.e2.implicitConvTo(exp.e1.type)) { - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.isAssignExp().opOverloadAssign(sc, aliasThisStop)) { result = e; return; @@ -11410,7 +11409,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (e2x.op == EXP.error && exp.op == EXP.construct && t1.ty == Tstruct) { scope sd = (cast(TypeStruct)t1).sym; - Dsymbol opAssign = search_function(sd, Id.assign); + Dsymbol opAssign = search_function(sd, Id.opAssign); // and the struct defines an opAssign if (opAssign) @@ -11705,9 +11704,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(PowAssignExp exp) { - - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinaryAssign(sc, aliasThisStop)) { result = e; return; @@ -11754,7 +11751,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if ((exp.e1.type.isIntegral() || exp.e1.type.isFloating()) && (exp.e2.type.isIntegral() || exp.e2.type.isFloating())) { Expression e0 = null; - e = exp.reorderSettingAAElem(sc); + Expression e = exp.reorderSettingAAElem(sc); e = Expression.extractLast(e, e0); assert(e == exp); @@ -11786,8 +11783,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { //printf("CatAssignExp::semantic() %s\n", exp.toChars()); - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinaryAssign(sc, aliasThisStop)) { result = e; return; @@ -11881,49 +11877,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else { - // Try alias this on first operand - static Expression tryAliasThisForLhs(BinAssignExp exp, Scope* sc) - { - AggregateDeclaration ad1 = isAggregate(exp.e1.type); - if (!ad1 || !ad1.aliasthis) - return null; - - /* Rewrite (e1 op e2) as: - * (e1.aliasthis op e2) - */ - if (isRecursiveAliasThis(exp.att1, exp.e1.type)) - return null; - //printf("att %s e1 = %s\n", Token.toChars(e.op), e.e1.type.toChars()); - Expression e1 = new DotIdExp(exp.loc, exp.e1, ad1.aliasthis.ident); - BinExp be = cast(BinExp)exp.copy(); - be.e1 = e1; - return be.trySemantic(sc); - } - - // Try alias this on second operand - static Expression tryAliasThisForRhs(BinAssignExp exp, Scope* sc) - { - AggregateDeclaration ad2 = isAggregate(exp.e2.type); - if (!ad2 || !ad2.aliasthis) - return null; - /* Rewrite (e1 op e2) as: - * (e1 op e2.aliasthis) - */ - if (isRecursiveAliasThis(exp.att2, exp.e2.type)) - return null; - //printf("att %s e2 = %s\n", Token.toChars(e.op), e.e2.type.toChars()); - Expression e2 = new DotIdExp(exp.loc, exp.e2, ad2.aliasthis.ident); - BinExp be = cast(BinExp)exp.copy(); - be.e2 = e2; - return be.trySemantic(sc); - } - Laliasthis: - result = tryAliasThisForLhs(exp, sc); + result = checkAliasThisForLhs(isAggregate(exp.e1.type), sc, exp, aliasThisStop); if (result) return; - result = tryAliasThisForRhs(exp, sc); + result = checkAliasThisForRhs(isAggregate(exp.e2.type), sc, exp, aliasThisStop); if (result) return; @@ -12076,13 +12035,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("AddExp::semantic('%s')\n", exp.toChars()); } - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinary(sc, aliasThisStop)) { result = e; return; @@ -12139,6 +12092,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } + if (exp.checkArithmeticBin()) + return setError(); + tb1 = exp.e1.type.toBasetype(); if (!target.isVectorOpSupported(tb1, exp.op, tb2)) { @@ -12178,13 +12134,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("MinExp::semantic('%s')\n", exp.toChars()); } - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinary(sc, aliasThisStop)) { result = e; return; @@ -12217,6 +12167,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (t1.ty == Tpointer) { + Expression e; if (t2.ty == Tpointer) { // https://dlang.org/spec/expression.html#add_expressions @@ -12294,6 +12245,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } + if (exp.checkArithmeticBin()) + return setError(); + t1 = exp.e1.type.toBasetype(); t2 = exp.e2.type.toBasetype(); if (!target.isVectorOpSupported(t1, exp.op, t2)) @@ -12422,14 +12376,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { // https://dlang.org/spec/expression.html#cat_expressions //printf("CatExp.semantic() %s\n", toChars()); - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinary(sc, aliasThisStop)) { result = e; return; @@ -12581,6 +12528,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } Type t1 = exp.e1.type.toBasetype(); Type t2 = exp.e2.type.toBasetype(); + Expression e; if ((t1.ty == Tarray || t1.ty == Tsarray) && (t2.ty == Tarray || t2.ty == Tsarray)) { @@ -12598,30 +12546,25 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor trySetCatExpLowering(result); } - override void visit(MulExp exp) + bool commonBinOpSemantic(BinExp exp) { - version (none) - { - printf("MulExp::semantic() %s\n", exp.toChars()); - } - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinary(sc, aliasThisStop)) { result = e; - return; + return true; } if (Expression ex = typeCombine(exp, sc)) { result = ex; - return; + return true; } + return false; + } + bool commonArithBinOpSemantic(BinExp exp) + { + if (commonBinOpSemantic(exp)) + return true; Type tb = exp.type.toBasetype(); if (tb.ty == Tarray || tb.ty == Tsarray) @@ -12629,15 +12572,28 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!isArrayOpValid(exp)) { result = arrayOpInvalidError(exp); - return; + return true; } result = exp; - return; + return true; } if (exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc)) - return setError(); + { + setError(); + return true; + } + return false; + } + override void visit(MulExp exp) + { + version (none) + { + printf("MulExp::semantic() %s\n", exp.toChars()); + } + if (commonArithBinOpSemantic(exp)) + return; if (exp.type.isFloating()) { Type t1 = exp.e1.type; @@ -12676,7 +12632,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // iy * iv = -yv exp.e1.type = exp.type; exp.e2.type = exp.type; - e = new NegExp(exp.loc, exp); + Expression e = new NegExp(exp.loc, exp); e = e.expressionSemantic(sc); result = e; return; @@ -12689,7 +12645,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.type = t1; // t1 is complex } } - else if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype())) + else if (!target.isVectorOpSupported(exp.type.toBasetype(), exp.op, exp.e2.type.toBasetype())) { result = exp.incompatibleTypes(); return; @@ -12699,39 +12655,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(DivExp exp) { - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) - { - result = e; - return; - } - - if (Expression ex = typeCombine(exp, sc)) - { - result = ex; - return; - } - - Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) - { - if (!isArrayOpValid(exp)) - { - result = arrayOpInvalidError(exp); - return; - } - result = exp; + if (commonArithBinOpSemantic(exp)) return; - } - - if (exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc)) - return setError(); if (exp.type.isFloating()) { @@ -12745,7 +12670,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { // x/iv = i(-x/v) exp.e2.type = t1; - e = new NegExp(exp.loc, exp); + Expression e = new NegExp(exp.loc, exp); e = e.expressionSemantic(sc); result = e; return; @@ -12785,7 +12710,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.type = t1; // t1 is complex } } - else if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype())) + else if (!target.isVectorOpSupported(exp.type.toBasetype(), exp.op, exp.e2.type.toBasetype())) { result = exp.incompatibleTypes(); return; @@ -12795,45 +12720,15 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(ModExp exp) { - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) - { - result = e; + if (commonArithBinOpSemantic(exp)) return; - } - if (Expression ex = typeCombine(exp, sc)) - { - result = ex; - return; - } - - Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) - { - if (!isArrayOpValid(exp)) - { - result = arrayOpInvalidError(exp); - return; - } - result = exp; - return; - } - if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype())) + if (!target.isVectorOpSupported(exp.type.toBasetype(), exp.op, exp.e2.type.toBasetype())) { result = exp.incompatibleTypes(); return; } - if (exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc)) - return setError(); - if (exp.type.isFloating()) { exp.type = exp.e1.type; @@ -12848,49 +12743,17 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(PowExp exp) { - - //printf("PowExp::semantic() %s\n", toChars()); - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) - { - result = e; + if (commonArithBinOpSemantic(exp)) return; - } - - if (Expression ex = typeCombine(exp, sc)) - { - result = ex; - return; - } - - Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) - { - if (!isArrayOpValid(exp)) - { - result = arrayOpInvalidError(exp); - return; - } - result = exp; - return; - } - - if (exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc)) - return setError(); - if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype())) + if (!target.isVectorOpSupported(exp.type.toBasetype(), exp.op, exp.e2.type.toBasetype())) { result = exp.incompatibleTypes(); return; } // First, attempt to fold the expression. - e = exp.optimize(WANTvalue); + Expression e = exp.optimize(WANTvalue); if (e.op != EXP.pow) { e = e.expressionSemantic(sc); @@ -12923,14 +12786,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor private void visitShift(BinExp exp) { - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinary(sc, aliasThisStop)) { result = e; return; @@ -12971,14 +12827,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor private void visitBinaryBitOp(BinExp exp) { - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinary(sc, aliasThisStop)) { result = e; return; @@ -13136,57 +12985,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); } - - EXP cmpop = exp.op; - if (auto e = exp.op_overload(sc, &cmpop)) + if (auto e = exp.opOverloadCmp(sc, aliasThisStop)) { - if (!e.type.isScalar() && e.type.equals(exp.e1.type)) - { - error(exp.loc, "recursive `opCmp` expansion"); - return setError(); - } - if (e.op == EXP.call) - { - - if (t1.ty == Tclass && t2.ty == Tclass) - { - // Lower to object.__cmp(e1, e2) - Expression cl = new IdentifierExp(exp.loc, Id.empty); - cl = new DotIdExp(exp.loc, cl, Id.object); - cl = new DotIdExp(exp.loc, cl, Id.__cmp); - cl = cl.expressionSemantic(sc); - - auto arguments = new Expressions(); - // Check if op_overload found a better match by calling e2.opCmp(e1) - // If the operands were swapped, then the result must be reversed - // e1.opCmp(e2) == -e2.opCmp(e1) - // cmpop takes care of this - if (exp.op == cmpop) - { - arguments.push(exp.e1); - arguments.push(exp.e2); - } - else - { - // Use better match found by op_overload - arguments.push(exp.e2); - arguments.push(exp.e1); - } - - cl = new CallExp(exp.loc, cl, arguments); - cl = new CmpExp(cmpop, exp.loc, cl, new IntegerExp(0)); - result = cl.expressionSemantic(sc); - return; - } - - e = new CmpExp(cmpop, exp.loc, e, IntegerExp.literal!0); - e = e.expressionSemantic(sc); - } result = e; return; } - if (Expression ex = typeCombine(exp, sc)) { result = ex; @@ -13284,14 +13088,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(InExp exp) { - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinary(sc, aliasThisStop)) { result = e; return; @@ -13455,13 +13252,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return false; } - if (auto e = exp.op_overload(sc)) + if (auto e = exp.opOverloadEqual(sc, aliasThisStop)) { result = e; return; } - const isArrayComparison = (t1.ty == Tarray || t1.ty == Tsarray) && (t2.ty == Tarray || t2.ty == Tsarray); const needsArrayLowering = isArrayComparison && needsDirectEq(t1, t2, sc); @@ -13931,6 +13727,32 @@ Expression trySemantic(Expression exp, Scope* sc) return e; } +/********************************** + * Try expression semantic on `exp`, gagging semantic errors, + * but don't resolve alias this on a BinExp when the lhs or rhs + * has the corresponding type in `aliasThisStop` (See `isRecursiveAliasThis`). + * + * Params: + * exp = expression to try semantic on + * sc = scope + * aliasThisStop = pair of recursive alias this types to stop endless recursion + * Returns: + * exp after expression semantic, or `null` on error + */ +Expression trySemanticAliasThis(Expression exp, Scope* sc, Type[2] aliasThisStop) +{ + if (exp.expressionSemanticDone) + return exp; + + const errors = global.startGagging(); + Expression e = expressionSemantic(exp, sc, aliasThisStop); + + if (global.endGagging(errors)) + return null; + + return e; +} + /************************** * Helper function for easy error propagation. * If error occurs, returns ErrorExp. Otherwise returns NULL. @@ -14015,6 +13837,18 @@ Expression expressionSemantic(Expression e, Scope* sc) return v.result; } +// ditto, but passes alias this stop types, see trySemanticAliasThis +private Expression expressionSemantic(Expression e, Scope* sc, Type[2] aliasThisStop) +{ + if (e.expressionSemanticDone) + return e; + + scope v = new ExpressionSemanticVisitor(sc); + v.aliasThisStop = aliasThisStop; + e.accept(v); + return v.result; +} + private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc) { //printf("dotIdSemanticPropX() %s\n", toChars(exp)); @@ -14849,6 +14683,105 @@ MATCH matchType(FuncExp funcExp, Type to, Scope* sc, FuncExp* presult, ErrorSink return m; } +private bool checkScalar(Expression e) +{ + if (e.op == EXP.error) + return true; + if (e.type.toBasetype().ty == Terror) + return true; + if (!e.type.isScalar()) + { + error(e.loc, "`%s` is not a scalar, it is a `%s`", e.toChars(), e.type.toChars()); + return true; + } + return e.checkValue(); +} + +private bool checkNoBool(Expression e) +{ + if (e.op == EXP.error) + return true; + if (e.type.toBasetype().ty == Terror) + return true; + if (e.type.toBasetype().ty == Tbool) + { + error(e.loc, "operation not allowed on `bool` `%s`", e.toChars()); + return true; + } + return false; +} + +private bool checkIntegral(Expression e) +{ + if (e.op == EXP.error) + return true; + if (e.type.toBasetype().ty == Terror) + return true; + if (!e.type.isIntegral()) + { + error(e.loc, "`%s` is not of integral type, it is a `%s`", e.toChars(), e.type.toChars()); + return true; + } + return e.checkValue(); +} + +private bool checkArithmetic(Expression e, EXP op) +{ + if (op == EXP.error) + return true; + if (e.type.toBasetype().ty == Terror) + return true; + if (!e.type.isIntegral() && !e.type.isFloating()) + { + // unary aggregate ops error here + const char* msg = e.type.isAggregate() ? + "operator `%s` is not defined for `%s` of type `%s`" : + "illegal operator `%s` for `%s` of type `%s`"; + error(e.loc, msg, EXPtoString(op).ptr, e.toChars(), e.type.toChars()); + return true; + } + + // FIXME: Existing code relies on adding / subtracting types in typeof() expressions: + // alias I = ulong; alias U = typeof(I + 1u); + // https://github.com/dlang/dmd/issues/20763 + if (op == EXP.add || op == EXP.min) + return false; + + return e.checkValue(); +} + +/******************************* + * Check whether the expression allows RMW operations, error with rmw operator diagnostic if not. + * ex is the RHS expression, or NULL if ++/-- is used (for diagnostics) + * Returns true if error occurs. + */ +private bool checkReadModifyWrite(Expression e, EXP rmwOp, Expression ex = null) +{ + //printf("Expression::checkReadModifyWrite() %s %s", toChars(), ex ? ex.toChars() : ""); + if (!e.type || !e.type.isShared() || e.type.isTypeStruct() || e.type.isTypeClass()) + return false; + + // atomicOp uses opAssign (+=/-=) rather than opOp (++/--) for the CT string literal. + switch (rmwOp) + { + case EXP.plusPlus: + case EXP.prePlusPlus: + rmwOp = EXP.addAssign; + break; + case EXP.minusMinus: + case EXP.preMinusMinus: + rmwOp = EXP.minAssign; + break; + default: + break; + } + + error(e.loc, "read-modify-write operations are not allowed for `shared` variables"); + errorSupplemental(e.loc, "Use `core.atomic.atomicOp!\"%s\"(%s, %s)` instead", + EXPtoString(rmwOp).ptr, e.toChars(), ex ? ex.toChars() : "1"); + return true; +} + private bool checkSharedAccessBin(BinExp binExp, Scope* sc) { const r1 = binExp.e1.checkSharedAccess(sc); @@ -14856,6 +14789,18 @@ private bool checkSharedAccessBin(BinExp binExp, Scope* sc) return (r1 || r2); } +private bool checkIntegralBin(BinExp e) +{ + bool r1 = e.e1.checkIntegral(); + bool r2 = e.e2.checkIntegral(); + return (r1 || r2); +} + +private bool checkArithmeticBin(BinExp e) +{ + return (e.e1.checkArithmetic(e.op) || e.e2.checkArithmetic(e.op)); +} + /*************************************** * If expression is shared, check that we can access it. * Give error message if not. @@ -16451,7 +16396,7 @@ Expression toBoolean(Expression exp, Scope* sc) /* Don't really need to check for opCast first, but by doing so we * get better error messages if it isn't there. */ - if (Dsymbol fd = search_function(ad, Id._cast)) + if (Dsymbol fd = search_function(ad, Id.opCast)) { e = new CastExp(exp.loc, e, Type.tbool); e = e.expressionSemantic(sc); diff --git a/gcc/d/dmd/func.d b/gcc/d/dmd/func.d index 993bce8..70f4c83 100644 --- a/gcc/d/dmd/func.d +++ b/gcc/d/dmd/func.d @@ -581,8 +581,7 @@ extern (C++) class FuncDeclaration : Declaration { if (isMain()) return "D main"; - else - return Dsymbol.toPrettyChars(QualifyTypes); + return Dsymbol.toPrettyChars(QualifyTypes); } /** for diagnostics, e.g. 'int foo(int x, int y) pure' */ diff --git a/gcc/d/dmd/hdrgen.d b/gcc/d/dmd/hdrgen.d index d391bdb..961a0d2 100644 --- a/gcc/d/dmd/hdrgen.d +++ b/gcc/d/dmd/hdrgen.d @@ -2435,7 +2435,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt void visitSymOff(SymOffExp e) { if (e.offset) - buf.printf("(& %s%+lld)", e.var.toChars(), e.offset); + buf.printf("(& %s + %llu)", e.var.toChars(), e.offset); else if (e.var.isTypeInfoDeclaration()) buf.writestring(e.var.toChars()); else diff --git a/gcc/d/dmd/id.d b/gcc/d/dmd/id.d index ee4214a..9833e198 100644 --- a/gcc/d/dmd/id.d +++ b/gcc/d/dmd/id.d @@ -223,60 +223,15 @@ immutable Msgtable[] msgtable = { "__LOCAL_SIZE" }, // For operator overloads - { "uadd", "opPos" }, - { "neg", "opNeg" }, - { "com", "opCom" }, - { "add", "opAdd" }, - { "add_r", "opAdd_r" }, - { "sub", "opSub" }, - { "sub_r", "opSub_r" }, - { "mul", "opMul" }, - { "mul_r", "opMul_r" }, - { "div", "opDiv" }, - { "div_r", "opDiv_r" }, - { "mod", "opMod" }, - { "mod_r", "opMod_r" }, - { "eq", "opEquals" }, - { "cmp", "opCmp" }, - { "iand", "opAnd" }, - { "iand_r", "opAnd_r" }, - { "ior", "opOr" }, - { "ior_r", "opOr_r" }, - { "ixor", "opXor" }, - { "ixor_r", "opXor_r" }, - { "shl", "opShl" }, - { "shl_r", "opShl_r" }, - { "shr", "opShr" }, - { "shr_r", "opShr_r" }, - { "ushr", "opUShr" }, - { "ushr_r", "opUShr_r" }, - { "cat", "opCat" }, - { "cat_r", "opCat_r" }, - { "assign", "opAssign" }, - { "addass", "opAddAssign" }, - { "subass", "opSubAssign" }, - { "mulass", "opMulAssign" }, - { "divass", "opDivAssign" }, - { "modass", "opModAssign" }, - { "andass", "opAndAssign" }, - { "orass", "opOrAssign" }, - { "xorass", "opXorAssign" }, - { "shlass", "opShlAssign" }, - { "shrass", "opShrAssign" }, - { "ushrass", "opUShrAssign" }, - { "catass", "opCatAssign" }, - { "postinc", "opPostInc" }, - { "postdec", "opPostDec" }, - { "index", "opIndex" }, - { "indexass", "opIndexAssign" }, - { "slice", "opSlice" }, - { "sliceass", "opSliceAssign" }, - { "call", "opCall" }, - { "_cast", "opCast" }, - { "opIn" }, - { "opIn_r" }, - { "opStar" }, - { "opDot" }, + { "opEquals" }, + { "opCmp" }, + { "opAssign" }, + { "opIndex" }, + { "opIndexAssign" }, + { "opSlice" }, + { "opSliceAssign" }, + { "opCall" }, + { "opCast" }, { "opDispatch" }, { "opDollar" }, { "opUnary" }, @@ -287,9 +242,6 @@ immutable Msgtable[] msgtable = { "opOpAssign" }, { "opIndexOpAssign" }, { "opSliceOpAssign" }, - { "pow", "opPow" }, - { "pow_r", "opPow_r" }, - { "powass", "opPowAssign" }, { "classNew", "new" }, { "classDelete", "delete" }, diff --git a/gcc/d/dmd/initsem.d b/gcc/d/dmd/initsem.d index aec6807..c181d53 100644 --- a/gcc/d/dmd/initsem.d +++ b/gcc/d/dmd/initsem.d @@ -489,7 +489,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn else i.exp = e.optimize(WANTvalue); } - else if (search_function(sd, Id.call)) + else if (search_function(sd, Id.opCall)) { /* https://issues.dlang.org/show_bug.cgi?id=1547 * @@ -499,7 +499,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn * i.exp = typeof(sd).opCall(arguments) */ - Expression e = typeDotIdExp(i.loc, sd.type, Id.call); + Expression e = typeDotIdExp(i.loc, sd.type, Id.opCall); e = new CallExp(i.loc, e, i.exp); e = e.expressionSemantic(sc); e = resolveProperties(sc, e); diff --git a/gcc/d/dmd/mangle/cpp.d b/gcc/d/dmd/mangle/cpp.d index 67c9b53..4932b01 100644 --- a/gcc/d/dmd/mangle/cpp.d +++ b/gcc/d/dmd/mangle/cpp.d @@ -60,11 +60,11 @@ package CppOperator isCppOperator(const scope Identifier id) { with (Id) with (CppOperator) { - return (id == _cast) ? Cast : - (id == assign) ? Assign : - (id == eq) ? Eq : - (id == index) ? Index : - (id == call) ? Call : + return (id == opCast) ? Cast : + (id == opAssign) ? Assign : + (id == opEquals) ? Eq : + (id == opIndex) ? Index : + (id == opCall) ? Call : (id == opUnary) ? Unary : (id == opBinary) ? Binary : (id == opOpAssign) ? OpAssign : @@ -435,10 +435,10 @@ private final class CppMangleVisitor : Visitor // 4. null pointer: std::nullptr_t (since C++11) if (t.ty == Tvoid || t.ty == Tbool) return true; - else if (t.ty == Tnull && global.params.cplusplus >= CppStdRevision.cpp11) + if (t.ty == Tnull && global.params.cplusplus >= CppStdRevision.cpp11) return true; - else - return t.isTypeBasic() && (t.isIntegral() || t.isReal()); + + return t.isTypeBasic() && (t.isIntegral() || t.isReal()); } /****************************** @@ -1102,13 +1102,13 @@ private final class CppMangleVisitor : Visitor buf.writestring(ctor.isCpCtor ? "C2" : "C1"); else if (d.isAggregateDtor()) buf.writestring("D1"); - else if (d.ident && d.ident == Id.assign) + else if (d.ident && d.ident == Id.opAssign) buf.writestring("aS"); - else if (d.ident && d.ident == Id.eq) + else if (d.ident && d.ident == Id.opEquals) buf.writestring("eq"); - else if (d.ident && d.ident == Id.index) + else if (d.ident && d.ident == Id.opIndex) buf.writestring("ix"); - else if (d.ident && d.ident == Id.call) + else if (d.ident && d.ident == Id.opCall) buf.writestring("cl"); else source_name(d, true); @@ -1939,21 +1939,21 @@ extern(C++): //printf("enum id = '%s'\n", id.toChars()); if (id == Id.__c_long) return writeBasicType(t, 0, 'l'); - else if (id == Id.__c_ulong) + if (id == Id.__c_ulong) return writeBasicType(t, 0, 'm'); - else if (id == Id.__c_char) + if (id == Id.__c_char) return writeBasicType(t, 0, 'c'); - else if (id == Id.__c_wchar_t) + if (id == Id.__c_wchar_t) return writeBasicType(t, 0, 'w'); - else if (id == Id.__c_longlong) + if (id == Id.__c_longlong) return writeBasicType(t, 0, 'x'); - else if (id == Id.__c_ulonglong) + if (id == Id.__c_ulonglong) return writeBasicType(t, 0, 'y'); - else if (id == Id.__c_complex_float) + if (id == Id.__c_complex_float) return Type.tcomplex32.accept(this); - else if (id == Id.__c_complex_double) + if (id == Id.__c_complex_double) return Type.tcomplex64.accept(this); - else if (id == Id.__c_complex_real) + if (id == Id.__c_complex_real) return Type.tcomplex80.accept(this); doSymbol(t); @@ -2362,8 +2362,7 @@ private bool isNamespaceEqual (CPPNamespaceDeclaration a, Nspace b, size_t idx = // We need to see if there's more ident enclosing if (auto pb = b.toParent().isNspace()) return isNamespaceEqual(a.cppnamespace, pb); - else - return a.cppnamespace is null; + return a.cppnamespace is null; } /// Returns: diff --git a/gcc/d/dmd/mtype.d b/gcc/d/dmd/mtype.d index f91687b..b270943 100644 --- a/gcc/d/dmd/mtype.d +++ b/gcc/d/dmd/mtype.d @@ -1295,17 +1295,15 @@ extern (C++) abstract class Type : ASTNode { if (t.isWildConst()) return MODFlags.wild; - else - return MODFlags.wildconst; + return MODFlags.wildconst; } - else if (isWild()) + if (isWild()) return MODFlags.wild; - else if (isConst()) + if (isConst()) return MODFlags.const_; - else if (isMutable()) + if (isMutable()) return MODFlags.mutable; - else - assert(0); + assert(0); } return 0; } diff --git a/gcc/d/dmd/mustuse.d b/gcc/d/dmd/mustuse.d index fc7618b..4fc117e 100644 --- a/gcc/d/dmd/mustuse.d +++ b/gcc/d/dmd/mustuse.d @@ -111,26 +111,13 @@ private bool isAssignmentOpId(Identifier id) { import dmd.id : Id; - return id == Id.assign - || id == Id.addass - || id == Id.subass - || id == Id.mulass - || id == Id.divass - || id == Id.modass - || id == Id.andass - || id == Id.orass - || id == Id.xorass - || id == Id.shlass - || id == Id.shrass - || id == Id.ushrass - || id == Id.catass - || id == Id.indexass - || id == Id.slice - || id == Id.sliceass + return id == Id.opAssign + || id == Id.opIndexAssign + || id == Id.opSlice + || id == Id.opSliceAssign || id == Id.opOpAssign || id == Id.opIndexOpAssign - || id == Id.opSliceOpAssign - || id == Id.powass; + || id == Id.opSliceOpAssign; } /** diff --git a/gcc/d/dmd/objc.d b/gcc/d/dmd/objc.d index c5ee77a..b671986 100644 --- a/gcc/d/dmd/objc.d +++ b/gcc/d/dmd/objc.d @@ -714,8 +714,8 @@ extern(C++) private final class Supported : Objc { if (cd.classKind == ClassKind.objc && fd.isStatic && !cd.objc.isMeta) return cd.objc.metaclass; - else - return cd; + + return cd; } override void addToClassMethodList(FuncDeclaration fd, ClassDeclaration cd) const @@ -805,11 +805,10 @@ extern(C++) private final class Supported : Objc { if (classDeclaration.baseClass) return getRuntimeMetaclass(classDeclaration.baseClass); - else - return classDeclaration; + + return classDeclaration; } - else - return classDeclaration.objc.metaclass; + return classDeclaration.objc.metaclass; } override void addSymbols(AttribDeclaration attribDeclaration, diff --git a/gcc/d/dmd/opover.d b/gcc/d/dmd/opover.d index 56b3601..92f3bb2 100644 --- a/gcc/d/dmd/opover.d +++ b/gcc/d/dmd/opover.d @@ -71,84 +71,6 @@ bool isCommutative(EXP op) @safe return false; } -/*********************************** - * Get Identifier for operator overload. - */ -private Identifier opId(Expression e) -{ - switch (e.op) - { - case EXP.uadd: return Id.uadd; - case EXP.negate: return Id.neg; - case EXP.tilde: return Id.com; - case EXP.cast_: return Id._cast; - case EXP.in_: return Id.opIn; - case EXP.plusPlus: return Id.postinc; - case EXP.minusMinus: return Id.postdec; - case EXP.add: return Id.add; - case EXP.min: return Id.sub; - case EXP.mul: return Id.mul; - case EXP.div: return Id.div; - case EXP.mod: return Id.mod; - case EXP.pow: return Id.pow; - case EXP.leftShift: return Id.shl; - case EXP.rightShift: return Id.shr; - case EXP.unsignedRightShift: return Id.ushr; - case EXP.and: return Id.iand; - case EXP.or: return Id.ior; - case EXP.xor: return Id.ixor; - case EXP.concatenate: return Id.cat; - case EXP.assign: return Id.assign; - case EXP.addAssign: return Id.addass; - case EXP.minAssign: return Id.subass; - case EXP.mulAssign: return Id.mulass; - case EXP.divAssign: return Id.divass; - case EXP.modAssign: return Id.modass; - case EXP.powAssign: return Id.powass; - case EXP.leftShiftAssign: return Id.shlass; - case EXP.rightShiftAssign: return Id.shrass; - case EXP.unsignedRightShiftAssign: return Id.ushrass; - case EXP.andAssign: return Id.andass; - case EXP.orAssign: return Id.orass; - case EXP.xorAssign: return Id.xorass; - case EXP.concatenateAssign: return Id.catass; - case EXP.equal: return Id.eq; - case EXP.lessThan: - case EXP.lessOrEqual: - case EXP.greaterThan: - case EXP.greaterOrEqual: return Id.cmp; - case EXP.array: return Id.index; - case EXP.star: return Id.opStar; - default: assert(0); - } -} - -/*********************************** - * Get Identifier for reverse operator overload, - * `null` if not supported for this operator. - */ -private Identifier opId_r(Expression e) -{ - switch (e.op) - { - case EXP.in_: return Id.opIn_r; - case EXP.add: return Id.add_r; - case EXP.min: return Id.sub_r; - case EXP.mul: return Id.mul_r; - case EXP.div: return Id.div_r; - case EXP.mod: return Id.mod_r; - case EXP.pow: return Id.pow_r; - case EXP.leftShift: return Id.shl_r; - case EXP.rightShift: return Id.shr_r; - case EXP.unsignedRightShift:return Id.ushr_r; - case EXP.and: return Id.iand_r; - case EXP.or: return Id.ior_r; - case EXP.xor: return Id.ixor_r; - case EXP.concatenate: return Id.cat_r; - default: return null; - } -} - /******************************************* * Helper function to turn operator into template argument list */ @@ -208,7 +130,7 @@ Objects* opToArg(Scope* sc, EXP op) } // Try alias this on first operand -private Expression checkAliasThisForLhs(AggregateDeclaration ad, Scope* sc, BinExp e) +Expression checkAliasThisForLhs(AggregateDeclaration ad, Scope* sc, BinExp e, Type[2] aliasThisStop) { if (!ad || !ad.aliasthis) return null; @@ -216,7 +138,7 @@ private Expression checkAliasThisForLhs(AggregateDeclaration ad, Scope* sc, BinE /* Rewrite (e1 op e2) as: * (e1.aliasthis op e2) */ - if (isRecursiveAliasThis(e.att1, e.e1.type)) + if (isRecursiveAliasThis(aliasThisStop[0], e.e1.type)) return null; //printf("att %s e1 = %s\n", Token.toChars(e.op), e.e1.type.toChars()); BinExp be = cast(BinExp)e.copy(); @@ -227,24 +149,18 @@ private Expression checkAliasThisForLhs(AggregateDeclaration ad, Scope* sc, BinE if (!be.e1) return null; - Expression result; - if (be.op == EXP.concatenateAssign) - result = be.op_overload(sc); - else - result = be.trySemantic(sc); - - return result; + return be.trySemanticAliasThis(sc, aliasThisStop); } // Try alias this on second operand -private Expression checkAliasThisForRhs(AggregateDeclaration ad, Scope* sc, BinExp e) +Expression checkAliasThisForRhs(AggregateDeclaration ad, Scope* sc, BinExp e, Type[2] aliasThisStop) { if (!ad || !ad.aliasthis) return null; /* Rewrite (e1 op e2) as: * (e1 op e2.aliasthis) */ - if (isRecursiveAliasThis(e.att2, e.e2.type)) + if (isRecursiveAliasThis(aliasThisStop[1], e.e2.type)) return null; //printf("att %s e2 = %s\n", Token.toChars(e.op), e.e2.type.toChars()); BinExp be = cast(BinExp)e.copy(); @@ -252,189 +168,14 @@ private Expression checkAliasThisForRhs(AggregateDeclaration ad, Scope* sc, BinE if (!be.e2) return null; - Expression result; - if (be.op == EXP.concatenateAssign) - result = be.op_overload(sc); - else - result = be.trySemantic(sc); - - return result; + return be.trySemanticAliasThis(sc, aliasThisStop); } -/************************************ - * Operator overload. - * Check for operator overload, if so, replace - * with function call. - * Params: - * e = expression with operator - * 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 - * match an overload - * Returns: - * `null` if not an operator overload, - * otherwise the lowered expression - */ -Expression op_overload(Expression e, Scope* sc, EXP* pop = null) +Expression opOverloadUnary(UnaExp e, Scope* sc) { - Expression visit(Expression e) - { - assert(0); - } - - Expression visitUna(UnaExp e) - { - //printf("UnaExp::op_overload() (%s)\n", e.toChars()); - Expression result; - if (auto ae = e.e1.isArrayExp()) - { - ae.e1 = ae.e1.expressionSemantic(sc); - ae.e1 = resolveProperties(sc, ae.e1); - Expression ae1old = ae.e1; - const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval); - IntervalExp ie = null; - if (maybeSlice && ae.arguments.length) - { - ie = (*ae.arguments)[0].isIntervalExp(); - } - Type att = null; // first cyclic `alias this` type - while (true) - { - if (ae.e1.op == EXP.error) - { - return ae.e1; - } - Expression e0 = null; - Expression ae1save = ae.e1; - ae.lengthVar = null; - Type t1b = ae.e1.type.toBasetype(); - AggregateDeclaration ad = isAggregate(t1b); - if (!ad) - break; - if (search_function(ad, Id.opIndexUnary)) - { - // Deal with $ - result = resolveOpDollar(sc, ae, &e0); - if (!result) // op(a[i..j]) might be: a.opSliceUnary!(op)(i, j) - goto Lfallback; - if (result.op == EXP.error) - return result; - /* Rewrite op(a[arguments]) as: - * a.opIndexUnary!(op)(arguments) - */ - Expressions* a = ae.arguments.copy(); - Objects* tiargs = opToArg(sc, e.op); - result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexUnary, tiargs); - result = new CallExp(e.loc, result, a); - if (maybeSlice) // op(a[]) might be: a.opSliceUnary!(op)() - result = result.trySemantic(sc); - else - result = result.expressionSemantic(sc); - if (result) - { - return Expression.combine(e0, result); - } - } - Lfallback: - if (maybeSlice && search_function(ad, Id.opSliceUnary)) - { - // Deal with $ - result = resolveOpDollar(sc, ae, ie, &e0); - if (result.op == EXP.error) - return result; - /* Rewrite op(a[i..j]) as: - * a.opSliceUnary!(op)(i, j) - */ - auto a = new Expressions(); - if (ie) - { - a.push(ie.lwr); - a.push(ie.upr); - } - Objects* tiargs = opToArg(sc, e.op); - result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceUnary, tiargs); - result = new CallExp(e.loc, result, a); - result = result.expressionSemantic(sc); - result = Expression.combine(e0, result); - return result; - } - // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) - { - /* Rewrite op(a[arguments]) as: - * op(a.aliasthis[arguments]) - */ - ae.e1 = resolveAliasThis(sc, ae1save, true); - if (ae.e1) - continue; - } - break; - } - ae.e1 = ae1old; // recovery - ae.lengthVar = null; - } - e.e1 = e.e1.expressionSemantic(sc); - e.e1 = resolveProperties(sc, e.e1); - Type att = null; // first cyclic `alias this` type - while (1) - { - if (e.e1.op == EXP.error) - { - return e.e1; - } - - AggregateDeclaration ad = isAggregate(e.e1.type); - if (!ad) - break; - - Dsymbol fd = null; - /* Rewrite as: - * e1.opUnary!(op)() - */ - fd = search_function(ad, Id.opUnary); - if (fd) - { - Objects* tiargs = opToArg(sc, e.op); - result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs); - result = new CallExp(e.loc, result); - result = result.expressionSemantic(sc); - return result; - } - // D1-style operator overloads, deprecated - if (e.op != EXP.prePlusPlus && e.op != EXP.preMinusMinus) - { - auto id = opId(e); - fd = search_function(ad, id); - if (fd) - { - // @@@DEPRECATED_2.110@@@. - // Deprecated in 2.088, made an error in 2.100 - error(e.loc, "`%s` is obsolete. Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr); - return ErrorExp.get(); - } - } - // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type)) - { - /* Rewrite op(e1) as: - * op(e1.aliasthis) - */ - //printf("att una %s e1 = %s\n", EXPtoString(op).ptr, this.e1.type.toChars()); - if (auto e1 = resolveAliasThis(sc, e.e1, true)) - { - e.e1 = e1; - continue; - } - break; - } - break; - } - return result; - } - - Expression visitArray(ArrayExp ae) + Expression result; + if (auto ae = e.e1.isArrayExp()) { - //printf("ArrayExp::op_overload() (%s)\n", ae.toChars()); ae.e1 = ae.e1.expressionSemantic(sc); ae.e1 = resolveProperties(sc, ae.e1); Expression ae1old = ae.e1; @@ -444,7 +185,6 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) { ie = (*ae.arguments)[0].isIntervalExp(); } - Expression result; Type att = null; // first cyclic `alias this` type while (true) { @@ -458,43 +198,23 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) Type t1b = ae.e1.type.toBasetype(); AggregateDeclaration ad = isAggregate(t1b); if (!ad) - { - // If the non-aggregate expression ae.e1 is indexable or sliceable, - // convert it to the corresponding concrete expression. - if (isIndexableNonAggregate(t1b) || ae.e1.op == EXP.type) - { - // Convert to SliceExp - if (maybeSlice) - { - result = new SliceExp(ae.loc, ae.e1, ie); - result = result.expressionSemantic(sc); - return result; - } - // Convert to IndexExp - if (ae.arguments.length == 1) - { - result = new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]); - result = result.expressionSemantic(sc); - return result; - } - } break; - } - if (search_function(ad, Id.index)) + if (search_function(ad, Id.opIndexUnary)) { // Deal with $ result = resolveOpDollar(sc, ae, &e0); - if (!result) // a[i..j] might be: a.opSlice(i, j) + if (!result) // op(a[i..j]) might be: a.opSliceUnary!(op)(i, j) goto Lfallback; if (result.op == EXP.error) return result; - /* Rewrite e1[arguments] as: - * e1.opIndex(arguments) - */ + /* Rewrite op(a[arguments]) as: + * a.opIndexUnary!(op)(arguments) + */ Expressions* a = ae.arguments.copy(); - result = new DotIdExp(ae.loc, ae.e1, Id.index); - result = new CallExp(ae.loc, result, a); - if (maybeSlice) // a[] might be: a.opSlice() + Objects* tiargs = opToArg(sc, e.op); + result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexUnary, tiargs); + result = new CallExp(e.loc, result, a); + if (maybeSlice) // op(a[]) might be: a.opSliceUnary!(op)() result = result.trySemantic(sc); else result = result.expressionSemantic(sc); @@ -504,36 +224,24 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) } } Lfallback: - if (maybeSlice && ae.e1.op == EXP.type) - { - result = new SliceExp(ae.loc, ae.e1, ie); - result = result.expressionSemantic(sc); - result = Expression.combine(e0, result); - return result; - } - if (maybeSlice && search_function(ad, Id.slice)) + if (maybeSlice && search_function(ad, Id.opSliceUnary)) { // Deal with $ result = resolveOpDollar(sc, ae, ie, &e0); - if (result.op == EXP.error) - { - if (!e0 && !search_function(ad, Id.dollar)) { - ae.loc.errorSupplemental("Aggregate declaration '%s' does not define 'opDollar'", ae.e1.toChars()); - } return result; - } - /* Rewrite a[i..j] as: - * a.opSlice(i, j) - */ + /* Rewrite op(a[i..j]) as: + * a.opSliceUnary!(op)(i, j) + */ auto a = new Expressions(); if (ie) { a.push(ie.lwr); a.push(ie.upr); } - result = new DotIdExp(ae.loc, ae.e1, Id.slice); - result = new CallExp(ae.loc, result, a); + Objects* tiargs = opToArg(sc, e.op); + result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceUnary, tiargs); + result = new CallExp(e.loc, result, a); result = result.expressionSemantic(sc); result = Expression.combine(e0, result); return result; @@ -541,10 +249,9 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) // Didn't find it. Forward to aliasthis if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) { - //printf("att arr e1 = %s\n", this.e1.type.toChars()); /* Rewrite op(a[arguments]) as: - * op(a.aliasthis[arguments]) - */ + * op(a.aliasthis[arguments]) + */ ae.e1 = resolveAliasThis(sc, ae1save, true); if (ae.e1) continue; @@ -553,846 +260,810 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) } ae.e1 = ae1old; // recovery ae.lengthVar = null; - return result; } - - /*********************************************** - * This is mostly the same as UnaryExp::op_overload(), but has - * a different rewrite. - */ - Expression visitCast(CastExp e, Type att = null) + e.e1 = e.e1.expressionSemantic(sc); + e.e1 = resolveProperties(sc, e.e1); + Type att = null; // first cyclic `alias this` type + while (1) { - //printf("CastExp::op_overload() (%s)\n", e.toChars()); - Expression result; - if (AggregateDeclaration ad = isAggregate(e.e1.type)) + if (e.e1.op == EXP.error) { - Dsymbol fd = null; - /* Rewrite as: - * e1.opCast!(T)() + return e.e1; + } + + AggregateDeclaration ad = isAggregate(e.e1.type); + if (!ad) + break; + + Dsymbol fd = null; + /* Rewrite as: + * e1.opUnary!(op)() + */ + fd = search_function(ad, Id.opUnary); + if (fd) + { + Objects* tiargs = opToArg(sc, e.op); + result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs); + result = new CallExp(e.loc, result); + result = result.expressionSemantic(sc); + return result; + } + + // Didn't find it. Forward to aliasthis + if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type)) + { + /* Rewrite op(e1) as: + * op(e1.aliasthis) */ - fd = search_function(ad, Id._cast); - if (fd) - { - version (all) - { - // Backwards compatibility with D1 if opCast is a function, not a template - if (fd.isFuncDeclaration()) - { - // Rewrite as: e1.opCast() - return build_overload(e.loc, sc, e.e1, null, fd); - } - } - auto tiargs = new Objects(); - tiargs.push(e.to); - result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs); - result = new CallExp(e.loc, result); - result = result.expressionSemantic(sc); - return result; - } - // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type)) + //printf("att una %s e1 = %s\n", EXPtoString(op).ptr, this.e1.type.toChars()); + if (auto e1 = resolveAliasThis(sc, e.e1, true)) { - /* Rewrite op(e1) as: - * op(e1.aliasthis) - */ - if (auto e1 = resolveAliasThis(sc, e.e1, true)) - { - result = e.copy(); - (cast(UnaExp)result).e1 = e1; - result = visitCast(result.isCastExp(), att); - return result; - } + e.e1 = e1; + continue; } + break; } - return result; + break; } + return result; +} - Expression visitBin(BinExp e) +Expression opOverloadArray(ArrayExp ae, Scope* sc) +{ + ae.e1 = ae.e1.expressionSemantic(sc); + ae.e1 = resolveProperties(sc, ae.e1); + Expression ae1old = ae.e1; + const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval); + IntervalExp ie = null; + if (maybeSlice && ae.arguments.length) { - //printf("BinExp::op_overload() (%s)\n", e.toChars()); - Identifier id = opId(e); - Identifier id_r = opId_r(e); - int argsset = 0; - AggregateDeclaration ad1 = isAggregate(e.e1.type); - AggregateDeclaration ad2 = isAggregate(e.e2.type); - if (e.op == EXP.assign && ad1 == ad2) - { - StructDeclaration sd = ad1.isStructDeclaration(); - if (sd && - (!sd.hasIdentityAssign || - /* Do a blit if we can and the rvalue is something like .init, - * where a postblit is not necessary. - */ - (sd.hasBlitAssign && !e.e2.isLvalue()))) - { - /* This is bitwise struct assignment. */ - return null; - } - } - Dsymbol s = null; - Dsymbol s_r = null; - Objects* tiargs = null; - if (e.op == EXP.plusPlus || e.op == EXP.minusMinus) + ie = (*ae.arguments)[0].isIntervalExp(); + } + Expression result; + Type att = null; // first cyclic `alias this` type + while (true) + { + if (ae.e1.op == EXP.error) { - // Bug4099 fix - if (ad1 && search_function(ad1, Id.opUnary)) - return null; + return ae.e1; } - if (e.op != EXP.equal && e.op != EXP.notEqual && e.op != EXP.assign && e.op != EXP.plusPlus && e.op != EXP.minusMinus) + Expression e0 = null; + Expression ae1save = ae.e1; + ae.lengthVar = null; + Type t1b = ae.e1.type.toBasetype(); + AggregateDeclaration ad = isAggregate(t1b); + if (!ad) { - /* Try opBinary and opBinaryRight - */ - if (ad1) + // If the non-aggregate expression ae.e1 is indexable or sliceable, + // convert it to the corresponding concrete expression. + if (isIndexableNonAggregate(t1b) || ae.e1.op == EXP.type) { - s = search_function(ad1, Id.opBinary); - if (s && !s.isTemplateDeclaration()) + // Convert to SliceExp + if (maybeSlice) { - error(e.e1.loc, "`%s.opBinary` isn't a template", e.e1.toChars()); - return ErrorExp.get(); + result = new SliceExp(ae.loc, ae.e1, ie); + result = result.expressionSemantic(sc); + return result; } - } - if (ad2) - { - s_r = search_function(ad2, Id.opBinaryRight); - if (s_r && !s_r.isTemplateDeclaration()) + // Convert to IndexExp + if (ae.arguments.length == 1) { - error(e.e2.loc, "`%s.opBinaryRight` isn't a template", e.e2.toChars()); - return ErrorExp.get(); + result = new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]); + result = result.expressionSemantic(sc); + return result; } - if (s_r && s_r == s) // https://issues.dlang.org/show_bug.cgi?id=12778 - s_r = null; } - // Set tiargs, the template argument list, which will be the operator string - if (s || s_r) + break; + } + if (search_function(ad, Id.opIndex)) + { + // Deal with $ + result = resolveOpDollar(sc, ae, &e0); + if (!result) // a[i..j] might be: a.opSlice(i, j) + goto Lfallback; + if (result.op == EXP.error) + return result; + /* Rewrite e1[arguments] as: + * e1.opIndex(arguments) + */ + Expressions* a = ae.arguments.copy(); + result = new DotIdExp(ae.loc, ae.e1, Id.opIndex); + result = new CallExp(ae.loc, result, a); + if (maybeSlice) // a[] might be: a.opSlice() + result = result.trySemantic(sc); + else + result = result.expressionSemantic(sc); + if (result) { - id = Id.opBinary; - id_r = Id.opBinaryRight; - tiargs = opToArg(sc, e.op); + return Expression.combine(e0, result); } } - if (!s && !s_r) + Lfallback: + if (maybeSlice && ae.e1.op == EXP.type) + { + result = new SliceExp(ae.loc, ae.e1, ie); + result = result.expressionSemantic(sc); + result = Expression.combine(e0, result); + return result; + } + if (maybeSlice && search_function(ad, Id.opSlice)) { - // Try the D1-style operators, deprecated - if (ad1 && id) + // Deal with $ + result = resolveOpDollar(sc, ae, ie, &e0); + + if (result.op == EXP.error) { - s = search_function(ad1, id); - if (s && id != Id.assign) - { - // @@@DEPRECATED_2.110@@@. - // Deprecated in 2.088, made an error in 2.100 - if (id == Id.postinc || id == Id.postdec) - error(e.loc, "`%s` is obsolete. Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr); - else - error(e.loc, "`%s` is obsolete. Use `opBinary(string op)(...) if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr); - return ErrorExp.get(); + if (!e0 && !search_function(ad, Id.dollar)) { + ae.loc.errorSupplemental("Aggregate declaration '%s' does not define 'opDollar'", ae.e1.toChars()); } + return result; } - if (ad2 && id_r) + /* Rewrite a[i..j] as: + * a.opSlice(i, j) + */ + auto a = new Expressions(); + if (ie) { - s_r = search_function(ad2, id_r); - // https://issues.dlang.org/show_bug.cgi?id=12778 - // If both x.opBinary(y) and y.opBinaryRight(x) found, - // and they are exactly same symbol, x.opBinary(y) should be preferred. - if (s_r && s_r == s) - s_r = null; - if (s_r) - { - // @@@DEPRECATED_2.110@@@. - // Deprecated in 2.088, made an error in 2.100 - error(e.loc, "`%s` is obsolete. Use `opBinaryRight(string op)(...) if (op == \"%s\")` instead.", id_r.toChars(), EXPtoString(e.op).ptr); - return ErrorExp.get(); - } + a.push(ie.lwr); + a.push(ie.upr); } + result = new DotIdExp(ae.loc, ae.e1, Id.opSlice); + result = new CallExp(ae.loc, result, a); + result = result.expressionSemantic(sc); + result = Expression.combine(e0, result); + return result; } - Expressions* args1 = new Expressions(); - Expressions* args2 = new Expressions(); - if (s || s_r) + // Didn't find it. Forward to aliasthis + if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) { - /* Try: - * a.opfunc(b) - * b.opfunc_r(a) - * and see which is better. + //printf("att arr e1 = %s\n", this.e1.type.toChars()); + /* Rewrite op(a[arguments]) as: + * op(a.aliasthis[arguments]) */ - args1.setDim(1); - (*args1)[0] = e.e1; - expandTuples(args1); - args2.setDim(1); - (*args2)[0] = e.e2; - expandTuples(args2); - argsset = 1; - MatchAccumulator m; - if (s) - { - functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) - { - return ErrorExp.get(); - } - } - FuncDeclaration lastf = m.lastf; - if (s_r) + ae.e1 = resolveAliasThis(sc, ae1save, true); + if (ae.e1) + continue; + } + break; + } + ae.e1 = ae1old; // recovery + ae.lengthVar = null; + return result; +} + +/*********************************************** + * This is mostly the same as opOverloadUnary but has + * a different rewrite. + */ +Expression opOverloadCast(CastExp e, Scope* sc, Type att = null) +{ + Expression result; + if (AggregateDeclaration ad = isAggregate(e.e1.type)) + { + Dsymbol fd = null; + /* Rewrite as: + * e1.opCast!(T)() + */ + fd = search_function(ad, Id.opCast); + if (fd) + { + version (all) { - functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) + // Backwards compatibility with D1 if opCast is a function, not a template + if (fd.isFuncDeclaration()) { - return ErrorExp.get(); + // Rewrite as: e1.opCast() + return build_overload(e.loc, sc, e.e1, null, fd); } } - if (m.count > 1) - { - // Error, ambiguous - error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); - } - else if (m.last == MATCH.nomatch) - { - if (tiargs) - goto L1; - m.lastf = null; - } - if (e.op == EXP.plusPlus || e.op == EXP.minusMinus) - { - // Kludge because operator overloading regards e++ and e-- - // as unary, but it's implemented as a binary. - // Rewrite (e1 ++ e2) as e1.postinc() - // Rewrite (e1 -- e2) as e1.postdec() - return build_overload(e.loc, sc, e.e1, null, m.lastf ? m.lastf : s); - } - else if (lastf && m.lastf == lastf || !s_r && m.last == MATCH.nomatch) - { - // Rewrite (e1 op e2) as e1.opfunc(e2) - return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); - } - else - { - // Rewrite (e1 op e2) as e2.opfunc_r(e1) - return build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r); - } + auto tiargs = new Objects(); + tiargs.push(e.to); + result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs); + result = new CallExp(e.loc, result); + result = result.expressionSemantic(sc); + return result; } - L1: - version (all) + // Didn't find it. Forward to aliasthis + if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type)) { - // Retained for D1 compatibility - if (isCommutative(e.op) && !tiargs) + /* Rewrite op(e1) as: + * op(e1.aliasthis) + */ + if (auto e1 = resolveAliasThis(sc, e.e1, true)) { - s = null; - s_r = null; - if (ad1 && id_r) - { - s_r = search_function(ad1, id_r); - } - if (ad2 && id) - { - s = search_function(ad2, id); - if (s && s == s_r) // https://issues.dlang.org/show_bug.cgi?id=12778 - s = null; - } - if (s || s_r) - { - /* Try: - * a.opfunc_r(b) - * b.opfunc(a) - * and see which is better. - */ - if (!argsset) - { - args1.setDim(1); - (*args1)[0] = e.e1; - expandTuples(args1); - args2.setDim(1); - (*args2)[0] = e.e2; - expandTuples(args2); - } - MatchAccumulator m; - if (s_r) - { - functionResolve(m, s_r, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) - { - return ErrorExp.get(); - } - } - FuncDeclaration lastf = m.lastf; - if (s) - { - functionResolve(m, s, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) - { - return ErrorExp.get(); - } - } - if (m.count > 1) - { - // Error, ambiguous - error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); - } - else if (m.last == MATCH.nomatch) - { - m.lastf = null; - } - - if (lastf && m.lastf == lastf || !s && m.last == MATCH.nomatch) - { - // Rewrite (e1 op e2) as e1.opfunc_r(e2) - return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s_r); - } - else - { - // Rewrite (e1 op e2) as e2.opfunc(e1) - Expression result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s); - // When reversing operands of comparison operators, - // need to reverse the sense of the op - if (pop) - *pop = reverseRelation(e.op); - return result; - } - } + result = e.copy(); + (cast(UnaExp)result).e1 = e1; + result = opOverloadCast(result.isCastExp(), sc, att); + return result; } } + } + return result; +} - Expression rewrittenLhs; - if (!(e.op == EXP.assign && ad2 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943 +// When no operator overload functions are found for `e`, recursively try with `alias this` +// Returns: `null` when still no overload found, otherwise resolved lowering +Expression binAliasThis(BinExp e, Scope* sc, Type[2] aliasThisStop) +{ + AggregateDeclaration ad1 = isAggregate(e.e1.type); + AggregateDeclaration ad2 = isAggregate(e.e2.type); + Expression rewrittenLhs; + if (!(e.op == EXP.assign && ad2 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943 + { + if (Expression result = checkAliasThisForLhs(ad1, sc, e, aliasThisStop)) { - if (Expression result = checkAliasThisForLhs(ad1, sc, e)) - { - /* https://issues.dlang.org/show_bug.cgi?id=19441 - * - * alias this may not be used for partial assignment. - * If a struct has a single member which is aliased this - * directly or aliased to a ref getter function that returns - * the mentioned member, then alias this may be - * used since the object will be fully initialised. - * If the struct is nested, the context pointer is considered - * one of the members, hence the `ad1.fields.length == 2 && ad1.vthis` - * condition. - */ - if (result.op != EXP.assign) - return result; // i.e: Rewrote `e1 = e2` -> `e1(e2)` + /* https://issues.dlang.org/show_bug.cgi?id=19441 + * + * alias this may not be used for partial assignment. + * If a struct has a single member which is aliased this + * directly or aliased to a ref getter function that returns + * the mentioned member, then alias this may be + * used since the object will be fully initialised. + * If the struct is nested, the context pointer is considered + * one of the members, hence the `ad1.fields.length == 2 && ad1.vthis` + * condition. + */ + if (result.op != EXP.assign) + return result; // i.e: Rewrote `e1 = e2` -> `e1(e2)` - auto ae = result.isAssignExp(); - if (ae.e1.op != EXP.dotVariable) - return result; // i.e: Rewrote `e1 = e2` -> `e1() = e2` + auto ae = result.isAssignExp(); + if (ae.e1.op != EXP.dotVariable) + return result; // i.e: Rewrote `e1 = e2` -> `e1() = e2` - auto dve = ae.e1.isDotVarExp(); - if (auto ad = dve.var.isMember2()) + auto dve = ae.e1.isDotVarExp(); + if (auto ad = dve.var.isMember2()) + { + // i.e: Rewrote `e1 = e2` -> `e1.some.var = e2` + // Ensure that `var` is the only field member in `ad` + if (ad.fields.length == 1 || (ad.fields.length == 2 && ad.vthis)) { - // i.e: Rewrote `e1 = e2` -> `e1.some.var = e2` - // Ensure that `var` is the only field member in `ad` - if (ad.fields.length == 1 || (ad.fields.length == 2 && ad.vthis)) - { - if (dve.var == ad.aliasthis.sym) - return result; - } + if (dve.var == ad.aliasthis.sym) + return result; } - rewrittenLhs = ae.e1; } + rewrittenLhs = ae.e1; } - if (!(e.op == EXP.assign && ad1 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943 - { - if (Expression result = checkAliasThisForRhs(ad2, sc, e)) - return result; - } - if (rewrittenLhs) - { - error(e.loc, "cannot use `alias this` to partially initialize variable `%s` of type `%s`. Use `%s`", - e.e1.toChars(), ad1.toChars(), rewrittenLhs.toChars()); - return ErrorExp.get(); - } - return null; } - - Expression visitEqual(EqualExp e) + if (!(e.op == EXP.assign && ad1 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943 { - //printf("EqualExp::op_overload() (%s)\n", e.toChars()); - Type t1 = e.e1.type.toBasetype(); - Type t2 = e.e2.type.toBasetype(); + if (Expression result = checkAliasThisForRhs(ad2, sc, e, aliasThisStop)) + return result; + } + if (rewrittenLhs) + { + error(e.loc, "cannot use `alias this` to partially initialize variable `%s` of type `%s`. Use `%s`", + e.e1.toChars(), ad1.toChars(), rewrittenLhs.toChars()); + return ErrorExp.get(); + } + return null; +} - /* 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)) +Expression opOverloadAssign(AssignExp e, Scope* sc, Type[2] aliasThisStop) +{ + AggregateDeclaration ad1 = isAggregate(e.e1.type); + AggregateDeclaration ad2 = isAggregate(e.e2.type); + if (ad1 == ad2) + { + StructDeclaration sd = ad1.isStructDeclaration(); + if (sd && + (!sd.hasIdentityAssign || + /* Do a blit if we can and the rvalue is something like .init, + * where a postblit is not necessary. + */ + (sd.hasBlitAssign && !e.e2.isLvalue()))) { + /* This is bitwise struct assignment. */ return null; } + } + Dsymbol s = search_function(ad1, Id.opAssign); - /* Check for class equality with null literal or typeof(null). - */ - if (t1.ty == Tclass && e.e2.op == EXP.null_ || - t2.ty == Tclass && e.e1.op == EXP.null_) - { - error(e.loc, "use `%s` instead of `%s` when comparing with `null`", - EXPtoString(e.op == EXP.equal ? EXP.identity : EXP.notIdentity).ptr, - EXPtoString(e.op).ptr); - return ErrorExp.get(); - } - if (t1.ty == Tclass && t2.ty == Tnull || - t1.ty == Tnull && t2.ty == Tclass) - { - // Comparing a class with typeof(null) should not call opEquals - return null; - } + bool choseReverse; + if (auto result = pickBestBinaryOverload(sc, null, s, null, e, choseReverse)) + return result; - /* Check for class equality. - */ - if (t1.ty == Tclass && t2.ty == Tclass) - { - ClassDeclaration cd1 = t1.isClassHandle(); - ClassDeclaration cd2 = t2.isClassHandle(); - if (!(cd1.classKind == ClassKind.cpp || cd2.classKind == ClassKind.cpp)) - { - /* Rewrite as: - * .object.opEquals(e1, e2) - */ - if (!ClassDeclaration.object) - { - error(e.loc, "cannot compare classes for equality because `object.Object` was not declared"); - return null; - } + return binAliasThis(e, sc, aliasThisStop); +} - Expression e1x = e.e1; - Expression e2x = e.e2; +Expression opOverloadBinary(BinExp e, Scope* sc, Type[2] aliasThisStop) +{ + if (Expression err = binSemanticProp(e, sc)) + return err; - /* The explicit cast is necessary for interfaces - * https://issues.dlang.org/show_bug.cgi?id=4088 - */ - Type to = ClassDeclaration.object.getType(); - if (cd1.isInterfaceDeclaration()) - e1x = new CastExp(e.loc, e.e1, t1.isMutable() ? to : to.constOf()); - if (cd2.isInterfaceDeclaration()) - e2x = new CastExp(e.loc, e.e2, t2.isMutable() ? to : to.constOf()); - - Expression result = new IdentifierExp(e.loc, Id.empty); - result = new DotIdExp(e.loc, result, Id.object); - result = new DotIdExp(e.loc, result, Id.eq); - result = new CallExp(e.loc, result, e1x, e2x); - if (e.op == EXP.notEqual) - result = new NotExp(e.loc, result); - result = result.expressionSemantic(sc); - return result; - } - } + AggregateDeclaration ad1 = isAggregate(e.e1.type); + AggregateDeclaration ad2 = isAggregate(e.e2.type); + + // Try opBinary and opBinaryRight + Dsymbol s = search_function(ad1, Id.opBinary); + if (s && !s.isTemplateDeclaration()) + { + error(e.e1.loc, "`%s.opBinary` isn't a template", e.e1.toChars()); + return ErrorExp.get(); + } + + Dsymbol s_r = search_function(ad2, Id.opBinaryRight); + if (s_r && !s_r.isTemplateDeclaration()) + { + error(e.e2.loc, "`%s.opBinaryRight` isn't a template", e.e2.toChars()); + return ErrorExp.get(); + } + if (s_r && s_r == s) // https://issues.dlang.org/show_bug.cgi?id=12778 + s_r = null; + + bool choseReverse; + if (auto res = pickBestBinaryOverload(sc, opToArg(sc, e.op), s, s_r, e, choseReverse)) + return res; + + return binAliasThis(e, sc, aliasThisStop); +} + +Expression opOverloadEqual(EqualExp e, Scope* sc, Type[2] aliasThisStop) +{ + Type t1 = e.e1.type.toBasetype(); + Type t2 = e.e2.type.toBasetype(); + + /* 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)) + { + return null; + } + + /* Check for class equality with null literal or typeof(null). + */ + if (t1.ty == Tclass && e.e2.op == EXP.null_ || + t2.ty == Tclass && e.e1.op == EXP.null_) + { + error(e.loc, "use `%s` instead of `%s` when comparing with `null`", + EXPtoString(e.op == EXP.equal ? EXP.identity : EXP.notIdentity).ptr, + EXPtoString(e.op).ptr); + return ErrorExp.get(); + } + if (t1.ty == Tclass && t2.ty == Tnull || + t1.ty == Tnull && t2.ty == Tclass) + { + // Comparing a class with typeof(null) should not call opEquals + return null; + } - if (Expression result = compare_overload(e, sc, Id.eq, null)) + /* Check for class equality. + */ + if (t1.ty == Tclass && t2.ty == Tclass) + { + ClassDeclaration cd1 = t1.isClassHandle(); + ClassDeclaration cd2 = t2.isClassHandle(); + if (!(cd1.classKind == ClassKind.cpp || cd2.classKind == ClassKind.cpp)) { - if (lastComma(result).op == EXP.call && e.op == EXP.notEqual) + /* Rewrite as: + * .object.opEquals(e1, e2) + */ + if (!ClassDeclaration.object) { - result = new NotExp(result.loc, result); - result = result.expressionSemantic(sc); + error(e.loc, "cannot compare classes for equality because `object.Object` was not declared"); + return null; } + + Expression e1x = e.e1; + Expression e2x = e.e2; + + /* The explicit cast is necessary for interfaces + * https://issues.dlang.org/show_bug.cgi?id=4088 + */ + Type to = ClassDeclaration.object.getType(); + if (cd1.isInterfaceDeclaration()) + e1x = new CastExp(e.loc, e.e1, t1.isMutable() ? to : to.constOf()); + if (cd2.isInterfaceDeclaration()) + e2x = new CastExp(e.loc, e.e2, t2.isMutable() ? to : to.constOf()); + + Expression result = new IdentifierExp(e.loc, Id.empty); + result = new DotIdExp(e.loc, result, Id.object); + result = new DotIdExp(e.loc, result, Id.opEquals); + result = new CallExp(e.loc, result, e1x, e2x); + if (e.op == EXP.notEqual) + result = new NotExp(e.loc, result); + result = result.expressionSemantic(sc); return result; } + } + + EXP cmpOp; + if (Expression result = compare_overload(e, sc, Id.opEquals, cmpOp, aliasThisStop)) + { + if (lastComma(result).op == EXP.call && e.op == EXP.notEqual) + { + result = new NotExp(result.loc, result); + result = result.expressionSemantic(sc); + } + return result; + } - /* Check for pointer equality. + /* Check for pointer equality. + */ + if (t1.ty == Tpointer || t2.ty == Tpointer) + { + /* Rewrite: + * ptr1 == ptr2 + * as: + * ptr1 is ptr2 + * + * This is just a rewriting for deterministic AST representation + * as the backend input. */ - if (t1.ty == Tpointer || t2.ty == Tpointer) + auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity; + Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2); + return r.expressionSemantic(sc); + } + + /* Check for struct equality without opEquals. + */ + if (t1.ty == Tstruct && t2.ty == Tstruct) + { + auto sd = t1.isTypeStruct().sym; + if (sd != t2.isTypeStruct().sym) + return null; + + import dmd.clone : needOpEquals; + if (!sc.previews.fieldwise && !needOpEquals(sd)) { - /* Rewrite: - * ptr1 == ptr2 - * as: - * ptr1 is ptr2 - * - * This is just a rewriting for deterministic AST representation - * as the backend input. - */ + // Use bitwise equality. auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity; Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2); return r.expressionSemantic(sc); } - /* Check for struct equality without opEquals. + /* Do memberwise equality. + * https://dlang.org/spec/expression.html#equality_expressions + * Rewrite: + * e1 == e2 + * as: + * e1.tupleof == e2.tupleof + * + * If sd is a nested struct, and if it's nested in a class, it will + * also compare the parent class's equality. Otherwise, compares + * the identity of parent context through void*. */ - if (t1.ty == Tstruct && t2.ty == Tstruct) - { - auto sd = t1.isTypeStruct().sym; - if (sd != t2.isTypeStruct().sym) - return null; - - import dmd.clone : needOpEquals; - if (!sc.previews.fieldwise && !needOpEquals(sd)) - { - // Use bitwise equality. - auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity; - Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2); - return r.expressionSemantic(sc); - } + e = e.copy().isEqualExp(); + e.e1 = new DotIdExp(e.loc, e.e1, Id._tupleof); + e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof); + + auto sc2 = sc.push(); + sc2.noAccessCheck = true; + Expression r = e.expressionSemantic(sc2); + sc2.pop(); + return r; + } - /* Do memberwise equality. - * https://dlang.org/spec/expression.html#equality_expressions - * Rewrite: - * e1 == e2 - * as: - * e1.tupleof == e2.tupleof - * - * If sd is a nested struct, and if it's nested in a class, it will - * also compare the parent class's equality. Otherwise, compares - * the identity of parent context through void*. - */ - e = e.copy().isEqualExp(); - e.e1 = new DotIdExp(e.loc, e.e1, Id._tupleof); - e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof); - - auto sc2 = sc.push(); - sc2.noAccessCheck = true; - Expression r = e.expressionSemantic(sc2); - sc2.pop(); - return r; + /* Check for tuple equality. + */ + if (e.e1.op == EXP.tuple && e.e2.op == EXP.tuple) + { + auto tup1 = e.e1.isTupleExp(); + auto tup2 = e.e2.isTupleExp(); + size_t dim = tup1.exps.length; + if (dim != tup2.exps.length) + { + error(e.loc, "mismatched sequence lengths, `%d` and `%d`", + cast(int)dim, cast(int)tup2.exps.length); + return ErrorExp.get(); } - /* Check for tuple equality. - */ - if (e.e1.op == EXP.tuple && e.e2.op == EXP.tuple) + Expression result; + if (dim == 0) + { + // zero-length tuple comparison should always return true or false. + result = IntegerExp.createBool(e.op == EXP.equal); + } + else { - auto tup1 = e.e1.isTupleExp(); - auto tup2 = e.e2.isTupleExp(); - size_t dim = tup1.exps.length; - if (dim != tup2.exps.length) + for (size_t i = 0; i < dim; i++) { - error(e.loc, "mismatched sequence lengths, `%d` and `%d`", - cast(int)dim, cast(int)tup2.exps.length); - return ErrorExp.get(); + auto ex1 = (*tup1.exps)[i]; + auto ex2 = (*tup2.exps)[i]; + auto eeq = new EqualExp(e.op, e.loc, ex1, ex2); + + if (!result) + result = eeq; + else if (e.op == EXP.equal) + result = new LogicalExp(e.loc, EXP.andAnd, result, eeq); + else + result = new LogicalExp(e.loc, EXP.orOr, result, eeq); } + assert(result); + } + result = Expression.combine(tup1.e0, tup2.e0, result); + result = result.expressionSemantic(sc); - Expression result; - if (dim == 0) - { - // zero-length tuple comparison should always return true or false. - result = IntegerExp.createBool(e.op == EXP.equal); - } - else - { - for (size_t i = 0; i < dim; i++) - { - auto ex1 = (*tup1.exps)[i]; - auto ex2 = (*tup2.exps)[i]; - auto eeq = new EqualExp(e.op, e.loc, ex1, ex2); - - if (!result) - result = eeq; - else if (e.op == EXP.equal) - result = new LogicalExp(e.loc, EXP.andAnd, result, eeq); - else - result = new LogicalExp(e.loc, EXP.orOr, result, eeq); - } - assert(result); - } - result = Expression.combine(tup1.e0, tup2.e0, result); - result = result.expressionSemantic(sc); + return result; + } + return null; +} - return result; - } +Expression opOverloadCmp(CmpExp exp, Scope* sc, Type[2] aliasThisStop) +{ + //printf("CmpExp:: () (%s)\n", e.toChars()); + EXP cmpOp = exp.op; + auto e = compare_overload(exp, sc, Id.opCmp, cmpOp, aliasThisStop); + if (!e) return null; + + if (!e.type.isScalar() && e.type.equals(exp.e1.type)) + { + error(e.loc, "recursive `opCmp` expansion"); + return ErrorExp.get(); } + if (e.op != EXP.call) + return e; - Expression visitCmp(CmpExp e) + Type t1 = exp.e1.type.toBasetype(); + Type t2 = exp.e2.type.toBasetype(); + if (t1.ty != Tclass || t2.ty != Tclass) { - //printf("CmpExp:: () (%s)\n", e.toChars()); - return compare_overload(e, sc, Id.cmp, pop); + return new CmpExp(cmpOp, exp.loc, e, IntegerExp.literal!0).expressionSemantic(sc); } - /********************************* - * Operator overloading for op= - */ - Expression visitBinAssign(BinAssignExp e) + // Lower to object.__cmp(e1, e2) + Expression cl = new IdentifierExp(exp.loc, Id.empty); + cl = new DotIdExp(exp.loc, cl, Id.object); + cl = new DotIdExp(exp.loc, cl, Id.__cmp); + cl = cl.expressionSemantic(sc); + + auto arguments = new Expressions(); + // Check if op_overload found a better match by calling e2.opCmp(e1) + // If the operands were swapped, then the result must be reversed + // e1.opCmp(e2) == -e2.opCmp(e1) + // cmpop takes care of this + if (exp.op == cmpOp) + { + arguments.push(exp.e1); + arguments.push(exp.e2); + } + else + { + // Use better match found by op_overload + arguments.push(exp.e2); + arguments.push(exp.e1); + } + + cl = new CallExp(e.loc, cl, arguments); + cl = new CmpExp(cmpOp, exp.loc, cl, new IntegerExp(0)); + return cl.expressionSemantic(sc); +} + +/********************************* + * Operator overloading for op= + */ +Expression opOverloadBinaryAssign(BinAssignExp e, Scope* sc, Type[2] aliasThisStop) +{ + if (auto ae = e.e1.isArrayExp()) { - //printf("BinAssignExp::op_overload() (%s)\n", e.toChars()); - if (auto ae = e.e1.isArrayExp()) + ae.e1 = ae.e1.expressionSemantic(sc); + ae.e1 = resolveProperties(sc, ae.e1); + Expression ae1old = ae.e1; + const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval); + IntervalExp ie = null; + if (maybeSlice && ae.arguments.length) { - ae.e1 = ae.e1.expressionSemantic(sc); - ae.e1 = resolveProperties(sc, ae.e1); - Expression ae1old = ae.e1; - const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval); - IntervalExp ie = null; - if (maybeSlice && ae.arguments.length) + ie = (*ae.arguments)[0].isIntervalExp(); + } + Type att = null; // first cyclic `alias this` type + while (true) + { + if (ae.e1.op == EXP.error) { - ie = (*ae.arguments)[0].isIntervalExp(); + return ae.e1; } - Type att = null; // first cyclic `alias this` type - while (true) + Expression e0 = null; + Expression ae1save = ae.e1; + ae.lengthVar = null; + Type t1b = ae.e1.type.toBasetype(); + AggregateDeclaration ad = isAggregate(t1b); + if (!ad) + break; + if (search_function(ad, Id.opIndexOpAssign)) { - if (ae.e1.op == EXP.error) - { - return ae.e1; - } - Expression e0 = null; - Expression ae1save = ae.e1; - ae.lengthVar = null; - Type t1b = ae.e1.type.toBasetype(); - AggregateDeclaration ad = isAggregate(t1b); - if (!ad) - break; - if (search_function(ad, Id.opIndexOpAssign)) - { - // Deal with $ - Expression result = resolveOpDollar(sc, ae, &e0); - if (!result) // (a[i..j] op= e2) might be: a.opSliceOpAssign!(op)(e2, i, j) - goto Lfallback; - if (result.op == EXP.error) - return result; - result = e.e2.expressionSemantic(sc); - if (result.op == EXP.error) - return result; - e.e2 = result; - /* Rewrite a[arguments] op= e2 as: - * a.opIndexOpAssign!(op)(e2, arguments) - */ - Expressions* a = ae.arguments.copy(); - a.insert(0, e.e2); - Objects* tiargs = opToArg(sc, e.op); - result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexOpAssign, tiargs); - result = new CallExp(e.loc, result, a); - if (maybeSlice) // (a[] op= e2) might be: a.opSliceOpAssign!(op)(e2) - result = result.trySemantic(sc); - else - result = result.expressionSemantic(sc); - if (result) - { - return Expression.combine(e0, result); - } - } - Lfallback: - if (maybeSlice && search_function(ad, Id.opSliceOpAssign)) - { - // Deal with $ - Expression result = resolveOpDollar(sc, ae, ie, &e0); - if (result.op == EXP.error) - return result; - result = e.e2.expressionSemantic(sc); - if (result.op == EXP.error) - return result; - e.e2 = result; - /* Rewrite (a[i..j] op= e2) as: - * a.opSliceOpAssign!(op)(e2, i, j) - */ - auto a = new Expressions(); - a.push(e.e2); - if (ie) - { - a.push(ie.lwr); - a.push(ie.upr); - } - Objects* tiargs = opToArg(sc, e.op); - result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceOpAssign, tiargs); - result = new CallExp(e.loc, result, a); - result = result.expressionSemantic(sc); - result = Expression.combine(e0, result); + // Deal with $ + Expression result = resolveOpDollar(sc, ae, &e0); + if (!result) // (a[i..j] op= e2) might be: a.opSliceOpAssign!(op)(e2, i, j) + goto Lfallback; + if (result.op == EXP.error) return result; - } - // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) + result = e.e2.expressionSemantic(sc); + if (result.op == EXP.error) + return result; + e.e2 = result; + /* Rewrite a[arguments] op= e2 as: + * a.opIndexOpAssign!(op)(e2, arguments) + */ + Expressions* a = ae.arguments.copy(); + a.insert(0, e.e2); + Objects* tiargs = opToArg(sc, e.op); + result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexOpAssign, tiargs); + result = new CallExp(e.loc, result, a); + if (maybeSlice) // (a[] op= e2) might be: a.opSliceOpAssign!(op)(e2) + result = result.trySemantic(sc); + else + result = result.expressionSemantic(sc); + if (result) { - /* Rewrite (a[arguments] op= e2) as: - * a.aliasthis[arguments] op= e2 - */ - ae.e1 = resolveAliasThis(sc, ae1save, true); - if (ae.e1) - continue; + return Expression.combine(e0, result); } - break; } - ae.e1 = ae1old; // recovery - ae.lengthVar = null; - } - Expression result = e.binSemanticProp(sc); - if (result) - return result; - // Don't attempt 'alias this' if an error occurred - if (e.e1.type.ty == Terror || e.e2.type.ty == Terror) - { - return ErrorExp.get(); - } - Identifier id = opId(e); - Expressions* args2 = new Expressions(); - AggregateDeclaration ad1 = isAggregate(e.e1.type); - Dsymbol s = null; - Objects* tiargs = null; - /* Try opOpAssign - */ - if (ad1) - { - s = search_function(ad1, Id.opOpAssign); - if (s && !s.isTemplateDeclaration()) + Lfallback: + if (maybeSlice && search_function(ad, Id.opSliceOpAssign)) { - error(e.loc, "`%s.opOpAssign` isn't a template", e.e1.toChars()); - return ErrorExp.get(); + // Deal with $ + Expression result = resolveOpDollar(sc, ae, ie, &e0); + if (result.op == EXP.error) + return result; + result = e.e2.expressionSemantic(sc); + if (result.op == EXP.error) + return result; + e.e2 = result; + /* Rewrite (a[i..j] op= e2) as: + * a.opSliceOpAssign!(op)(e2, i, j) + */ + auto a = new Expressions(); + a.push(e.e2); + if (ie) + { + a.push(ie.lwr); + a.push(ie.upr); + } + Objects* tiargs = opToArg(sc, e.op); + result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceOpAssign, tiargs); + result = new CallExp(e.loc, result, a); + result = result.expressionSemantic(sc); + result = Expression.combine(e0, result); + return result; } - } - // Set tiargs, the template argument list, which will be the operator string - if (s) - { - id = Id.opOpAssign; - tiargs = opToArg(sc, e.op); - } - - // Try D1-style operator overload, deprecated - if (!s && ad1 && id) - { - s = search_function(ad1, id); - if (s) + // Didn't find it. Forward to aliasthis + if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) { - // @@@DEPRECATED_2.110@@@. - // Deprecated in 2.088, made an error in 2.100 - scope char[] op = EXPtoString(e.op).dup; - op[$-1] = '\0'; // remove trailing `=` - error(e.loc, "`%s` is obsolete. Use `opOpAssign(string op)(...) if (op == \"%s\")` instead.", id.toChars(), op.ptr); - return ErrorExp.get(); + /* Rewrite (a[arguments] op= e2) as: + * a.aliasthis[arguments] op= e2 + */ + ae.e1 = resolveAliasThis(sc, ae1save, true); + if (ae.e1) + continue; } + break; } + ae.e1 = ae1old; // recovery + ae.lengthVar = null; + } + Expression result = e.binSemanticProp(sc); + if (result) + return result; + // Don't attempt 'alias this' if an error occurred + if (e.e1.type.ty == Terror || e.e2.type.ty == Terror) + { + return ErrorExp.get(); + } + AggregateDeclaration ad1 = isAggregate(e.e1.type); + Dsymbol s = search_function(ad1, Id.opOpAssign); + if (s && !s.isTemplateDeclaration()) + { + error(e.loc, "`%s.opOpAssign` isn't a template", e.e1.toChars()); + return ErrorExp.get(); + } - if (s) - { - /* Try: - * a.opOpAssign(b) - */ - args2.setDim(1); - (*args2)[0] = e.e2; - expandTuples(args2); - MatchAccumulator m; - functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) - { - return ErrorExp.get(); - } - if (m.count > 1) - { - // Error, ambiguous - error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); - } - else if (m.last == MATCH.nomatch) - { - if (tiargs) - goto L1; - m.lastf = null; - } - // Rewrite (e1 op e2) as e1.opOpAssign(e2) - return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); - } - L1: - result = checkAliasThisForLhs(ad1, sc, e); - if (result || !s) // no point in trying Rhs alias-this if there's no overload of any kind in lhs - return result; + bool choseReverse; + if (auto res = pickBestBinaryOverload(sc, opToArg(sc, e.op), s, null, e, choseReverse)) + return res; - return checkAliasThisForRhs(isAggregate(e.e2.type), sc, e); - } + result = checkAliasThisForLhs(ad1, sc, e, aliasThisStop); + if (result || !s) // no point in trying Rhs alias-this if there's no overload of any kind in lhs + return result; - if (pop) - *pop = e.op; + return checkAliasThisForRhs(isAggregate(e.e2.type), sc, e, aliasThisStop); +} - switch (e.op) - { - case EXP.cast_ : return visitCast(e.isCastExp()); - case EXP.array : return visitArray(e.isArrayExp()); +/** +Given symbols `s` and `s_r`, try to instantiate `e.e1.s!tiargs(e.e2)` and `e.e2.s_r!tiargs(e.e1)`, +and return the one with the best match level. + +Params: + sc = scope + tiargs = (optional) template arguments to instantiate symbols with + s = (optional) symbol of straightforward template (e.g. opBinary) + s_r = (optional) symbol of reversed template (e.g. opBinaryRight) + e = binary expression being overloaded, supplying arguments to the function calls + choseReverse = set to true when `s_r` was chosen instead of `s` +Returns: + Resulting operator overload function call, or `null` if neither symbol worked +*/ +private Expression pickBestBinaryOverload(Scope* sc, Objects* tiargs, Dsymbol s, Dsymbol s_r, BinExp e, out bool choseReverse) +{ + if (!s && !s_r) + return null; - case EXP.notEqual : - case EXP.equal : return visitEqual(e.isEqualExp()); + Expressions* args1 = new Expressions(1); + (*args1)[0] = e.e1; + expandTuples(args1); + Expressions* args2 = new Expressions(1); + (*args2)[0] = e.e2; + expandTuples(args2); + MatchAccumulator m; - case EXP.lessOrEqual : - case EXP.greaterThan : - case EXP.greaterOrEqual: - case EXP.lessThan : return visitCmp(cast(CmpExp)e); + if (s) + { + functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2)); + if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) + return ErrorExp.get(); + } + FuncDeclaration lastf = m.lastf; + int count = m.count; + if (s_r) + { + functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1)); + if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) + return ErrorExp.get(); + } + if (m.count > 1) + { + /* The following if says "not ambiguous" if there's one match + * from s and one from s_r, in which case we pick s. + * This doesn't follow the spec, but is a workaround for the case + * where opEquals was generated from templates and we cannot figure + * out if both s and s_r came from the same declaration or not. + * The test case is: + * import std.typecons; + * void main() { + * assert(tuple("has a", 2u) == tuple("has a", 1)); + * } + */ + if (!(m.lastf == lastf && m.count == 2 && count == 1)) + { + // Error, ambiguous + error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); + } + } + else if (m.last == MATCH.nomatch) + { + if (tiargs) + return null; + m.lastf = null; + } - default: - if (auto ex = e.isBinAssignExp()) return visitBinAssign(ex); - if (auto ex = e.isBinExp()) return visitBin(ex); - if (auto ex = e.isUnaExp()) return visitUna(ex); - return visit(e); + if (lastf && m.lastf == lastf || !s_r && m.last == MATCH.nomatch) + { + choseReverse = false; + // Rewrite (e1 op e2) as e1.opfunc(e2) + return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); + } + else + { + choseReverse = true; + // Rewrite (e1 op e2) as e2.opfunc_r(e1) + return build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r); } } /****************************************** * Common code for overloading of EqualExp and CmpExp */ -private Expression compare_overload(BinExp e, Scope* sc, Identifier id, EXP* pop) +private Expression compare_overload(BinExp e, Scope* sc, Identifier id, ref EXP cmpOp, Type[2] aliasThisStop) { //printf("BinExp::compare_overload(id = %s) %s\n", id.toChars(), e.toChars()); AggregateDeclaration ad1 = isAggregate(e.e1.type); AggregateDeclaration ad2 = isAggregate(e.e2.type); - Dsymbol s = null; - Dsymbol s_r = null; - if (ad1) - { - s = search_function(ad1, id); - } - if (ad2) + Dsymbol s = search_function(ad1, id); + Dsymbol s_r = search_function(ad2, id); + + if (s == s_r) + s_r = null; + + bool choseReverse; + if (auto res = pickBestBinaryOverload(sc, null, s, s_r, e, choseReverse)) { - s_r = search_function(ad2, id); - if (s == s_r) - s_r = null; - } - Objects* tiargs = null; - if (s || s_r) - { - /* Try: - * a.opEquals(b) - * b.opEquals(a) - * and see which is better. - */ - Expressions* args1 = new Expressions(1); - (*args1)[0] = e.e1; - expandTuples(args1); - Expressions* args2 = new Expressions(1); - (*args2)[0] = e.e2; - expandTuples(args2); - MatchAccumulator m; - if (0 && s && s_r) - { - printf("s : %s\n", s.toPrettyChars()); - printf("s_r: %s\n", s_r.toPrettyChars()); - } - if (s) - { - functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) - return ErrorExp.get(); - } - FuncDeclaration lastf = m.lastf; - int count = m.count; - if (s_r) - { - functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) - return ErrorExp.get(); - } - if (m.count > 1) - { - /* The following if says "not ambiguous" if there's one match - * from s and one from s_r, in which case we pick s. - * This doesn't follow the spec, but is a workaround for the case - * where opEquals was generated from templates and we cannot figure - * out if both s and s_r came from the same declaration or not. - * The test case is: - * import std.typecons; - * void main() { - * assert(tuple("has a", 2u) == tuple("has a", 1)); - * } - */ - if (!(m.lastf == lastf && m.count == 2 && count == 1)) - { - // Error, ambiguous - error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); - } - } - else if (m.last == MATCH.nomatch) - { - m.lastf = null; - } - Expression result; - if (lastf && m.lastf == lastf || !s_r && m.last == MATCH.nomatch) - { - // Rewrite (e1 op e2) as e1.opfunc(e2) - result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); - } - else - { - // Rewrite (e1 op e2) as e2.opfunc_r(e1) - result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r); - // When reversing operands of comparison operators, - // need to reverse the sense of the op - if (pop) - *pop = reverseRelation(e.op); - } - return result; + if (choseReverse) + cmpOp = reverseRelation(e.op); + return res; } + /* * https://issues.dlang.org/show_bug.cgi?id=16657 * at this point, no matching opEquals was found for structs, @@ -1400,8 +1071,8 @@ private Expression compare_overload(BinExp e, Scope* sc, Identifier id, EXP* pop */ if ((e.op == EXP.equal || e.op == EXP.notEqual) && ad1 == ad2) return null; - Expression result = checkAliasThisForLhs(ad1, sc, e); - return result ? result : checkAliasThisForRhs(isAggregate(e.e2.type), sc, e); + Expression result = checkAliasThisForLhs(ad1, sc, e, aliasThisStop); + return result ? result : checkAliasThisForRhs(isAggregate(e.e2.type), sc, e, aliasThisStop); } /*********************************** @@ -1425,6 +1096,8 @@ Expression build_overload(const ref Loc loc, Scope* sc, Expression ethis, Expres */ Dsymbol search_function(ScopeDsymbol ad, Identifier funcid) { + if (!ad) + return null; if (Dsymbol s = ad.search(Loc.initial, funcid)) { //printf("search_function: s = '%s'\n", s.kind()); diff --git a/gcc/d/dmd/parse.d b/gcc/d/dmd/parse.d index b851b9a..2e29762 100644 --- a/gcc/d/dmd/parse.d +++ b/gcc/d/dmd/parse.d @@ -2759,35 +2759,10 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer error("`new` allocator must be annotated with `@disabled`"); } nextToken(); - - /* @@@DEPRECATED_2.108@@@ - * After deprecation period (2.108), remove all code in the version(all) block. - */ - version (all) - { - auto parameterList = parseParameterList(null); // parameterList ignored - if (parameterList.parameters.length > 0 || parameterList.varargs != VarArg.none) - deprecation("`new` allocator with non-empty parameter list is deprecated"); - auto f = new AST.NewDeclaration(loc, stc); - if (token.value != TOK.semicolon) - { - deprecation("`new` allocator with function definition is deprecated"); - parseContracts(f); // body ignored - f.fbody = null; - f.fensures = null; - f.frequires = null; - } - else - nextToken(); - return f; - } - else - { - check(TOK.leftParenthesis); - check(TOK.rightParenthesis); - check(TOK.semicolon); - return new AST.NewDeclaration(loc, stc); - } + check(TOK.leftParenthesis); + check(TOK.rightParenthesis); + check(TOK.semicolon); + return new AST.NewDeclaration(loc, stc); } /********************************************** @@ -5844,7 +5819,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.plusPlus: case TOK.minusMinus: case TOK.new_: - case TOK.delete_: case TOK.delegate_: case TOK.function_: case TOK.typeid_: @@ -8713,15 +8687,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer e = new AST.ComExp(loc, e); break; - case TOK.delete_: - // @@@DEPRECATED_2.109@@@ - // Use of `delete` keyword has been an error since 2.099. - // Remove from the parser after 2.109. - nextToken(); - e = parseUnaryExp(); - e = new AST.DeleteExp(loc, e, false); - break; - case TOK.cast_: // cast(type) expression { nextToken(); @@ -8839,7 +8804,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.dot: case TOK.plusPlus: case TOK.minusMinus: - case TOK.delete_: case TOK.new_: case TOK.leftParenthesis: case TOK.identifier: diff --git a/gcc/d/dmd/semantic3.d b/gcc/d/dmd/semantic3.d index def7d46..89f612c 100644 --- a/gcc/d/dmd/semantic3.d +++ b/gcc/d/dmd/semantic3.d @@ -270,7 +270,7 @@ private extern(C++) final class Semantic3Visitor : Visitor //{ static int x; if (++x == 2) *(char*)0=0; } //printf("\tlinkage = %d\n", sc.linkage); - if (funcdecl.ident == Id.assign && !funcdecl.inuse) + if (funcdecl.ident == Id.opAssign && !funcdecl.inuse) { if (funcdecl.storage_class & STC.inference) { diff --git a/gcc/d/dmd/tokens.d b/gcc/d/dmd/tokens.d index b499c00..e82a582 100644 --- a/gcc/d/dmd/tokens.d +++ b/gcc/d/dmd/tokens.d @@ -48,7 +48,6 @@ enum TOK : ubyte false_, throw_, new_, - delete_, variable, slice, version_, @@ -459,7 +458,6 @@ private immutable TOK[] keywords = TOK.false_, TOK.cast_, TOK.new_, - TOK.delete_, TOK.throw_, TOK.module_, TOK.pragma_, @@ -680,7 +678,6 @@ extern (C++) struct Token TOK.false_: "false", TOK.cast_: "cast", TOK.new_: "new", - TOK.delete_: "delete", TOK.throw_: "throw", TOK.module_: "module", TOK.pragma_: "pragma", diff --git a/gcc/d/dmd/tokens.h b/gcc/d/dmd/tokens.h index 2a984b4..c12a00b 100644 --- a/gcc/d/dmd/tokens.h +++ b/gcc/d/dmd/tokens.h @@ -54,7 +54,6 @@ enum class TOK : unsigned char false_, throw_, new_, - delete_, variable, slice, version_, diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d index ca10db1..65d267f 100644 --- a/gcc/d/dmd/typesem.d +++ b/gcc/d/dmd/typesem.d @@ -2025,7 +2025,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) } else if (sd.xeq == sd.xerreq) { - if (search_function(sd, Id.eq)) + if (search_function(sd, Id.opEquals)) { .error(loc, "%sAA key type `%s` does not have `bool opEquals(ref const %s) const`", s, sd.toChars(), sd.toChars()); } @@ -2037,7 +2037,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) } else if (!sd.xhash) { - if (search_function(sd, Id.eq)) + if (search_function(sd, Id.opEquals)) { .error(loc, "%sAA key type `%s` should have `extern (D) size_t toHash() const nothrow @safe` if `opEquals` defined", s, sd.toChars()); } @@ -2075,9 +2075,9 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) __gshared FuncDeclaration fcmp = null; __gshared FuncDeclaration fhash = null; if (!feq) - feq = search_function(ClassDeclaration.object, Id.eq).isFuncDeclaration(); + feq = search_function(ClassDeclaration.object, Id.opEquals).isFuncDeclaration(); if (!fcmp) - fcmp = search_function(ClassDeclaration.object, Id.cmp).isFuncDeclaration(); + fcmp = search_function(ClassDeclaration.object, Id.opCmp).isFuncDeclaration(); if (!fhash) fhash = search_function(ClassDeclaration.object, Id.tohash).isFuncDeclaration(); assert(fcmp && feq && fhash); @@ -3417,7 +3417,7 @@ Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden if (s) error(loc, "no property `%s` for type `%s`, did you mean `%s`?", ident.toChars(), mt.toChars(), s.toPrettyChars()); - else if (ident == Id.call && mt.ty == Tclass) + else if (ident == Id.opCall && mt.ty == Tclass) error(loc, "no property `%s` for type `%s`, did you mean `new %s`?", ident.toChars(), mt.toChars(), mt.toPrettyChars()); else if (const n = importHint(ident.toString())) @@ -4883,7 +4883,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag /*************************************** * `ident` was not found as a member of `mt`. - * Attempt to use overloaded opDot(), overloaded opDispatch(), or `alias this`. + * Attempt to use overloaded opDispatch() or `alias this`. * If that fails, forward to visitType(). * Params: * mt = class or struct @@ -4939,21 +4939,6 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag ident != Id.postblit && ident != Id.__xpostblit) { - /* Look for overloaded opDot() to see if we should forward request - * to it. - */ - if (auto fd = search_function(sym, Id.opDot)) - { - /* Rewrite e.ident as: - * e.opDot().ident - */ - e = build_overload(e.loc, sc, e, null, fd); - // @@@DEPRECATED_2.110@@@. - // Deprecated in 2.082, made an error in 2.100. - error(e.loc, "`opDot` is obsolete. Use `alias this`"); - return ErrorExp.get(); - } - /* Look for overloaded opDispatch to see if we should forward request * to it. */ |