@@ -60,6 +60,7 @@ import dmd.opover;
6060import dmd.optimize;
6161import dmd.parse;
6262import dmd.root.filename;
63+ import dmd.root.aav;
6364import dmd.root.array;
6465import dmd.common.outbuffer;
6566import dmd.root.rmem;
@@ -755,25 +756,10 @@ private extern(C++) final class Semantic3Visitor : Visitor
755756 */
756757 funcdecl.buildEnsureRequire();
757758
758- // Check for errors related to 'nothrow'.
759- const blockexit = funcdecl.fbody.blockExit(funcdecl, f.isNothrow ? global.errorSink : null );
760- if (f.isNothrow && blockexit & BE .throw_)
761- error(funcdecl.loc, " %s `%s` may throw but is marked as `nothrow`" , funcdecl.kind(), funcdecl.toPrettyChars());
762-
763- if (! (blockexit & (BE .throw_ | BE .halt) || funcdecl.hasCatches))
764- {
765- /* Don't generate unwind tables for this function
766- * https://issues.dlang.org/show_bug.cgi?id=17997
767- */
768- funcdecl.hasNoEH = true ;
769- }
770-
771- if (funcdecl.nothrowInprocess)
772- {
773- if (funcdecl.type == f)
774- f = cast (TypeFunction)f.copy();
775- f.isNothrow = ! (blockexit & BE .throw_);
776- }
759+ // Tentatively check the flow, ignoring errors.
760+ const oldErrors2 = global.startGagging();
761+ auto blockexit = funcdecl.fbody.blockExit(funcdecl, null );
762+ global.endGagging(oldErrors2);
777763
778764 if (funcdecl.fbody.isErrorStatement())
779765 {
@@ -834,6 +820,8 @@ private extern(C++) final class Semantic3Visitor : Visitor
834820 if (funcdecl.vresult || funcdecl.returnLabel)
835821 funcdecl.buildResultVar(scout ? scout : sc2, tret);
836822
823+ scope flr = new ForeachLambdaRewriter(funcdecl);
824+
837825 /* Cannot move this loop into NrvoWalker, because
838826 * returns[i] may be in the nested delegate for foreach-body.
839827 */
@@ -975,7 +963,16 @@ private extern(C++) final class Semantic3Visitor : Visitor
975963
976964 if (funcdecl.vresult)
977965 {
978- Scope* scret = rs.fesFunc ? rs.fesFunc._scope : sc2;
966+ Scope* scret = sc2;
967+
968+ if (rs.fesFunc)
969+ {
970+ flr.restartInference(rs.fesFunc);
971+
972+ // Borrow context pointer's scope for vresult.
973+ assert (rs.fesFunc.vthis);
974+ scret = rs.fesFunc.vthis._scope;
975+ }
979976
980977 // Create: return (vresult = exp, vresult);
981978 exp = new ConstructExp(rs.loc, funcdecl.vresult, exp);
@@ -991,6 +988,8 @@ private extern(C++) final class Semantic3Visitor : Visitor
991988 }
992989 rs.exp = exp;
993990 }
991+
992+ flr.finish();
994993 }
995994 if (funcdecl.nrvo_var || funcdecl.returnLabel)
996995 {
@@ -1000,6 +999,27 @@ private extern(C++) final class Semantic3Visitor : Visitor
1000999 nw.visitStmt(funcdecl.fbody);
10011000 }
10021001
1002+ // Definitively check for errors related to 'nothrow'.
1003+ blockexit = funcdecl.fbody.blockExit(funcdecl, f.isNothrow ? global.errorSink : null );
1004+
1005+ if (f.isNothrow && blockexit & BE .throw_)
1006+ error(funcdecl.loc, " %s `%s` may throw but is marked as `nothrow`" , funcdecl.kind(), funcdecl.toPrettyChars());
1007+
1008+ if (! (blockexit & (BE .throw_ | BE .halt) || funcdecl.hasCatches))
1009+ {
1010+ /* Don't generate unwind tables for this function
1011+ * https://issues.dlang.org/show_bug.cgi?id=17997
1012+ */
1013+ funcdecl.hasNoEH = true ;
1014+ }
1015+
1016+ if (funcdecl.nothrowInprocess)
1017+ {
1018+ if (funcdecl.type == f)
1019+ f = cast (TypeFunction)f.copy();
1020+ f.isNothrow = ! (blockexit & BE .throw_);
1021+ }
1022+
10031023 sc2 = sc2.pop();
10041024 }
10051025
@@ -1945,3 +1965,133 @@ private void findClosureVars(FuncDeclaration fd, scope void delegate(FuncDeclara
19451965 }
19461966 }
19471967}
1968+
1969+ /* Rewrites nested functions generated by foreach statements
1970+ * that contain returns. Fixes up attributes and runs semantic
1971+ * on lowered statements.
1972+ */
1973+ private final class ForeachLambdaRewriter
1974+ {
1975+ // Parent function
1976+ FuncDeclaration parent;
1977+
1978+ // Set of nested functions pending rewrite
1979+ AssocArray! (FuncDeclaration, bool ) lambdas;
1980+
1981+ public :
1982+ this (FuncDeclaration parent)
1983+ {
1984+ this .parent = parent;
1985+ }
1986+
1987+ void restartInference (FuncDeclaration fd)
1988+ {
1989+ auto tfp = parent.type.isTypeFunction();
1990+
1991+ for (; fd && fd.fes; fd = fd.toParent2().isFuncDeclaration())
1992+ {
1993+ auto tfl = fd.type.isTypeFunction();
1994+
1995+ if (tfp.purity == PURE .impure && tfl.purity > PURE .impure)
1996+ {
1997+ tfl.purity = PURE .impure;
1998+ fd.purityInprocess = true ;
1999+ }
2000+
2001+ if (tfp.trust != TRUST .safe && tfl.trust == TRUST .safe)
2002+ fd.safetyInprocess = true ;
2003+
2004+ if (! tfp.isNogc && tfl.isNogc)
2005+ fd.nogcInprocess = true ;
2006+
2007+ /* Always restart inference of nothrow because new exceptions
2008+ * can be introduced that are caught by parent function.
2009+ * However, parent catches should not change whether
2010+ * a nested function is nothrow.
2011+ */
2012+ if (tfl.isNothrow)
2013+ fd.nothrowInprocess = true ;
2014+
2015+ lambdas.getLvalue(fd);
2016+ }
2017+ }
2018+
2019+ void finish ()
2020+ {
2021+ Array! (FuncDeclarations* ) nestLevels;
2022+
2023+ /* At this point, AST post-order no longer corresponds to function
2024+ * nesting depth. Do a counting sort of nesting levels.
2025+ */
2026+ foreach (keyValue; lambdas.asRange())
2027+ {
2028+ size_t level = 0 ;
2029+ for (auto fd = keyValue.key; fd && fd.fes;
2030+ fd = fd.toParent2().isFuncDeclaration())
2031+ {
2032+ level++ ;
2033+ }
2034+
2035+ if (level > nestLevels.length)
2036+ {
2037+ size_t oldSize = nestLevels.length;
2038+ nestLevels.setDim(level);
2039+ foreach (size_t i; oldSize .. level)
2040+ nestLevels[i] = new FuncDeclarations();
2041+ }
2042+
2043+ nestLevels[level - 1 ].push(keyValue.key);
2044+ }
2045+
2046+ foreach_reverse (funcs; nestLevels[])
2047+ {
2048+ foreach (fd; (* funcs)[])
2049+ {
2050+ // Redo attribute inference, most nested first.
2051+ auto tf = fd.type.isTypeFunction();
2052+
2053+ if (fd.purityInprocess)
2054+ {
2055+ fd.purityInprocess = false ;
2056+ tf.purity = PURE .fwdref;
2057+ }
2058+
2059+ if (fd.safetyInprocess)
2060+ fd.safetyInprocess = false ;
2061+
2062+ if (fd.nogcInprocess)
2063+ fd.nogcInprocess = false ;
2064+
2065+ if (fd.nothrowInprocess)
2066+ {
2067+ auto blockexit = fd.fbody.blockExit(fd, null );
2068+
2069+ if (blockexit & (BE .throw_ | BE .halt))
2070+ fd.hasNoEH = false ;
2071+
2072+ fd.nothrowInprocess = false ;
2073+ tf.isNothrow = ! (blockexit & BE .throw_);
2074+ }
2075+
2076+ if (! fd.fes.lowering)
2077+ continue ;
2078+
2079+ // Run semantic on the calling point.
2080+ Expression* ec;
2081+ if (auto ss = fd.fes.lowering.isSwitchStatement())
2082+ ec = &ss.condition;
2083+ else if (auto es = fd.fes.lowering.isExpStatement())
2084+ if (auto ce = es.exp.isCastExp())
2085+ ec = &ce.e1;
2086+
2087+ assert (ec, " Unexpected lowering of foreach statement" );
2088+
2089+ ec.type = null ;
2090+ * ec = expressionSemantic(* ec, fd._scope);
2091+
2092+ if (! ec.isErrorExp() && ec.type != Type.tint32)
2093+ .error(fd.fes.loc, " `opApply()` function for `%s` must return an `int`" , fd.fes.aggr.type.toBasetype().toChars());
2094+ }
2095+ }
2096+ }
2097+ }
0 commit comments