]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/d/dmd/opover.d
d: Merge upstream dmd, druntime 4574d1728d, phobos d7e79f024.
[thirdparty/gcc.git] / gcc / d / dmd / opover.d
index 5d6128b014c98b559bc615ee64f5bc1adc3a4d91..457e8b6479c5c24fd035352b400ac809ab0955f5 100644 (file)
@@ -3,7 +3,7 @@
  *
  * 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)
@@ -32,6 +32,7 @@ import dmd.globals;
 import dmd.hdrgen;
 import dmd.id;
 import dmd.identifier;
+import dmd.location;
 import dmd.mtype;
 import dmd.statement;
 import dmd.tokens;
@@ -42,7 +43,7 @@ import dmd.visitor;
  * Determine if operands of binary op can be reversed
  * to fit operator overload.
  */
-bool isCommutative(EXP op)
+bool isCommutative(EXP op) @safe
 {
     switch (op)
     {
@@ -213,7 +214,7 @@ private Expression checkAliasThisForLhs(AggregateDeclaration ad, Scope* sc, BinE
      */
     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'
@@ -241,7 +242,7 @@ private Expression checkAliasThisForRhs(AggregateDeclaration ad, Scope* sc, BinE
      */
     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)
@@ -272,47 +273,32 @@ private Expression checkAliasThisForRhs(AggregateDeclaration ad, Scope* sc, BinE
  */
 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;
@@ -328,7 +314,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                         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)
                          */
@@ -342,8 +328,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                             result = result.expressionSemantic(sc);
                         if (result)
                         {
-                            result = Expression.combine(e0, result);
-                            return;
+                            return Expression.combine(e0, result);
                         }
                     }
                 Lfallback:
@@ -352,7 +337,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                         // 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)
                          */
@@ -367,10 +352,10 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                         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])
@@ -386,14 +371,18 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
             }
             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)()
@@ -405,7 +394,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                     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)
@@ -414,50 +403,50 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                     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;
@@ -475,14 +464,14 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                         {
                             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;
@@ -494,7 +483,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                     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)
                      */
@@ -507,8 +496,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                         result = result.expressionSemantic(sc);
                     if (result)
                     {
-                        result = Expression.combine(e0, result);
-                        return;
+                        return Expression.combine(e0, result);
                     }
                 }
             Lfallback:
@@ -517,7 +505,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                     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))
                 {
@@ -529,7 +517,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                         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)
@@ -544,10 +532,10 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                     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:
@@ -561,15 +549,17 @@ 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.
          */
-        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)
             {
@@ -586,8 +576,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                         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();
@@ -595,10 +584,10 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                     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)
@@ -607,20 +596,19 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                     {
                         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);
@@ -635,7 +623,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                      (sd.hasBlitAssign && !e.e2.isLvalue())))
                 {
                     /* This is bitwise struct assignment. */
-                    return;
+                    return null;
                 }
             }
             Dsymbol s = null;
@@ -645,7 +633,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = 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)
             {
@@ -657,8 +645,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                     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)
@@ -667,8 +654,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                     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;
@@ -689,13 +675,13 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = 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)
@@ -708,13 +694,15 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                         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:
@@ -723,30 +711,28 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                  * 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)
@@ -766,19 +752,18 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                     // 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)
@@ -808,30 +793,28 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                         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)
@@ -847,27 +830,26 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                         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
                      *
@@ -877,46 +859,45 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                      * 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();
@@ -929,7 +910,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
             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).
@@ -940,14 +921,13 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = 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.
@@ -961,6 +941,12 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                     /* 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;
 
@@ -973,26 +959,25 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                     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.
@@ -1008,27 +993,25 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                  * 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.
@@ -1042,49 +1025,32 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                  * 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.
@@ -1097,8 +1063,6 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                         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;
@@ -1112,41 +1076,40 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                 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;
@@ -1158,14 +1121,14 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                     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)
@@ -1181,20 +1144,19 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                             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)
@@ -1211,10 +1173,10 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                         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
@@ -1228,17 +1190,16 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                 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;
@@ -1250,8 +1211,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = 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
@@ -1267,12 +1227,12 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                 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();
                 }
             }
 
@@ -1282,17 +1242,13 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                  *      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)
                 {
@@ -1306,23 +1262,38 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                     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);
+    }
 }
 
 /******************************************
@@ -1353,12 +1324,12 @@ private Expression compare_overload(BinExp e, Scope* sc, Identifier id, EXP* pop
          *      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)
         {
@@ -1367,16 +1338,16 @@ private Expression compare_overload(BinExp e, Scope* sc, Identifier id, EXP* pop
         }
         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)
@@ -1505,8 +1476,8 @@ bool inferForeachAggregate(Scope* sc, bool isForeach, ref Expression feaggr, out
         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);
@@ -1547,9 +1518,9 @@ bool inferForeachAggregate(Scope* sc, bool isForeach, ref Expression feaggr, out
         }
 
         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;
 
@@ -1573,13 +1544,14 @@ bool inferForeachAggregate(Scope* sc, bool isForeach, ref Expression feaggr, out
  * 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
     {
@@ -1600,7 +1572,7 @@ bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply)
         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
@@ -1609,17 +1581,16 @@ bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply)
          */
         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];
@@ -1631,7 +1602,7 @@ bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply)
     case Tarray:
     case Tsarray:
     case Ttuple:
-        if (fes.parameters.dim == 2)
+        if (fes.parameters.length == 2)
         {
             if (!p.type)
             {
@@ -1649,8 +1620,8 @@ bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply)
 
     case Taarray:
         {
-            TypeAArray taa = cast(TypeAArray)tab;
-            if (fes.parameters.dim == 2)
+            TypeAArray taa = tab.isTypeAArray();
+            if (fes.parameters.length == 2)
             {
                 if (!p.type)
                 {
@@ -1672,9 +1643,9 @@ bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply)
     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)
             {
@@ -1697,7 +1668,7 @@ bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply)
                 {
                 }
                 else if (s && s.isDeclaration())
-                    p.type = (cast(Declaration)s).type;
+                    p.type = s.isDeclaration().type;
                 else
                     break;
             }
@@ -1707,9 +1678,12 @@ bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply)
     }
 
     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
@@ -1738,7 +1712,7 @@ private FuncDeclaration findBestOpApplyMatch(Expression ethis, FuncDeclaration f
         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())
         {
@@ -1763,7 +1737,10 @@ private FuncDeclaration findBestOpApplyMatch(Expression ethis, FuncDeclaration f
 
             // 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
@@ -1825,17 +1802,17 @@ private bool matchParamsToOpApply(TypeFunction tf, Parameters* parameters, bool
     /* 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)
@@ -1863,7 +1840,7 @@ private bool matchParamsToOpApply(TypeFunction tf, Parameters* parameters, bool
  * Returns:
  *      reverse of op
  */
-private EXP reverseRelation(EXP op) pure
+private EXP reverseRelation(EXP op) pure @safe
 {
     switch (op)
     {