*
* Specification: $(LINK2 https://dlang.org/spec/operatoroverloading.html, Operator Overloading)
*
- * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved
+ * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
* License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/opover.d, _opover.d)
import dmd.hdrgen;
import dmd.id;
import dmd.identifier;
+import dmd.location;
import dmd.mtype;
import dmd.statement;
import dmd.tokens;
* Determine if operands of binary op can be reversed
* to fit operator overload.
*/
-bool isCommutative(EXP op)
+bool isCommutative(EXP op) @safe
{
switch (op)
{
*/
if (isRecursiveAliasThis(e.att1, e.e1.type))
return null;
- //printf("att %s e1 = %s\n", Token::toChars(e.op), e.e1.type.toChars());
+ //printf("att %s e1 = %s\n", Token.toChars(e.op), e.e1.type.toChars());
BinExp be = cast(BinExp)e.copy();
// Resolve 'alias this' but in case of assigment don't resolve properties yet
// because 'e1 = e2' could mean 'e1(e2)' or 'e1() = e2'
*/
if (isRecursiveAliasThis(e.att2, e.e2.type))
return null;
- //printf("att %s e2 = %s\n", Token::toChars(e.op), e.e2.type.toChars());
+ //printf("att %s e2 = %s\n", Token.toChars(e.op), e.e2.type.toChars());
BinExp be = cast(BinExp)e.copy();
be.e2 = resolveAliasThis(sc, e.e2, true);
if (!be.e2)
*/
Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
{
- extern (C++) final class OpOverload : Visitor
- {
- alias visit = Visitor.visit;
- public:
- Scope* sc;
- EXP* pop;
- Expression result;
-
- extern (D) this(Scope* sc, EXP* pop)
- {
- this.sc = sc;
- this.pop = pop;
- }
-
- override void visit(Expression e)
+ Expression visit(Expression e)
{
assert(0);
}
- override void visit(UnaExp e)
+ Expression visitUna(UnaExp e)
{
//printf("UnaExp::op_overload() (%s)\n", e.toChars());
- if (e.e1.op == EXP.array)
+ Expression result;
+ if (auto ae = e.e1.isArrayExp())
{
- ArrayExp ae = cast(ArrayExp)e.e1;
ae.e1 = ae.e1.expressionSemantic(sc);
ae.e1 = resolveProperties(sc, ae.e1);
Expression ae1old = ae.e1;
- const(bool) maybeSlice = (ae.arguments.dim == 0 || ae.arguments.dim == 1 && (*ae.arguments)[0].op == EXP.interval);
+ const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval);
IntervalExp ie = null;
- if (maybeSlice && ae.arguments.dim)
+ if (maybeSlice && ae.arguments.length)
{
- assert((*ae.arguments)[0].op == EXP.interval);
- ie = cast(IntervalExp)(*ae.arguments)[0];
+ ie = (*ae.arguments)[0].isIntervalExp();
}
+ Type att = null; // first cyclic `alias this` type
while (true)
{
if (ae.e1.op == EXP.error)
{
- result = ae.e1;
- return;
+ return ae.e1;
}
Expression e0 = null;
Expression ae1save = ae.e1;
if (!result) // op(a[i..j]) might be: a.opSliceUnary!(op)(i, j)
goto Lfallback;
if (result.op == EXP.error)
- return;
+ return result;
/* Rewrite op(a[arguments]) as:
* a.opIndexUnary!(op)(arguments)
*/
result = result.expressionSemantic(sc);
if (result)
{
- result = Expression.combine(e0, result);
- return;
+ return Expression.combine(e0, result);
}
}
Lfallback:
// Deal with $
result = resolveOpDollar(sc, ae, ie, &e0);
if (result.op == EXP.error)
- return;
+ return result;
/* Rewrite op(a[i..j]) as:
* a.opSliceUnary!(op)(i, j)
*/
result = new CallExp(e.loc, result, a);
result = result.expressionSemantic(sc);
result = Expression.combine(e0, result);
- return;
+ return result;
}
// Didn't find it. Forward to aliasthis
- if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type))
+ if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type))
{
/* Rewrite op(a[arguments]) as:
* op(a.aliasthis[arguments])
}
e.e1 = e.e1.expressionSemantic(sc);
e.e1 = resolveProperties(sc, e.e1);
- if (e.e1.op == EXP.error)
- {
- result = e.e1;
- return;
- }
- AggregateDeclaration ad = isAggregate(e.e1.type);
- if (ad)
+ 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)()
result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs);
result = new CallExp(e.loc, result);
result = result.expressionSemantic(sc);
- return;
+ return result;
}
// D1-style operator overloads, deprecated
if (e.op != EXP.prePlusPlus && e.op != EXP.preMinusMinus)
fd = search_function(ad, id);
if (fd)
{
- // @@@DEPRECATED_2.098@@@.
- // Deprecated in 2.088
- // Make an error in 2.098
- e.deprecation("`%s` is deprecated. Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr);
- // Rewrite +e1 as e1.add()
- result = build_overload(e.loc, sc, e.e1, null, fd);
- return;
+ // @@@DEPRECATED_2.110@@@.
+ // Deprecated in 2.088, made an error in 2.100
+ e.error("`%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(e.att1, e.e1.type))
+ 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());
- Expression e1 = new DotIdExp(e.loc, e.e1, ad.aliasthis.ident);
- UnaExp ue = cast(UnaExp)e.copy();
- ue.e1 = e1;
- result = ue.trySemantic(sc);
- return;
+ if (auto e1 = resolveAliasThis(sc, e.e1, true))
+ {
+ e.e1 = e1;
+ continue;
+ }
+ break;
}
+ break;
}
+ return result;
}
- override void visit(ArrayExp ae)
+ Expression visitArray(ArrayExp ae)
{
//printf("ArrayExp::op_overload() (%s)\n", ae.toChars());
ae.e1 = ae.e1.expressionSemantic(sc);
ae.e1 = resolveProperties(sc, ae.e1);
Expression ae1old = ae.e1;
- const(bool) maybeSlice = (ae.arguments.dim == 0 || ae.arguments.dim == 1 && (*ae.arguments)[0].op == EXP.interval);
+ const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval);
IntervalExp ie = null;
- if (maybeSlice && ae.arguments.dim)
+ if (maybeSlice && ae.arguments.length)
{
- assert((*ae.arguments)[0].op == EXP.interval);
- ie = cast(IntervalExp)(*ae.arguments)[0];
+ ie = (*ae.arguments)[0].isIntervalExp();
}
+ Expression result;
+ Type att = null; // first cyclic `alias this` type
while (true)
{
if (ae.e1.op == EXP.error)
{
- result = ae.e1;
- return;
+ return ae.e1;
}
Expression e0 = null;
Expression ae1save = ae.e1;
{
result = new SliceExp(ae.loc, ae.e1, ie);
result = result.expressionSemantic(sc);
- return;
+ return result;
}
// Convert to IndexExp
- if (ae.arguments.dim == 1)
+ if (ae.arguments.length == 1)
{
result = new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]);
result = result.expressionSemantic(sc);
- return;
+ return result;
}
}
break;
if (!result) // a[i..j] might be: a.opSlice(i, j)
goto Lfallback;
if (result.op == EXP.error)
- return;
+ return result;
/* Rewrite e1[arguments] as:
* e1.opIndex(arguments)
*/
result = result.expressionSemantic(sc);
if (result)
{
- result = Expression.combine(e0, result);
- return;
+ return Expression.combine(e0, result);
}
}
Lfallback:
result = new SliceExp(ae.loc, ae.e1, ie);
result = result.expressionSemantic(sc);
result = Expression.combine(e0, result);
- return;
+ return result;
}
if (maybeSlice && search_function(ad, Id.slice))
{
if (!e0 && !search_function(ad, Id.dollar)) {
ae.loc.errorSupplemental("Aggregate declaration '%s' does not define 'opDollar'", ae.e1.toChars());
}
- return;
+ return result;
}
/* Rewrite a[i..j] as:
* a.opSlice(i, j)
result = new CallExp(ae.loc, result, a);
result = result.expressionSemantic(sc);
result = Expression.combine(e0, result);
- return;
+ return result;
}
// Didn't find it. Forward to aliasthis
- if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type))
+ if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type))
{
//printf("att arr e1 = %s\n", this.e1.type.toChars());
/* Rewrite op(a[arguments]) as:
}
ae.e1 = ae1old; // recovery
ae.lengthVar = null;
+ return result;
}
/***********************************************
* This is mostly the same as UnaryExp::op_overload(), but has
* a different rewrite.
*/
- override void visit(CastExp e)
+ Expression visitCast(CastExp e, Type att = null)
{
//printf("CastExp::op_overload() (%s)\n", e.toChars());
+ Expression result;
AggregateDeclaration ad = isAggregate(e.e1.type);
if (ad)
{
if (fd.isFuncDeclaration())
{
// Rewrite as: e1.opCast()
- result = build_overload(e.loc, sc, e.e1, null, fd);
- return;
+ return build_overload(e.loc, sc, e.e1, null, fd);
}
}
auto tiargs = new Objects();
result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs);
result = new CallExp(e.loc, result);
result = result.expressionSemantic(sc);
- return;
+ return result;
}
// Didn't find it. Forward to aliasthis
- if (ad.aliasthis && !isRecursiveAliasThis(e.att1, e.e1.type))
+ if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type))
{
/* Rewrite op(e1) as:
* op(e1.aliasthis)
{
result = e.copy();
(cast(UnaExp)result).e1 = e1;
- result = result.op_overload(sc);
- return;
+ result = visitCast(result.isCastExp(), att);
+ return result;
}
}
}
+ return result;
}
- override void visit(BinExp e)
+ Expression visitBin(BinExp e)
{
//printf("BinExp::op_overload() (%s)\n", e.toChars());
Identifier id = opId(e);
Identifier id_r = opId_r(e);
- Expressions args1;
- Expressions args2;
int argsset = 0;
AggregateDeclaration ad1 = isAggregate(e.e1.type);
AggregateDeclaration ad2 = isAggregate(e.e2.type);
(sd.hasBlitAssign && !e.e2.isLvalue())))
{
/* This is bitwise struct assignment. */
- return;
+ return null;
}
}
Dsymbol s = null;
{
// Bug4099 fix
if (ad1 && search_function(ad1, Id.opUnary))
- return;
+ return null;
}
if (e.op != EXP.equal && e.op != EXP.notEqual && e.op != EXP.assign && e.op != EXP.plusPlus && e.op != EXP.minusMinus)
{
if (s && !s.isTemplateDeclaration())
{
e.e1.error("`%s.opBinary` isn't a template", e.e1.toChars());
- result = ErrorExp.get();
- return;
+ return ErrorExp.get();
}
}
if (ad2)
if (s_r && !s_r.isTemplateDeclaration())
{
e.e2.error("`%s.opBinaryRight` isn't a template", e.e2.toChars());
- result = ErrorExp.get();
- return;
+ return ErrorExp.get();
}
if (s_r && s_r == s) // https://issues.dlang.org/show_bug.cgi?id=12778
s_r = null;
s = search_function(ad1, id);
if (s && id != Id.assign)
{
- // @@@DEPRECATED_2.098@@@.
- // Deprecated in 2.088
- // Make an error in 2.098
+ // @@@DEPRECATED_2.110@@@.
+ // Deprecated in 2.088, made an error in 2.100
if (id == Id.postinc || id == Id.postdec)
- e.deprecation("`%s` is deprecated. Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr);
+ e.error("`%s` is obsolete. Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr);
else
- e.deprecation("`%s` is deprecated. Use `opBinary(string op)(...) if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr);
+ e.error("`%s` is obsolete. Use `opBinary(string op)(...) if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr);
+ return ErrorExp.get();
}
}
if (ad2 && id_r)
s_r = null;
if (s_r)
{
- // @@@DEPRECATED_2.098@@@.
- // Deprecated in 2.088
- // Make an error in 2.098
- e.deprecation("`%s` is deprecated. Use `opBinaryRight(string op)(...) if (op == \"%s\")` instead.", id_r.toChars(), EXPtoString(e.op).ptr);
+ // @@@DEPRECATED_2.110@@@.
+ // Deprecated in 2.088, made an error in 2.100
+ e.error("`%s` is obsolete. Use `opBinaryRight(string op)(...) if (op == \"%s\")` instead.", id_r.toChars(), EXPtoString(e.op).ptr);
+ return ErrorExp.get();
}
}
}
+ Expressions* args1 = new Expressions();
+ Expressions* args2 = new Expressions();
if (s || s_r)
{
/* Try:
* and see which is better.
*/
args1.setDim(1);
- args1[0] = e.e1;
- expandTuples(&args1);
+ (*args1)[0] = e.e1;
+ expandTuples(args1);
args2.setDim(1);
- args2[0] = e.e2;
- expandTuples(&args2);
+ (*args2)[0] = e.e2;
+ expandTuples(args2);
argsset = 1;
MatchAccumulator m;
if (s)
{
- functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, &args2);
- if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
+ functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2));
+ if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
{
- result = ErrorExp.get();
- return;
+ return ErrorExp.get();
}
}
FuncDeclaration lastf = m.lastf;
if (s_r)
{
- functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, &args1);
- if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
+ functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1));
+ if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
{
- result = ErrorExp.get();
- return;
+ return ErrorExp.get();
}
}
if (m.count > 1)
// as unary, but it's implemented as a binary.
// Rewrite (e1 ++ e2) as e1.postinc()
// Rewrite (e1 -- e2) as e1.postdec()
- result = build_overload(e.loc, sc, e.e1, null, m.lastf ? m.lastf : s);
+ 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)
- result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
+ 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)
- result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r);
+ return build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r);
}
- return;
}
L1:
version (all)
if (!argsset)
{
args1.setDim(1);
- args1[0] = e.e1;
- expandTuples(&args1);
+ (*args1)[0] = e.e1;
+ expandTuples(args1);
args2.setDim(1);
- args2[0] = e.e2;
- expandTuples(&args2);
+ (*args2)[0] = e.e2;
+ expandTuples(args2);
}
MatchAccumulator m;
if (s_r)
{
- functionResolve(m, s_r, e.loc, sc, tiargs, e.e1.type, &args2);
- if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
+ functionResolve(m, s_r, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2));
+ if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
{
- result = ErrorExp.get();
- return;
+ return ErrorExp.get();
}
}
FuncDeclaration lastf = m.lastf;
if (s)
{
- functionResolve(m, s, e.loc, sc, tiargs, e.e2.type, &args1);
- if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
+ functionResolve(m, s, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1));
+ if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
{
- result = ErrorExp.get();
- return;
+ return ErrorExp.get();
}
}
if (m.count > 1)
if (lastf && m.lastf == lastf || !s && m.last == MATCH.nomatch)
{
// Rewrite (e1 op e2) as e1.opfunc_r(e2)
- result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s_r);
+ 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)
- result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s);
+ 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;
}
- return;
}
}
}
- Expression tempResult;
+ Expression rewrittenLhs;
if (!(e.op == EXP.assign && ad2 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943
{
- result = checkAliasThisForLhs(ad1, sc, e);
- if (result)
+ if (Expression result = checkAliasThisForLhs(ad1, sc, e))
{
/* https://issues.dlang.org/show_bug.cgi?id=19441
*
* 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.dim == 2 && ad1.vthis`
+ * one of the members, hence the `ad1.fields.length == 2 && ad1.vthis`
* condition.
*/
- if (e.op != EXP.assign || e.e1.op == EXP.type)
- return;
+ 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`
- if (ad1.fields.dim == 1 || (ad1.fields.dim == 2 && ad1.vthis))
+ auto dve = ae.e1.isDotVarExp();
+ if (auto ad = dve.var.isMember2())
{
- auto var = ad1.aliasthis.sym.isVarDeclaration();
- if (var && var.type == ad1.fields[0].type)
- return;
-
- auto func = ad1.aliasthis.sym.isFuncDeclaration();
- auto tf = cast(TypeFunction)(func.type);
- if (tf.isref && ad1.fields[0].type == tf.next)
- return;
+ // 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;
+ }
}
- tempResult = result;
+ rewrittenLhs = ae.e1;
}
}
if (!(e.op == EXP.assign && ad1 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943
{
- result = checkAliasThisForRhs(ad2, sc, e);
- if (result)
- return;
+ if (Expression result = checkAliasThisForRhs(ad2, sc, e))
+ return result;
}
-
- // @@@DEPRECATED_2019-02@@@
- // 1. Deprecation for 1 year
- // 2. Turn to error after
- if (tempResult)
+ if (rewrittenLhs)
{
- // move this line where tempResult is assigned to result and turn to error when derecation period is over
- e.deprecation("Cannot use `alias this` to partially initialize variable `%s` of type `%s`. Use `%s`", e.e1.toChars(), ad1.toChars(), (cast(BinExp)tempResult).e1.toChars());
- // delete this line when deprecation period is over
- result = tempResult;
+ e.error("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;
}
- override void visit(EqualExp e)
+ Expression visitEqual(EqualExp e)
{
//printf("EqualExp::op_overload() (%s)\n", e.toChars());
Type t1 = e.e1.type.toBasetype();
if ((t1.ty == Tarray || t1.ty == Tsarray) &&
(t2.ty == Tarray || t2.ty == Tsarray))
{
- return;
+ return null;
}
/* Check for class equality with null literal or typeof(null).
e.error("use `%s` instead of `%s` when comparing with `null`",
EXPtoString(e.op == EXP.equal ? EXP.identity : EXP.notIdentity).ptr,
EXPtoString(e.op).ptr);
- result = ErrorExp.get();
- return;
+ 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;
+ return null;
}
/* Check for class equality.
/* Rewrite as:
* .object.opEquals(e1, e2)
*/
+ if (!ClassDeclaration.object)
+ {
+ e.error("cannot compare classes for equality because `object.Object` was not declared");
+ return null;
+ }
+
Expression e1x = e.e1;
Expression e2x = e.e2;
if (cd2.isInterfaceDeclaration())
e2x = new CastExp(e.loc, e.e2, t2.isMutable() ? to : to.constOf());
- result = new IdentifierExp(e.loc, Id.empty);
+ 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;
+ return result;
}
}
- result = compare_overload(e, sc, Id.eq, null);
- if (result)
+ if (Expression result = compare_overload(e, sc, Id.eq, null))
{
if (lastComma(result).op == EXP.call && e.op == EXP.notEqual)
{
result = new NotExp(result.loc, result);
result = result.expressionSemantic(sc);
}
- return;
+ return result;
}
/* Check for pointer equality.
* as the backend input.
*/
auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity;
- result = new IdentityExp(op2, e.loc, e.e1, e.e2);
- result = result.expressionSemantic(sc);
- return;
+ 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 = (cast(TypeStruct)t1).sym;
- if (sd != (cast(TypeStruct)t2).sym)
- return;
+ auto sd = t1.isTypeStruct().sym;
+ if (sd != t2.isTypeStruct().sym)
+ return null;
import dmd.clone : needOpEquals;
- if (!global.params.fieldwise && !needOpEquals(sd))
+ if (global.params.fieldwise != FeatureState.enabled && !needOpEquals(sd))
{
// Use bitwise equality.
auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity;
- result = new IdentityExp(op2, e.loc, e.e1, e.e2);
- result = result.expressionSemantic(sc);
- return;
+ Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2);
+ return r.expressionSemantic(sc);
}
/* Do memberwise equality.
* also compare the parent class's equality. Otherwise, compares
* the identity of parent context through void*.
*/
- if (e.att1 && t1.equivalent(e.att1)) return;
- if (e.att2 && t2.equivalent(e.att2)) return;
-
- e = cast(EqualExp)e.copy();
- if (!e.att1) e.att1 = t1;
- if (!e.att2) e.att2 = t2;
+ 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.flags |= SCOPE.noaccesscheck;
- result = e.expressionSemantic(sc2);
+ Expression r = e.expressionSemantic(sc2);
sc2.pop();
-
- /* https://issues.dlang.org/show_bug.cgi?id=15292
- * if the rewrite result is same with the original,
- * the equality is unresolvable because it has recursive definition.
- */
- if (result.op == e.op &&
- (cast(EqualExp)result).e1.type.toBasetype() == t1)
- {
- e.error("cannot compare `%s` because its auto generated member-wise equality has recursive definition",
- t1.toChars());
- result = ErrorExp.get();
- }
- return;
+ return r;
}
/* Check for tuple equality.
*/
if (e.e1.op == EXP.tuple && e.e2.op == EXP.tuple)
{
- auto tup1 = cast(TupleExp)e.e1;
- auto tup2 = cast(TupleExp)e.e2;
- size_t dim = tup1.exps.dim;
- if (dim != tup2.exps.dim)
+ auto tup1 = e.e1.isTupleExp();
+ auto tup2 = e.e2.isTupleExp();
+ size_t dim = tup1.exps.length;
+ if (dim != tup2.exps.length)
{
- e.error("mismatched tuple lengths, `%d` and `%d`",
- cast(int)dim, cast(int)tup2.exps.dim);
- result = ErrorExp.get();
- return;
+ e.error("mismatched sequence lengths, `%d` and `%d`",
+ cast(int)dim, cast(int)tup2.exps.length);
+ return ErrorExp.get();
}
+ Expression result;
if (dim == 0)
{
// zero-length tuple comparison should always return true or false.
auto ex1 = (*tup1.exps)[i];
auto ex2 = (*tup2.exps)[i];
auto eeq = new EqualExp(e.op, e.loc, ex1, ex2);
- eeq.att1 = e.att1;
- eeq.att2 = e.att2;
if (!result)
result = eeq;
result = Expression.combine(tup1.e0, tup2.e0, result);
result = result.expressionSemantic(sc);
- return;
+ return result;
}
+ return null;
}
- override void visit(CmpExp e)
+ Expression visitCmp(CmpExp e)
{
//printf("CmpExp:: () (%s)\n", e.toChars());
- result = compare_overload(e, sc, Id.cmp, pop);
+ return compare_overload(e, sc, Id.cmp, pop);
}
/*********************************
* Operator overloading for op=
*/
- override void visit(BinAssignExp e)
+ Expression visitBinAssign(BinAssignExp e)
{
//printf("BinAssignExp::op_overload() (%s)\n", e.toChars());
- if (e.e1.op == EXP.array)
+ if (auto ae = e.e1.isArrayExp())
{
- ArrayExp ae = cast(ArrayExp)e.e1;
ae.e1 = ae.e1.expressionSemantic(sc);
ae.e1 = resolveProperties(sc, ae.e1);
Expression ae1old = ae.e1;
- const(bool) maybeSlice = (ae.arguments.dim == 0 || ae.arguments.dim == 1 && (*ae.arguments)[0].op == EXP.interval);
+ const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval);
IntervalExp ie = null;
- if (maybeSlice && ae.arguments.dim)
+ if (maybeSlice && ae.arguments.length)
{
- assert((*ae.arguments)[0].op == EXP.interval);
- ie = cast(IntervalExp)(*ae.arguments)[0];
+ ie = (*ae.arguments)[0].isIntervalExp();
}
+ Type att = null; // first cyclic `alias this` type
while (true)
{
if (ae.e1.op == EXP.error)
{
- result = ae.e1;
- return;
+ return ae.e1;
}
Expression e0 = null;
Expression ae1save = ae.e1;
if (search_function(ad, Id.opIndexOpAssign))
{
// Deal with $
- result = resolveOpDollar(sc, ae, &e0);
+ 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;
+ return result;
result = e.e2.expressionSemantic(sc);
if (result.op == EXP.error)
- return;
+ return result;
e.e2 = result;
/* Rewrite a[arguments] op= e2 as:
* a.opIndexOpAssign!(op)(e2, arguments)
result = result.expressionSemantic(sc);
if (result)
{
- result = Expression.combine(e0, result);
- return;
+ return Expression.combine(e0, result);
}
}
Lfallback:
if (maybeSlice && search_function(ad, Id.opSliceOpAssign))
{
// Deal with $
- result = resolveOpDollar(sc, ae, ie, &e0);
+ Expression result = resolveOpDollar(sc, ae, ie, &e0);
if (result.op == EXP.error)
- return;
+ return result;
result = e.e2.expressionSemantic(sc);
if (result.op == EXP.error)
- return;
+ return result;
e.e2 = result;
/* Rewrite (a[i..j] op= e2) as:
* a.opSliceOpAssign!(op)(e2, i, j)
result = new CallExp(e.loc, result, a);
result = result.expressionSemantic(sc);
result = Expression.combine(e0, result);
- return;
+ return result;
}
// Didn't find it. Forward to aliasthis
- if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type))
+ if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type))
{
/* Rewrite (a[arguments] op= e2) as:
* a.aliasthis[arguments] op= e2
ae.e1 = ae1old; // recovery
ae.lengthVar = null;
}
- result = e.binSemanticProp(sc);
+ Expression result = e.binSemanticProp(sc);
if (result)
- return;
+ return result;
// Don't attempt 'alias this' if an error occurred
if (e.e1.type.ty == Terror || e.e2.type.ty == Terror)
{
- result = ErrorExp.get();
- return;
+ return ErrorExp.get();
}
Identifier id = opId(e);
- Expressions args2;
+ Expressions* args2 = new Expressions();
AggregateDeclaration ad1 = isAggregate(e.e1.type);
Dsymbol s = null;
Objects* tiargs = null;
if (s && !s.isTemplateDeclaration())
{
e.error("`%s.opOpAssign` isn't a template", e.e1.toChars());
- result = ErrorExp.get();
- return;
+ return ErrorExp.get();
}
}
// Set tiargs, the template argument list, which will be the operator string
s = search_function(ad1, id);
if (s)
{
- // @@@DEPRECATED_2.098@@@.
- // Deprecated in 2.088
- // Make an error in 2.098
+ // @@@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 `=`
- e.deprecation("`%s` is deprecated. Use `opOpAssign(string op)(...) if (op == \"%s\")` instead.", id.toChars(), op.ptr);
+ e.error("`%s` is obsolete. Use `opOpAssign(string op)(...) if (op == \"%s\")` instead.", id.toChars(), op.ptr);
+ return ErrorExp.get();
}
}
* a.opOpAssign(b)
*/
args2.setDim(1);
- args2[0] = e.e2;
- expandTuples(&args2);
+ (*args2)[0] = e.e2;
+ expandTuples(args2);
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()))
{
- functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, &args2);
- if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
- {
- result = ErrorExp.get();
- return;
- }
+ return ErrorExp.get();
}
if (m.count > 1)
{
m.lastf = null;
}
// Rewrite (e1 op e2) as e1.opOpAssign(e2)
- result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
- return;
+ 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;
+ return result;
- result = checkAliasThisForRhs(isAggregate(e.e2.type), sc, e);
+ return checkAliasThisForRhs(isAggregate(e.e2.type), sc, e);
}
- }
if (pop)
*pop = e.op;
- scope OpOverload v = new OpOverload(sc, pop);
- e.accept(v);
- return v.result;
+
+ switch (e.op)
+ {
+ case EXP.cast_ : return visitCast(e.isCastExp());
+ case EXP.array : return visitArray(e.isArrayExp());
+
+ case EXP.notEqual :
+ case EXP.equal : return visitEqual(e.isEqualExp());
+
+ case EXP.lessOrEqual :
+ case EXP.greaterThan :
+ case EXP.greaterOrEqual:
+ case EXP.lessThan : return visitCmp(cast(CmpExp)e);
+
+ 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);
+ }
}
/******************************************
* b.opEquals(a)
* and see which is better.
*/
- Expressions args1 = Expressions(1);
- args1[0] = e.e1;
- expandTuples(&args1);
- Expressions args2 = Expressions(1);
- args2[0] = e.e2;
- expandTuples(&args2);
+ 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)
{
}
if (s)
{
- functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, &args2);
- if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
+ 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, &args1);
- if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
+ 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)
case Tclass:
case Tstruct:
{
- AggregateDeclaration ad = (tab.ty == Tclass) ? (cast(TypeClass)tab).sym
- : (cast(TypeStruct)tab).sym;
+ AggregateDeclaration ad = (tab.ty == Tclass) ? tab.isTypeClass().sym
+ : tab.isTypeStruct().sym;
if (!sliced)
{
sapply = search_function(ad, isForeach ? Id.apply : Id.applyReverse);
}
case Tdelegate: // https://dlang.org/spec/statement.html#foreach_over_delegates
- if (aggr.op == EXP.delegate_)
+ if (auto de = aggr.isDelegateExp())
{
- sapply = (cast(DelegateExp)aggr).func;
+ sapply = de.func;
}
break;
* Params:
* fes = the foreach statement
* sc = context
- * sapply = null or opApply or delegate
+ * sapply = null or opApply or delegate, overload resolution has not been done.
+ * Do overload resolution on sapply.
* Returns:
* false for errors
*/
bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply)
{
- if (!fes.parameters || !fes.parameters.dim)
+ if (!fes.parameters || !fes.parameters.length)
return false;
if (sapply) // prefer opApply
{
else
{
assert(tab.ty == Tdelegate && fes.aggr.op == EXP.delegate_);
- ethis = (cast(DelegateExp)fes.aggr).e1;
+ ethis = fes.aggr.isDelegateExp().e1;
}
/* Look for like an
*/
if (FuncDeclaration fd = sapply.isFuncDeclaration())
{
- auto fdapply = findBestOpApplyMatch(ethis, fd, fes.parameters);
- if (fdapply)
+ if (auto fdapply = findBestOpApplyMatch(ethis, fd, fes.parameters))
{
// Fill in any missing types on foreach parameters[]
- matchParamsToOpApply(cast(TypeFunction)fdapply.type, fes.parameters, true);
+ matchParamsToOpApply(fdapply.type.isTypeFunction(), fes.parameters, true);
sapply = fdapply;
return true;
}
return false;
}
- return sapply !is null;
+ return true; // shouldn't this be false?
}
Parameter p = (*fes.parameters)[0];
case Tarray:
case Tsarray:
case Ttuple:
- if (fes.parameters.dim == 2)
+ if (fes.parameters.length == 2)
{
if (!p.type)
{
case Taarray:
{
- TypeAArray taa = cast(TypeAArray)tab;
- if (fes.parameters.dim == 2)
+ TypeAArray taa = tab.isTypeAArray();
+ if (fes.parameters.length == 2)
{
if (!p.type)
{
case Tclass:
case Tstruct:
{
- AggregateDeclaration ad = (tab.ty == Tclass) ? (cast(TypeClass)tab).sym
- : (cast(TypeStruct)tab).sym;
- if (fes.parameters.dim == 1)
+ AggregateDeclaration ad = (tab.ty == Tclass) ? tab.isTypeClass().sym
+ : tab.isTypeStruct().sym;
+ if (fes.parameters.length == 1)
{
if (!p.type)
{
{
}
else if (s && s.isDeclaration())
- p.type = (cast(Declaration)s).type;
+ p.type = s.isDeclaration().type;
else
break;
}
}
case Tdelegate:
- if (!matchParamsToOpApply(cast(TypeFunction)tab.nextOf(), fes.parameters, true))
+ {
+ auto td = tab.isTypeDelegate();
+ if (!matchParamsToOpApply(td.next.isTypeFunction(), fes.parameters, true))
return false;
break;
+ }
default:
break; // ignore error, caught later
auto f = s.isFuncDeclaration();
if (!f)
return 0; // continue
- auto tf = cast(TypeFunction)f.type;
+ auto tf = f.type.isTypeFunction();
MATCH m = MATCH.exact;
if (f.isThis())
{
// Found another overload with different attributes?
// e.g. @system vs. @safe opApply
- bool ambig = tf.attributesEqual(bestTf);
+ // @@@DEPRECATED_2.112@@@
+ // See semantic2.d Semantic2Visitor.visit(FuncDeclaration):
+ // Remove `false` after deprecation period is over.
+ bool ambig = tf.attributesEqual(bestTf, false);
// opApplies with identical attributes could still accept
// different function bodies as delegate
/* Get the type of opApply's dg parameter
*/
Parameter p0 = tf.parameterList[0];
- if (p0.type.ty != Tdelegate)
+ auto de = p0.type.isTypeDelegate();
+ if (!de)
return nomatch;
- TypeFunction tdg = cast(TypeFunction)p0.type.nextOf();
- assert(tdg.ty == Tfunction);
+ TypeFunction tdg = de.next.isTypeFunction();
/* We now have tdg, the type of the delegate.
* tdg's parameters must match that of the foreach arglist (i.e. parameters).
* Fill in missing types in parameters.
*/
const nparams = tdg.parameterList.length;
- if (nparams == 0 || nparams != parameters.dim || tdg.parameterList.varargs != VarArg.none)
+ if (nparams == 0 || nparams != parameters.length || tdg.parameterList.varargs != VarArg.none)
return nomatch; // parameter mismatch
foreach (u, p; *parameters)
* Returns:
* reverse of op
*/
-private EXP reverseRelation(EXP op) pure
+private EXP reverseRelation(EXP op) pure @safe
{
switch (op)
{