2 * Does semantic analysis for statements.
4 * Specification: $(LINK2 https://dlang.org/spec/statement.html, Statements)
6 * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
7 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
8 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/statementsem.d, _statementsem.d)
10 * Documentation: https://dlang.org/phobos/dmd_statementsem.html
11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/statementsem.d
14 module dmd
.statementsem
;
16 import core
.stdc
.stdio
;
21 import dmd
.arraytypes
;
22 import dmd
.astcodegen
;
31 import dmd
.declaration
;
34 import dmd
.dinterpret
;
38 import dmd
.dsymbolsem
;
43 import dmd
.expression
;
44 import dmd
.expressionsem
;
50 import dmd
.identifier
;
60 import dmd
.common
.outbuffer
;
61 import dmd
.root
.string
;
63 import dmd
.sideeffect
;
65 import dmd
.staticassert
;
73 version = CallbackAPI
;
76 /*****************************************
77 * CTFE requires FuncDeclaration::labtab for the interpretation.
78 * So fixing the label name inside in/out contracts is necessary
79 * for the uniqueness in labtab.
82 * ident = statement label name to be adjusted
86 private Identifier
fixupLabelName(Scope
* sc
, Identifier ident
)
88 uint flags
= (sc
.flags
& SCOPE
.contract
);
89 const id
= ident
.toString();
90 if (flags
&& flags
!= SCOPE
.invariant_
&&
91 !(id
.length
>= 2 && id
[0] == '_' && id
[1] == '_')) // does not start with "__"
94 buf
.writestring(flags
== SCOPE
.require ?
"__in_" : "__out_");
95 buf
.writestring(ident
.toString());
97 ident
= Identifier
.idPool(buf
[]);
102 /*******************************************
103 * Check to see if statement is the innermost labeled statement.
106 * statement = Statement to check
108 * if `true`, then the `LabelStatement`, otherwise `null`
110 private LabelStatement
checkLabeledLoop(Scope
* sc
, Statement statement
) @safe
112 if (sc
.slabel
&& sc
.slabel
.statement
== statement
)
119 /***********************************************************
120 * Check an assignment is used as a condition.
121 * Intended to be use before the `semantic` call on `e`.
123 * e = condition expression which is not yet run semantic analysis.
127 private Expression
checkAssignmentAsCondition(Expression e
, Scope
* sc
)
129 if (sc
.flags
& SCOPE
.Cfile
)
131 auto ec
= lastComma(e
);
132 if (ec
.op
== EXP
.assign
)
134 error(ec
.loc
, "assignment cannot be used as a condition, perhaps `==` was meant?");
135 return ErrorExp
.get();
140 // Performs semantic analysis in Statement AST nodes
141 extern(C
++) Statement
statementSemantic(Statement s
, Scope
* sc
)
145 version (CallbackAPI
)
146 Compiler
.onStatementSemanticStart(s
, sc
);
148 Statement result
= statementSemanticVisit(s
, sc
);
150 version (CallbackAPI
)
151 Compiler
.onStatementSemanticDone(s
, sc
);
157 Statement
statementSemanticVisit(Statement s
, Scope
* sc
)
163 result
= new ErrorStatement();
166 void visitDefaultCase(Statement s
)
171 void visitError(ErrorStatement s
)
176 void visitPeel(PeelStatement s
)
178 /* "peel" off this wrapper, and don't run semantic()
184 void visitExp(ExpStatement s
)
186 /* https://dlang.org/spec/statement.html#expression-statement
194 //printf("ExpStatement::semantic() %s\n", exp.toChars());
196 // Allow CommaExp in ExpStatement because return isn't used
197 CommaExp
.allow(s
.exp
);
199 s
.exp
= s
.exp
.expressionSemantic(sc
);
200 s
.exp
= resolveProperties(sc
, s
.exp
);
201 s
.exp
= s
.exp
.addDtorHook(sc
);
202 if (checkNonAssignmentArrayOp(s
.exp
))
203 s
.exp
= ErrorExp
.get();
204 if (auto f
= isFuncAddress(s
.exp
))
206 if (f
.checkForwardRef(s
.exp
.loc
))
207 s
.exp
= ErrorExp
.get();
209 if (checkMustUse(s
.exp
, sc
))
210 s
.exp
= ErrorExp
.get();
211 if (!(sc
.flags
& SCOPE
.Cfile
) && discardValue(s
.exp
))
212 s
.exp
= ErrorExp
.get();
214 s
.exp
= s
.exp
.optimize(WANTvalue
);
215 s
.exp
= checkGC(sc
, s
.exp
);
216 if (s
.exp
.op
== EXP
.error
)
221 void visitDtorExp(DtorExpStatement s
)
226 void visitMixin(MixinStatement cs
)
228 /* https://dlang.org/spec/statement.html#mixin-statement
231 //printf("MixinStatement::semantic() %s\n", exp.toChars());
232 Statements
* a
= cs
.flatten(sc
);
235 Statement s
= new CompoundStatement(cs
.loc
, a
);
236 result
= s
.statementSemantic(sc
);
239 void visitCompound(CompoundStatement cs
)
241 //printf("CompoundStatement::semantic(this = %p, sc = %p)\n", cs, sc);
244 foreach (i
, s
; cs
.statements
)
247 printf("[%d]: %s", i
, s
.toChars());
251 for (size_t i
= 0; i
< cs
.statements
.length
;)
253 Statement s
= (*cs
.statements
)[i
];
260 if (auto flt
= s
.flatten(sc
))
262 cs
.statements
.remove(i
);
263 cs
.statements
.insert(i
, flt
);
266 s
= s
.statementSemantic(sc
);
267 (*cs
.statements
)[i
] = s
;
270 /* Remove NULL statements from the list.
272 cs
.statements
.remove(i
);
275 if (s
.isErrorStatement())
277 result
= s
; // propagate error up the AST
279 continue; // look for errors in rest of statements
282 // expand tuple variables in order to attach destruction/exception logic
283 if (auto es
= s
.isExpStatement())
285 if (es
.exp
&& es
.exp
.isDeclarationExp())
287 auto de = es
.exp
.isDeclarationExp();
288 auto vd
= de.declaration
.isVarDeclaration();
289 if (vd
&& vd
.aliasTuple
&& vd
.aliasTuple
.objects
.length
)
292 cs
.statements
.insert(i
, vd
.aliasTuple
.objects
.length
- 1, null);
293 vd
.aliasTuple
.foreachVar((v
) { (*cs
.statements
)[j
++] = toStatement(v
); });
294 s
= (*cs
.statements
)[i
];
300 Statement sexception
;
303 (*cs
.statements
)[i
] = s
.scopeCode(sc
, sentry
, sexception
, sfinally
);
306 sentry
= sentry
.statementSemantic(sc
);
307 cs
.statements
.insert(i
, sentry
);
311 sexception
= sexception
.statementSemantic(sc
);
314 /* Returns: true if statements[] are empty statements
316 static bool isEmpty(const Statement
[] statements
)
318 foreach (s
; statements
)
320 if (const cs
= s
.isCompoundStatement())
322 if (!isEmpty((*cs
.statements
)[]))
331 if (!sfinally
&& isEmpty((*cs
.statements
)[i
+ 1 .. cs
.statements
.length
]))
341 * catch (Throwable __o)
342 * { sexception; throw __o; }
344 auto a
= new Statements();
345 a
.pushSlice((*cs
.statements
)[i
+ 1 .. cs
.statements
.length
]);
346 cs
.statements
.setDim(i
+ 1);
348 Statement _body
= new CompoundStatement(Loc
.initial
, a
);
349 _body
= new ScopeStatement(Loc
.initial
, _body
, Loc
.initial
);
351 Identifier id
= Identifier
.generateId("__o");
353 Statement handler
= new PeelStatement(sexception
);
354 if (sexception
.blockExit(sc
.func
, null) & BE
.fallthru
)
356 auto ts
= new ThrowStatement(Loc
.initial
, new IdentifierExp(Loc
.initial
, id
));
357 ts
.internalThrow
= true;
358 handler
= new CompoundStatement(Loc
.initial
, handler
, ts
);
361 auto catches
= new Catches();
362 auto ctch
= new Catch(Loc
.initial
, getThrowable(), id
, handler
);
363 ctch
.internalCatch
= true;
366 Statement st
= new TryCatchStatement(Loc
.initial
, _body
, catches
);
368 st
= new TryFinallyStatement(Loc
.initial
, st
, sfinally
);
369 st
= st
.statementSemantic(sc
);
371 cs
.statements
.push(st
);
377 if (0 && i
+ 1 == cs
.statements
.length
)
379 cs
.statements
.push(sfinally
);
386 * s; try { s1; s2; } finally { sfinally; }
388 auto a
= new Statements();
389 a
.pushSlice((*cs
.statements
)[i
+ 1 .. cs
.statements
.length
]);
390 cs
.statements
.setDim(i
+ 1);
392 auto _body
= new CompoundStatement(Loc
.initial
, a
);
393 Statement stf
= new TryFinallyStatement(Loc
.initial
, _body
, sfinally
);
394 stf
= stf
.statementSemantic(sc
);
395 cs
.statements
.push(stf
);
402 /* Flatten them in place
404 void flattenStatements(ref Statements statements
)
406 for (size_t i
= 0; i
< statements
.length
;)
408 if (auto s
= statements
[i
])
410 if (auto flt
= s
.flatten(sc
))
412 statements
.remove(i
);
413 statements
.insert(i
, flt
);
421 /* https://issues.dlang.org/show_bug.cgi?id=11653
422 * 'semantic' may return another CompoundStatement
423 * (eg. CaseRangeStatement), so flatten it here.
425 flattenStatements(*cs
.statements
);
427 foreach (s
; *cs
.statements
)
432 if (auto se
= s
.isErrorStatement())
439 if (cs
.statements
.length
== 1)
441 result
= (*cs
.statements
)[0];
447 void visitUnrolledLoop(UnrolledLoopStatement uls
)
449 //printf("UnrolledLoopStatement::semantic(this = %p, sc = %p)\n", uls, sc);
450 Scope
* scd
= sc
.push();
454 Statement serror
= null;
455 foreach (i
, ref s
; *uls
.statements
)
459 //printf("[%d]: %s\n", i, s.toChars());
460 s
= s
.statementSemantic(scd
);
462 serror
= s
.isErrorStatement();
467 result
= serror ? serror
: uls
;
470 void visitScope(ScopeStatement ss
)
472 //printf("ScopeStatement::semantic(sc = %p)\n", sc);
479 ScopeDsymbol sym
= new ScopeDsymbol();
480 sym
.parent
= sc
.scopesym
;
481 sym
.endlinnum
= ss
.endloc
.linnum
;
484 Statements
* a
= ss
.statement
.flatten(sc
);
487 ss
.statement
= new CompoundStatement(ss
.loc
, a
);
490 ss
.statement
= ss
.statement
.statementSemantic(sc
);
493 if (ss
.statement
.isErrorStatement())
496 result
= ss
.statement
;
501 Statement sexception
;
503 ss
.statement
= ss
.statement
.scopeCode(sc
, sentry
, sexception
, sfinally
);
508 //printf("adding sfinally\n");
509 sfinally
= sfinally
.statementSemantic(sc
);
510 ss
.statement
= new CompoundStatement(ss
.loc
, ss
.statement
, sfinally
);
517 void visitForwarding(ForwardingStatement ss
)
520 for (Scope
* csc
= sc
; !ss
.sym
.parent
; csc
= csc
.enclosing
)
523 ss
.sym
.parent
= csc
.scopesym
;
525 sc
= sc
.push(ss
.sym
);
528 ss
.statement
= ss
.statement
.statementSemantic(sc
);
530 result
= ss
.statement
;
533 void visitWhile(WhileStatement ws
)
535 /* Rewrite as a for(;condition;) loop
536 * https://dlang.org/spec/statement.html#while-statement
538 Expression cond
= ws
.condition
;
539 Statement _body
= ws
._body
;
543 * If the while loop is of form `while(auto a = exp) { loop_body }`,
552 _body
= new IfStatement(ws
.loc
, ws
.param
, ws
.condition
, ws
._body
, new BreakStatement(ws
.loc
, null), ws
.endloc
);
553 cond
= IntegerExp
.createBool(true);
555 Statement s
= new ForStatement(ws
.loc
, null, cond
, null, _body
, ws
.endloc
);
556 s
= s
.statementSemantic(sc
);
560 void visitDo(DoStatement
ds)
562 /* https://dlang.org/spec/statement.html#do-statement
564 const inLoopSave
= sc
.inLoop
;
567 ds._body
= ds._body
.semanticScope(sc
, ds, ds, null);
568 sc
.inLoop
= inLoopSave
;
570 if (ds.condition
.op
== EXP
.dotIdentifier
)
571 (cast(DotIdExp
)ds.condition
).noderef
= true;
573 // check in syntax level
574 ds.condition
= checkAssignmentAsCondition(ds.condition
, sc
);
576 ds.condition
= ds.condition
.expressionSemantic(sc
);
577 ds.condition
= resolveProperties(sc
, ds.condition
);
578 if (checkNonAssignmentArrayOp(ds.condition
))
579 ds.condition
= ErrorExp
.get();
580 ds.condition
= ds.condition
.optimize(WANTvalue
);
581 ds.condition
= checkGC(sc
, ds.condition
);
583 ds.condition
= ds.condition
.toBoolean(sc
);
585 if (ds.condition
.op
== EXP
.error
)
587 if (ds._body
&& ds._body
.isErrorStatement())
596 void visitFor(ForStatement fs
)
598 /* https://dlang.org/spec/statement.html#for-statement
600 //printf("ForStatement::semantic %s\n", fs.toChars());
605 * for (auto v1 = i1, v2 = i2; condition; increment) { ... }
607 * { auto v1 = i1, v2 = i2; for (; condition; increment) { ... } }
613 * for (; condition; increment) { ... }
614 * } finally { v2.~this(); }
615 * } finally { v1.~this(); }
617 auto ainit
= new Statements();
618 ainit
.push(fs
._init
);
621 Statement s
= new CompoundStatement(fs
.loc
, ainit
);
622 s
= new ScopeStatement(fs
.loc
, s
, fs
.endloc
);
623 s
= s
.statementSemantic(sc
);
624 if (!s
.isErrorStatement())
626 if (LabelStatement ls
= checkLabeledLoop(sc
, fs
))
628 fs
.relatedLabeled
= s
;
633 assert(fs
._init
is null);
635 auto sym
= new ScopeDsymbol();
636 sym
.parent
= sc
.scopesym
;
637 sym
.endlinnum
= fs
.endloc
.linnum
;
643 if (fs
.condition
.op
== EXP
.dotIdentifier
)
644 (cast(DotIdExp
)fs
.condition
).noderef
= true;
646 // check in syntax level
647 fs
.condition
= checkAssignmentAsCondition(fs
.condition
, sc
);
649 fs
.condition
= fs
.condition
.expressionSemantic(sc
);
650 fs
.condition
= resolveProperties(sc
, fs
.condition
);
651 if (checkNonAssignmentArrayOp(fs
.condition
))
652 fs
.condition
= ErrorExp
.get();
653 fs
.condition
= fs
.condition
.optimize(WANTvalue
);
654 fs
.condition
= checkGC(sc
, fs
.condition
);
656 fs
.condition
= fs
.condition
.toBoolean(sc
);
660 CommaExp
.allow(fs
.increment
);
661 fs
.increment
= fs
.increment
.expressionSemantic(sc
);
662 fs
.increment
= resolveProperties(sc
, fs
.increment
);
663 // @@@DEPRECATED_2.112@@@
664 // remove gagging and deprecation() to turn deprecation into an error when
665 // deprecation cycle is over
666 const olderrors
= global
.startGagging();
667 discardValue(fs
.increment
);
668 if (global
.endGagging(olderrors
))
669 deprecation(fs
.increment
.loc
, "`%s` has no effect", fs
.increment
.toChars());
670 if (checkNonAssignmentArrayOp(fs
.increment
))
671 fs
.increment
= ErrorExp
.get();
672 fs
.increment
= fs
.increment
.optimize(WANTvalue
);
673 fs
.increment
= checkGC(sc
, fs
.increment
);
679 fs
._body
= fs
._body
.semanticNoScope(sc
);
683 if (fs
.condition
&& fs
.condition
.op
== EXP
.error ||
684 fs
.increment
&& fs
.increment
.op
== EXP
.error ||
685 fs
._body
&& fs
._body
.isErrorStatement())
690 void visitForeach(ForeachStatement fs
)
692 /* https://dlang.org/spec/statement.html#foreach-statement
695 //printf("ForeachStatement::semantic() %p\n", fs);
698 * Issue error if any of the ForeachTypes were not supplied and could not be inferred.
700 * true if error issued
702 static bool checkForArgTypes(ForeachStatement fs
)
705 foreach (p
; *fs
.parameters
)
709 error(fs
.loc
, "cannot infer type for `foreach` variable `%s`, perhaps set it explicitly", p
.ident
.toChars());
710 p
.type
= Type
.terror
;
718 const dim
= fs
.parameters
.length
;
722 fs
.func
= fs
.func
.fes
.func
;
724 VarDeclaration vinit
= null;
725 fs
.aggr
= fs
.aggr
.expressionSemantic(sc
);
726 fs
.aggr
= resolveProperties(sc
, fs
.aggr
);
727 fs
.aggr
= fs
.aggr
.optimize(WANTvalue
);
728 if (fs
.aggr
.op
== EXP
.error
)
730 Expression oaggr
= fs
.aggr
; // remember original for error messages
731 if (fs
.aggr
.type
&& fs
.aggr
.type
.toBasetype().ty
== Tstruct
&&
732 (cast(TypeStruct
)(fs
.aggr
.type
.toBasetype())).sym
.dtor
&&
733 !fs
.aggr
.isTypeExp() && !fs
.aggr
.isLvalue())
735 // https://issues.dlang.org/show_bug.cgi?id=14653
736 // Extend the life of rvalue aggregate till the end of foreach.
737 vinit
= copyToTemp(STC
.rvalue
, "__aggr", fs
.aggr
);
738 vinit
.endlinnum
= fs
.endloc
.linnum
;
739 vinit
.dsymbolSemantic(sc
);
740 fs
.aggr
= new VarExp(fs
.aggr
.loc
, vinit
);
743 /* If aggregate is a vector type, add the .array to make it a static array
746 if (auto tv
= fs
.aggr
.type
.toBasetype().isTypeVector())
748 auto vae
= new VectorArrayExp(fs
.aggr
.loc
, fs
.aggr
);
749 vae
.type
= tv
.basetype
;
753 Dsymbol sapply
= null; // the inferred opApply() or front() function
754 if (!inferForeachAggregate(sc
, fs
.op
== TOK
.foreach_
, fs
.aggr
, sapply
))
758 error(fs
.loc
, "invalid `%s` aggregate `%s` of type `%s`",
759 Token
.toChars(fs
.op
), oaggr
.toChars(), oaggr
.type
.toPrettyChars());
761 if (auto ad
= isAggregate(fs
.aggr
.type
))
763 if (fs
.op
== TOK
.foreach_reverse_
)
765 fs
.loc
.errorSupplemental("`foreach_reverse` works with bidirectional ranges"~
766 " (implementing `back` and `popBack`), aggregates implementing" ~
767 " `opApplyReverse`, or the result of an aggregate's `.tupleof` property");
768 fs
.loc
.errorSupplemental("https://dlang.org/phobos/std_range_primitives.html#isBidirectionalRange");
772 fs
.loc
.errorSupplemental("`foreach` works with input ranges"~
773 " (implementing `front` and `popFront`), aggregates implementing" ~
774 " `opApply`, or the result of an aggregate's `.tupleof` property");
775 fs
.loc
.errorSupplemental("https://dlang.org/phobos/std_range_primitives.html#isInputRange");
782 Dsymbol sapplyOld
= sapply
; // 'sapply' will be NULL if and after 'inferApplyArgTypes' errors
784 /* Check for inference errors
786 if (!inferApplyArgTypes(fs
, sc
, sapply
))
789 Try and extract the parameter count of the opApply callback function, e.g.:
790 int opApply(int delegate(int, float)) => 2 args
792 bool foundMismatch
= false;
793 size_t foreachParamCount
= 0;
796 if (FuncDeclaration fd
= sapplyOld
.isFuncDeclaration())
798 auto fparameters
= fd
.getParameterList();
800 if (fparameters
.length
== 1)
802 // first param should be the callback function
803 Parameter fparam
= fparameters
[0];
804 if ((fparam
.type
.ty
== Tpointer ||
805 fparam
.type
.ty
== Tdelegate
) &&
806 fparam
.type
.nextOf().ty
== Tfunction
)
808 TypeFunction tf
= cast(TypeFunction
)fparam
.type
.nextOf();
809 foreachParamCount
= tf
.parameterList
.length
;
810 foundMismatch
= true;
816 //printf("dim = %d, parameters.length = %d\n", dim, parameters.length);
817 if (foundMismatch
&& dim
!= foreachParamCount
)
819 const(char)* plural
= foreachParamCount
> 1 ?
"s" : "";
820 error(fs
.loc
, "cannot infer argument types, expected %llu argument%s, not %llu",
821 cast(ulong) foreachParamCount
, plural
, cast(ulong) dim
);
824 error(fs
.loc
, "cannot uniquely infer `foreach` argument types");
829 Type tab
= fs
.aggr
.type
.toBasetype();
831 if (tab
.ty
== Ttuple
) // don't generate new scope for tuple loops
833 Statement s
= makeTupleForeach(sc
, false, false, fs
, null, false).statement
;
835 s
= new CompoundStatement(loc
, new ExpStatement(loc
, vinit
), s
);
836 result
= s
.statementSemantic(sc
);
840 auto sym
= new ScopeDsymbol();
841 sym
.parent
= sc
.scopesym
;
842 sym
.endlinnum
= fs
.endloc
.linnum
;
843 auto sc2
= sc
.push(sym
);
846 foreach (Parameter p
; *fs
.parameters
)
848 if (p
.storageClass
& STC
.manifest
)
850 error(fs
.loc
, "cannot declare `enum` loop variables for non-unrolled foreach");
852 if (p
.storageClass
& STC
.alias_
)
854 error(fs
.loc
, "cannot declare `alias` loop variables for non-unrolled foreach");
861 result
= new ErrorStatement();
866 error(fs
.loc
, "cannot infer argument types");
870 void retStmt(Statement s
)
874 s
= s
.statementSemantic(sc2
);
883 if (checkForArgTypes(fs
))
886 TypeFunction tfld
= null;
889 if (auto fdapply
= sapply
.isFuncDeclaration())
891 assert(fdapply
.type
&& fdapply
.type
.isTypeFunction());
892 tfld
= fdapply
.type
.typeSemantic(loc
, sc2
).isTypeFunction();
895 else if (tab
.isTypeDelegate())
897 tfld
= tab
.nextOf().isTypeFunction();
899 //printf("tfld = %s\n", tfld.toChars());
900 if (tfld
.parameterList
.parameters
.length
== 1)
902 Parameter p
= tfld
.parameterList
[0];
903 if (p
.type
&& p
.type
.isTypeDelegate())
905 auto t
= p
.type
.typeSemantic(loc
, sc2
);
906 assert(t
.ty
== Tdelegate
);
907 tfld
= t
.nextOf().isTypeFunction();
913 FuncExp flde
= foreachBodyToFunction(sc2
, fs
, tfld
);
917 // Resolve any forward referenced goto's
918 foreach (ScopeStatement ss
; *fs
.gotos
)
920 GotoStatement gs
= ss
.statement
.isGotoStatement();
921 if (!gs
.label
.statement
)
923 // 'Promote' it to this scope, and replace with a return
925 ss
.statement
= new ReturnStatement(Loc
.initial
, new IntegerExp(fs
.cases
.length
+ 1));
932 e
= new DeclarationExp(loc
, vinit
);
933 e
= e
.expressionSemantic(sc2
);
934 if (e
.op
== EXP
.error
)
942 case Tsarray
: ec
= applyArray (fs
, flde
, tab
, sc2
, tn
, tnv
); break;
943 case Tdelegate
: ec
= applyDelegate (fs
, flde
, tab
, sc2
); break;
944 case Taarray
: ec
= applyAssocArray(fs
, flde
, tab
); break;
945 default: ec
= applyOpApply (fs
, flde
, tab
, sc2
, sapply
); break;
950 e
= Expression
.combine(e
, ec
);
951 return loopReturn(e
, fs
.cases
, loc
);
959 if (checkForArgTypes(fs
))
962 if (dim
< 1 || dim
> 2)
964 error(fs
.loc
, "only one or two arguments for array `foreach`");
968 // Finish semantic on all foreach parameter types.
969 foreach (i
; 0 .. dim
)
971 Parameter p
= (*fs
.parameters
)[i
];
972 p
.type
= p
.type
.typeSemantic(loc
, sc2
);
973 p
.type
= p
.type
.addStorageClass(p
.storageClass
);
976 tn
= tab
.nextOf().toBasetype();
980 Type tindex
= (*fs
.parameters
)[0].type
;
981 if (!tindex
.isintegral())
983 error(fs
.loc
, "foreach: key cannot be of non-integral type `%s`", tindex
.toChars());
986 /* What cases to deprecate implicit conversions for:
987 * 1. foreach aggregate is a dynamic array
988 * 2. foreach body is lowered to _aApply (see special case below).
990 Type tv
= (*fs
.parameters
)[1].type
.toBasetype();
991 if ((tab
.isTypeDArray() ||
992 (tn
.ty
!= tv
.ty
&& tn
.ty
.isSomeChar
&& tv
.ty
.isSomeChar
)) &&
993 !Type
.tsize_t
.implicitConvTo(tindex
))
995 deprecation(fs
.loc
, "foreach: loop index implicitly converted from `size_t` to `%s`",
1000 /* Look for special case of parsing char types out of char type
1003 if (tn
.ty
.isSomeChar
)
1005 int i
= (dim
== 1) ?
0 : 1; // index of value
1006 Parameter p
= (*fs
.parameters
)[i
];
1007 tnv
= p
.type
.toBasetype();
1008 if (tnv
.ty
!= tn
.ty
&& tnv
.ty
.isSomeChar
)
1010 if (p
.storageClass
& STC
.ref_
)
1012 error(fs
.loc
, "`foreach`: value of UTF conversion cannot be `ref`");
1017 p
= (*fs
.parameters
)[0];
1018 if (p
.storageClass
& STC
.ref_
)
1020 error(fs
.loc
, "`foreach`: key cannot be `ref`");
1024 return retStmt(apply());
1031 Parameter p
= (*fs
.parameters
)[0];
1032 fs
.key
= new VarDeclaration(loc
, p
.type
.mutableOf(), Identifier
.generateId("__key"), null);
1033 fs
.key
.storage_class |
= STC
.temp | STC
.foreach_
;
1034 if (fs
.key
.isReference())
1035 fs
.key
.storage_class |
= STC
.nodtor
;
1037 if (p
.storageClass
& STC
.ref_
)
1039 if (fs
.key
.type
.constConv(p
.type
) == MATCH
.nomatch
)
1041 error(fs
.loc
, "key type mismatch, `%s` to `ref %s`",
1042 fs
.key
.type
.toChars(), p
.type
.toChars());
1046 if (auto ta
= tab
.isTypeSArray())
1048 IntRange dimrange
= getIntRange(ta
.dim
);
1049 // https://issues.dlang.org/show_bug.cgi?id=12504
1050 dimrange
.imax
= SignExtendedNumber(dimrange
.imax
.value
-1);
1051 if (!IntRange
.fromType(fs
.key
.type
).contains(dimrange
))
1053 error(fs
.loc
, "index type `%s` cannot cover index range 0..%llu",
1054 p
.type
.toChars(), ta
.dim
.toInteger());
1057 fs
.key
.range
= new IntRange(SignExtendedNumber(0), dimrange
.imax
);
1060 // Now declare the value
1062 Parameter p
= (*fs
.parameters
)[dim
- 1];
1063 fs
.value
= new VarDeclaration(loc
, p
.type
, p
.ident
, null);
1064 fs
.value
.storage_class |
= STC
.foreach_
;
1065 fs
.value
.storage_class |
= p
.storageClass
& (STC
.scope_ | STC
.IOR | STC
.TYPECTOR
);
1066 if (fs
.value
.isReference())
1068 fs
.value
.storage_class |
= STC
.nodtor
;
1070 if (fs
.aggr
.checkModifiable(sc2
, ModifyFlags
.noError
) == Modifiable
.initialization
)
1071 fs
.value
.setInCtorOnly
= true;
1073 Type t
= tab
.nextOf();
1074 if (t
.constConv(p
.type
) == MATCH
.nomatch
)
1076 error(fs
.loc
, "argument type mismatch, `%s` to `ref %s`",
1077 t
.toChars(), p
.type
.toChars());
1083 /* Convert to a ForStatement
1084 * foreach (key, value; a) body =>
1085 * for (T[] tmp = a[], size_t key; key < tmp.length; ++key)
1086 * { T value = tmp[k]; body }
1088 * foreach_reverse (key, value; a) body =>
1089 * for (T[] tmp = a[], size_t key = tmp.length; key--; )
1090 * { T value = tmp[k]; body }
1092 auto id
= Identifier
.generateId("__r");
1093 auto ie
= new ExpInitializer(loc
, new SliceExp(loc
, fs
.aggr
, null, null));
1094 const valueIsRef
= (*fs
.parameters
)[$ - 1].isReference();
1096 if (fs
.aggr
.isArrayLiteralExp() && !valueIsRef
)
1098 auto ale
= fs
.aggr
.isArrayLiteralExp();
1099 size_t edim
= ale
.elements ? ale
.elements
.length
: 0;
1100 auto telem
= (*fs
.parameters
)[dim
- 1].type
;
1102 // https://issues.dlang.org/show_bug.cgi?id=12936
1103 // if telem has been specified explicitly,
1104 // converting array literal elements to telem might make it @nogc.
1105 fs
.aggr
= fs
.aggr
.implicitCastTo(sc
, telem
.sarrayOf(edim
));
1106 if (fs
.aggr
.op
== EXP
.error
)
1109 // for (T[edim] tmp = a, ...)
1110 tmp
= new VarDeclaration(loc
, fs
.aggr
.type
, id
, ie
);
1114 tmp
= new VarDeclaration(loc
, tab
.nextOf().arrayOf(), id
, ie
);
1116 tmp
.storage_class |
= STC
.scope_
;
1118 tmp
.storage_class |
= STC
.temp
;
1120 Expression tmp_length
= new DotIdExp(loc
, new VarExp(loc
, tmp
), Id
.length
);
1124 Identifier idkey
= Identifier
.generateId("__key");
1125 fs
.key
= new VarDeclaration(loc
, Type
.tsize_t
, idkey
, null);
1126 fs
.key
.storage_class |
= STC
.temp
;
1128 else if (fs
.key
.type
.ty
!= Type
.tsize_t
.ty
)
1130 tmp_length
= new CastExp(loc
, tmp_length
, fs
.key
.type
);
1132 if (fs
.op
== TOK
.foreach_reverse_
)
1133 fs
.key
._init
= new ExpInitializer(loc
, tmp_length
);
1135 fs
.key
._init
= new ExpInitializer(loc
, new IntegerExp(loc
, 0, fs
.key
.type
));
1137 auto cs
= new Statements();
1139 cs
.push(new ExpStatement(loc
, vinit
));
1140 cs
.push(new ExpStatement(loc
, tmp
));
1141 cs
.push(new ExpStatement(loc
, fs
.key
));
1142 Statement forinit
= new CompoundDeclarationStatement(loc
, cs
);
1145 if (fs
.op
== TOK
.foreach_reverse_
)
1148 cond
= new PostExp(EXP
.minusMinus
, loc
, new VarExp(loc
, fs
.key
));
1153 cond
= new CmpExp(EXP
.lessThan
, loc
, new VarExp(loc
, fs
.key
), tmp_length
);
1156 Expression increment
= null;
1157 if (fs
.op
== TOK
.foreach_
)
1160 increment
= new AddAssignExp(loc
, new VarExp(loc
, fs
.key
), new IntegerExp(loc
, 1, fs
.key
.type
));
1163 // T value = tmp[key];
1164 IndexExp indexExp
= new IndexExp(loc
, new VarExp(loc
, tmp
), new VarExp(loc
, fs
.key
));
1165 indexExp
.indexIsInBounds
= true; // disabling bounds checking in foreach statements.
1166 fs
.value
._init
= new ExpInitializer(loc
, indexExp
);
1167 Statement
ds = new ExpStatement(loc
, fs
.value
);
1171 Parameter p
= (*fs
.parameters
)[0];
1172 if ((p
.storageClass
& STC
.ref_
) && p
.type
.equals(fs
.key
.type
))
1174 fs
.key
.range
= null;
1175 auto v
= new AliasDeclaration(loc
, p
.ident
, fs
.key
);
1176 fs
._body
= new CompoundStatement(loc
, new ExpStatement(loc
, v
), fs
._body
);
1180 auto ei
= new ExpInitializer(loc
, new IdentifierExp(loc
, fs
.key
.ident
));
1181 auto v
= new VarDeclaration(loc
, p
.type
, p
.ident
, ei
);
1182 v
.storage_class |
= STC
.foreach_ |
(p
.storageClass
& STC
.ref_
);
1183 fs
._body
= new CompoundStatement(loc
, new ExpStatement(loc
, v
), fs
._body
);
1184 if (fs
.key
.range
&& !p
.type
.isMutable())
1186 /* Limit the range of the key to the specified range
1188 v
.range
= new IntRange(fs
.key
.range
.imin
, fs
.key
.range
.imax
- SignExtendedNumber(1));
1192 fs
._body
= new CompoundStatement(loc
, ds, fs
._body
);
1194 Statement s
= new ForStatement(loc
, forinit
, cond
, increment
, fs
._body
, fs
.endloc
);
1195 if (auto ls
= checkLabeledLoop(sc
, fs
)) // https://issues.dlang.org/show_bug.cgi?id=15450
1201 if (fs
.op
== TOK
.foreach_reverse_
)
1202 warning(fs
.loc
, "cannot use `foreach_reverse` with an associative array");
1203 if (checkForArgTypes(fs
))
1206 if (dim
< 1 || dim
> 2)
1208 error(fs
.loc
, "only one or two arguments for associative array `foreach`");
1211 return retStmt(apply());
1215 /* Prefer using opApply, if it exists
1218 return retStmt(apply());
1220 /* Look for range iteration, i.e. the properties
1221 * .empty, .popFront, .popBack, .front and .back
1222 * foreach (e; aggr) { ... }
1224 * for (auto __r = aggr[]; !__r.empty; __r.popFront()) {
1225 * auto e = __r.front;
1229 auto ad
= (tab
.ty
== Tclass
) ?
1230 cast(AggregateDeclaration
)tab
.isTypeClass().sym
:
1231 cast(AggregateDeclaration
)tab
.isTypeStruct().sym
;
1233 Identifier idpopFront
;
1234 if (fs
.op
== TOK
.foreach_
)
1236 idfront
= Id
.Ffront
;
1237 idpopFront
= Id
.FpopFront
;
1242 idpopFront
= Id
.FpopBack
;
1244 auto sfront
= ad
.search(Loc
.initial
, idfront
);
1246 return retStmt(apply());
1248 /* Generate a temporary __r and initialize it with the aggregate.
1252 if (vinit
&& fs
.aggr
.isVarExp() && fs
.aggr
.isVarExp().var
== vinit
)
1255 _init
= new ExpStatement(loc
, vinit
);
1259 r
= copyToTemp(0, "__r", fs
.aggr
);
1260 r
.dsymbolSemantic(sc
);
1261 _init
= new ExpStatement(loc
, r
);
1263 _init
= new CompoundStatement(loc
, new ExpStatement(loc
, vinit
), _init
);
1267 Expression e
= new VarExp(loc
, r
);
1268 e
= new DotIdExp(loc
, e
, Id
.Fempty
);
1269 Expression condition
= new NotExp(loc
, e
);
1272 e
= new VarExp(loc
, r
);
1273 Expression increment
= new CallExp(loc
, new DotIdExp(loc
, e
, idpopFront
));
1275 /* Declaration statement for e:
1276 * auto e = __r.idfront;
1278 e
= new VarExp(loc
, r
);
1279 Expression einit
= new DotIdExp(loc
, e
, idfront
);
1280 Statement makeargs
, forbody
;
1281 bool ignoreRef
= false; // If a range returns a non-ref front we ignore ref on foreach
1284 if (auto fd
= sfront
.isFuncDeclaration())
1286 if (!fd
.functionSemantic())
1287 return rangeError();
1290 else if (auto td
= sfront
.isTemplateDeclaration())
1292 if (auto f
= resolveFuncCall(loc
, sc
, td
, null, tab
, ArgumentList(), FuncResolveFlag
.quiet
))
1295 else if (auto d
= sfront
.toAlias().isDeclaration())
1299 if (!tfront || tfront
.ty
== Terror
)
1300 return rangeError();
1301 if (auto ftt
= tfront
.toBasetype().isTypeFunction())
1303 tfront
= tfront
.toBasetype().nextOf();
1306 // .front() does not return a ref. We ignore ref on foreach arg.
1307 // see https://issues.dlang.org/show_bug.cgi?id=11934
1308 if (tfront
.needsDestruction()) ignoreRef
= true;
1311 if (tfront
.ty
== Tvoid
)
1313 error(fs
.loc
, "`%s.front` is `void` and has no value", oaggr
.toChars());
1319 auto p
= (*fs
.parameters
)[0];
1320 auto ve
= new VarDeclaration(loc
, p
.type
, p
.ident
, new ExpInitializer(loc
, einit
));
1321 ve
.storage_class |
= STC
.foreach_
;
1322 ve
.storage_class |
= p
.storageClass
& (STC
.scope_ | STC
.IOR | STC
.TYPECTOR
);
1325 ve
.storage_class
&= ~STC
.ref_
;
1327 makeargs
= new ExpStatement(loc
, ve
);
1331 auto vd
= copyToTemp(STC
.ref_
, "__front", einit
);
1332 vd
.dsymbolSemantic(sc
);
1333 makeargs
= new ExpStatement(loc
, vd
);
1335 // Resolve inout qualifier of front type
1336 tfront
= tfront
.substWildTo(tab
.mod
);
1338 Expression ve
= new VarExp(loc
, vd
);
1341 auto exps
= new Expressions();
1344 while (exps
.length
< dim
)
1346 pos
= expandAliasThisTuples(exps
, pos
);
1350 if (exps
.length
!= dim
)
1352 const(char)* plural
= exps
.length
> 1 ?
"s" : "";
1353 error(fs
.loc
, "cannot infer argument types, expected %llu argument%s, not %llu",
1354 cast(ulong) exps
.length
, plural
, cast(ulong) dim
);
1358 foreach (i
; 0 .. dim
)
1360 auto p
= (*fs
.parameters
)[i
];
1361 auto exp
= (*exps
)[i
];
1364 printf("[%lu] p = %s %s, exp = %s %s\n", i
,
1365 p
.type ? p
.type
.toChars() : "?", p
.ident
.toChars(),
1366 exp
.type
.toChars(), exp
.toChars());
1371 auto sc
= p
.storageClass
;
1372 if (ignoreRef
) sc
&= ~STC
.ref_
;
1373 p
.type
= p
.type
.addStorageClass(sc
).typeSemantic(loc
, sc2
);
1374 if (!exp
.implicitConvTo(p
.type
))
1376 error(fs
.loc
, "cannot implicilty convert range element of type `%s` to variable `%s` of type `%s`",
1377 exp
.type
.toChars(), p
.toChars(), p
.type
.toChars());
1381 auto var
= new VarDeclaration(loc
, p
.type
, p
.ident
, new ExpInitializer(loc
, exp
));
1382 var
.storage_class |
= STC
.ctfe | STC
.ref_ | STC
.foreach_
;
1383 makeargs
= new CompoundStatement(loc
, makeargs
, new ExpStatement(loc
, var
));
1387 forbody
= new CompoundStatement(loc
, makeargs
, fs
._body
);
1389 Statement s
= new ForStatement(loc
, _init
, condition
, increment
, forbody
, fs
.endloc
);
1390 if (auto ls
= checkLabeledLoop(sc
, fs
))
1395 printf("init: %s\n", toChars(_init
));
1396 printf("condition: %s\n", condition
.toChars());
1397 printf("increment: %s\n", increment
.toChars());
1398 printf("body: %s\n", forbody
.toChars());
1403 if (fs
.op
== TOK
.foreach_reverse_
)
1404 deprecation(fs
.loc
, "cannot use `foreach_reverse` with a delegate");
1405 return retStmt(apply());
1409 error(fs
.loc
, "`foreach`: `%s` is not an aggregate type", fs
.aggr
.type
.toChars());
1414 void visitForeachRange(ForeachRangeStatement fs
)
1416 /* https://dlang.org/spec/statement.html#foreach-range-statement
1419 //printf("ForeachRangeStatement::semantic() %p\n", fs);
1421 fs
.lwr
= fs
.lwr
.expressionSemantic(sc
);
1422 fs
.lwr
= resolveProperties(sc
, fs
.lwr
);
1423 fs
.lwr
= fs
.lwr
.optimize(WANTvalue
);
1426 error(fs
.loc
, "invalid range lower bound `%s`", fs
.lwr
.toChars());
1430 fs
.upr
= fs
.upr
.expressionSemantic(sc
);
1431 fs
.upr
= resolveProperties(sc
, fs
.upr
);
1432 fs
.upr
= fs
.upr
.optimize(WANTvalue
);
1435 error(fs
.loc
, "invalid range upper bound `%s`", fs
.upr
.toChars());
1441 fs
.prm
.type
= fs
.prm
.type
.typeSemantic(loc
, sc
);
1442 fs
.prm
.type
= fs
.prm
.type
.addStorageClass(fs
.prm
.storageClass
);
1443 fs
.lwr
= fs
.lwr
.implicitCastTo(sc
, fs
.prm
.type
);
1445 if (fs
.upr
.implicitConvTo(fs
.prm
.type
) ||
(fs
.prm
.storageClass
& STC
.ref_
))
1447 fs
.upr
= fs
.upr
.implicitCastTo(sc
, fs
.prm
.type
);
1451 // See if upr-1 fits in prm.type
1452 Expression limit
= new MinExp(loc
, fs
.upr
, IntegerExp
.literal
!1);
1453 limit
= limit
.expressionSemantic(sc
);
1454 limit
= limit
.optimize(WANTvalue
);
1455 if (!limit
.implicitConvTo(fs
.prm
.type
))
1457 fs
.upr
= fs
.upr
.implicitCastTo(sc
, fs
.prm
.type
);
1463 /* Must infer types from lwr and upr
1465 Type tlwr
= fs
.lwr
.type
.toBasetype();
1466 if (tlwr
.ty
== Tstruct || tlwr
.ty
== Tclass
)
1468 /* Just picking the first really isn't good enough.
1470 fs
.prm
.type
= fs
.lwr
.type
;
1472 else if (fs
.lwr
.type
== fs
.upr
.type
)
1474 /* Same logic as CondExp ?lwr:upr
1476 fs
.prm
.type
= fs
.lwr
.type
;
1480 scope AddExp ea
= new AddExp(loc
, fs
.lwr
, fs
.upr
);
1481 if (typeCombine(ea
, sc
))
1483 fs
.prm
.type
= ea
.type
;
1487 fs
.prm
.type
= fs
.prm
.type
.addStorageClass(fs
.prm
.storageClass
);
1489 if (fs
.prm
.type
.ty
== Terror || fs
.lwr
.op
== EXP
.error || fs
.upr
.op
== EXP
.error
)
1494 /* Convert to a for loop:
1495 * foreach (key; lwr .. upr) =>
1496 * for (auto key = lwr, auto tmp = upr; key < tmp; ++key)
1498 * foreach_reverse (key; lwr .. upr) =>
1499 * for (auto tmp = lwr, auto key = upr; key-- > tmp;)
1501 auto ie
= new ExpInitializer(loc
, (fs
.op
== TOK
.foreach_
) ? fs
.lwr
: fs
.upr
);
1502 fs
.key
= new VarDeclaration(loc
, fs
.upr
.type
.mutableOf(), Identifier
.generateId("__key"), ie
);
1503 fs
.key
.storage_class |
= STC
.temp
;
1504 SignExtendedNumber lower
= getIntRange(fs
.lwr
).imin
;
1505 SignExtendedNumber upper
= getIntRange(fs
.upr
).imax
;
1508 fs
.key
.range
= new IntRange(lower
, upper
);
1511 Identifier id
= Identifier
.generateId("__limit");
1512 ie
= new ExpInitializer(loc
, (fs
.op
== TOK
.foreach_
) ? fs
.upr
: fs
.lwr
);
1513 auto tmp
= new VarDeclaration(loc
, fs
.upr
.type
, id
, ie
);
1514 tmp
.storage_class |
= STC
.temp
;
1516 auto cs
= new Statements();
1517 // Keep order of evaluation as lwr, then upr
1518 if (fs
.op
== TOK
.foreach_
)
1520 cs
.push(new ExpStatement(loc
, fs
.key
));
1521 cs
.push(new ExpStatement(loc
, tmp
));
1525 cs
.push(new ExpStatement(loc
, tmp
));
1526 cs
.push(new ExpStatement(loc
, fs
.key
));
1528 Statement forinit
= new CompoundDeclarationStatement(loc
, cs
);
1531 if (fs
.op
== TOK
.foreach_reverse_
)
1533 cond
= new PostExp(EXP
.minusMinus
, loc
, new VarExp(loc
, fs
.key
));
1534 if (fs
.prm
.type
.isscalar())
1537 cond
= new CmpExp(EXP
.greaterThan
, loc
, cond
, new VarExp(loc
, tmp
));
1542 cond
= new EqualExp(EXP
.notEqual
, loc
, cond
, new VarExp(loc
, tmp
));
1547 if (fs
.prm
.type
.isscalar())
1550 cond
= new CmpExp(EXP
.lessThan
, loc
, new VarExp(loc
, fs
.key
), new VarExp(loc
, tmp
));
1555 cond
= new EqualExp(EXP
.notEqual
, loc
, new VarExp(loc
, fs
.key
), new VarExp(loc
, tmp
));
1559 Expression increment
= null;
1560 if (fs
.op
== TOK
.foreach_
)
1563 //increment = new AddAssignExp(loc, new VarExp(loc, fs.key), IntegerExp.literal!1);
1564 increment
= new PreExp(EXP
.prePlusPlus
, loc
, new VarExp(loc
, fs
.key
));
1566 if ((fs
.prm
.storageClass
& STC
.ref_
) && fs
.prm
.type
.equals(fs
.key
.type
))
1568 fs
.key
.range
= null;
1569 auto v
= new AliasDeclaration(loc
, fs
.prm
.ident
, fs
.key
);
1570 fs
._body
= new CompoundStatement(loc
, new ExpStatement(loc
, v
), fs
._body
);
1574 ie
= new ExpInitializer(loc
, new CastExp(loc
, new VarExp(loc
, fs
.key
), fs
.prm
.type
));
1575 auto v
= new VarDeclaration(loc
, fs
.prm
.type
, fs
.prm
.ident
, ie
);
1576 v
.storage_class |
= STC
.temp | STC
.foreach_ |
(fs
.prm
.storageClass
& STC
.ref_
);
1577 fs
._body
= new CompoundStatement(loc
, new ExpStatement(loc
, v
), fs
._body
);
1578 if (fs
.key
.range
&& !fs
.prm
.type
.isMutable())
1580 /* Limit the range of the key to the specified range
1582 v
.range
= new IntRange(fs
.key
.range
.imin
, fs
.key
.range
.imax
- SignExtendedNumber(1));
1585 if (fs
.prm
.storageClass
& STC
.ref_
)
1587 if (fs
.key
.type
.constConv(fs
.prm
.type
) == MATCH
.nomatch
)
1589 error(fs
.loc
, "argument type mismatch, `%s` to `ref %s`", fs
.key
.type
.toChars(), fs
.prm
.type
.toChars());
1594 auto s
= new ForStatement(loc
, forinit
, cond
, increment
, fs
._body
, fs
.endloc
);
1595 if (LabelStatement ls
= checkLabeledLoop(sc
, fs
))
1597 result
= s
.statementSemantic(sc
);
1600 void visitIf(IfStatement ifs
)
1602 /* https://dlang.org/spec/statement.html#IfStatement
1605 // check in syntax level
1606 ifs
.condition
= checkAssignmentAsCondition(ifs
.condition
, sc
);
1608 auto sym
= new ScopeDsymbol();
1609 sym
.parent
= sc
.scopesym
;
1610 sym
.endlinnum
= ifs
.endloc
.linnum
;
1611 Scope
* scd
= sc
.push(sym
);
1614 /* Declare prm, which we will set to be the
1615 * result of condition.
1617 auto ei
= new ExpInitializer(ifs
.loc
, ifs
.condition
);
1618 ifs
.match
= new VarDeclaration(ifs
.loc
, ifs
.prm
.type
, ifs
.prm
.ident
, ei
);
1619 ifs
.match
.parent
= scd
.func
;
1620 ifs
.match
.storage_class |
= ifs
.prm
.storageClass
;
1621 ifs
.match
.dsymbolSemantic(scd
);
1623 auto de = new DeclarationExp(ifs
.loc
, ifs
.match
);
1624 auto ve
= new VarExp(ifs
.loc
, ifs
.match
);
1625 ifs
.condition
= new CommaExp(ifs
.loc
, de, ve
);
1626 ifs
.condition
= ifs
.condition
.expressionSemantic(scd
);
1628 if (ifs
.match
.edtor
)
1630 Statement sdtor
= new DtorExpStatement(ifs
.loc
, ifs
.match
.edtor
, ifs
.match
);
1631 sdtor
= new ScopeGuardStatement(ifs
.loc
, TOK
.onScopeExit
, sdtor
);
1632 ifs
.ifbody
= new CompoundStatement(ifs
.loc
, sdtor
, ifs
.ifbody
);
1633 ifs
.match
.storage_class |
= STC
.nodtor
;
1635 // the destructor is always called
1636 // whether the 'ifbody' is executed or not
1637 Statement sdtor2
= new DtorExpStatement(ifs
.loc
, ifs
.match
.edtor
, ifs
.match
);
1639 ifs
.elsebody
= new CompoundStatement(ifs
.loc
, sdtor2
, ifs
.elsebody
);
1641 ifs
.elsebody
= sdtor2
;
1646 if (ifs
.condition
.op
== EXP
.dotIdentifier
)
1647 (cast(DotIdExp
)ifs
.condition
).noderef
= true;
1649 ifs
.condition
= ifs
.condition
.expressionSemantic(scd
);
1650 ifs
.condition
= resolveProperties(scd
, ifs
.condition
);
1651 ifs
.condition
= ifs
.condition
.addDtorHook(scd
);
1653 if (checkNonAssignmentArrayOp(ifs
.condition
))
1654 ifs
.condition
= ErrorExp
.get();
1656 // Convert to boolean after declaring prm so this works:
1657 // if (S prm = S()) {}
1658 // where S is a struct that defines opCast!bool.
1659 ifs
.condition
= ifs
.condition
.toBoolean(scd
);
1661 // If we can short-circuit evaluate the if statement, don't do the
1662 // semantic analysis of the skipped code.
1663 // This feature allows a limited form of conditional compilation.
1664 ifs
.condition
= ifs
.condition
.optimize(WANTvalue
);
1666 // checkGC after optimizing the condition so that
1667 // compile time constants are reduced.
1668 ifs
.condition
= checkGC(scd
, ifs
.condition
);
1670 // Save 'root' of two branches (then and else) at the point where it forks
1671 CtorFlow ctorflow_root
= scd
.ctorflow
.clone();
1673 /* Rewrite `if (!__ctfe) A else B` as `if (__ctfe) B else A`
1677 (notExp
= ifs
.condition
.isNotExp()) !is null &&
1678 notExp
.e1
.isVarExp() &&
1679 notExp
.e1
.isVarExp().var
.ident
== Id
.ctfe
)
1681 ifs
.condition
= notExp
.e1
;
1682 auto sbody
= ifs
.ifbody
;
1683 ifs
.ifbody
= ifs
.elsebody
;
1684 ifs
.elsebody
= sbody
;
1687 /* Detect `if (__ctfe)`
1689 if (ifs
.isIfCtfeBlock())
1691 Scope
* scd2
= scd
.push();
1692 scd2
.flags |
= SCOPE
.ctfeBlock
;
1693 ifs
.ifbody
= ifs
.ifbody
.semanticNoScope(scd2
);
1697 ifs
.ifbody
= ifs
.ifbody
.semanticNoScope(scd
);
1700 CtorFlow ctorflow_then
= sc
.ctorflow
; // move flow results
1701 sc
.ctorflow
= ctorflow_root
; // reset flow analysis back to root
1703 ifs
.elsebody
= ifs
.elsebody
.semanticScope(sc
, null, null, null);
1705 // Merge 'then' results into 'else' results
1706 sc
.merge(ifs
.loc
, ctorflow_then
);
1708 ctorflow_then
.freeFieldinit(); // free extra copy of the data
1710 if (ifs
.condition
.op
== EXP
.error ||
1711 (ifs
.ifbody
&& ifs
.ifbody
.isErrorStatement()) ||
1712 (ifs
.elsebody
&& ifs
.elsebody
.isErrorStatement()))
1719 void visitConditional(ConditionalStatement cs
)
1721 //printf("ConditionalStatement::semantic()\n");
1723 // If we can short-circuit evaluate the if statement, don't do the
1724 // semantic analysis of the skipped code.
1725 // This feature allows a limited form of conditional compilation.
1726 if (cs
.condition
.include(sc
))
1728 DebugCondition dc
= cs
.condition
.isDebugCondition();
1732 sc
.flags |
= SCOPE
.debug_
;
1733 cs
.ifbody
= cs
.ifbody
.statementSemantic(sc
);
1737 cs
.ifbody
= cs
.ifbody
.statementSemantic(sc
);
1743 cs
.elsebody
= cs
.elsebody
.statementSemantic(sc
);
1744 result
= cs
.elsebody
;
1748 void visitPragma(PragmaStatement ps
)
1750 /* https://dlang.org/spec/statement.html#pragma-statement
1752 // Should be merged with PragmaDeclaration
1754 //printf("PragmaStatement::semantic() %s\n", ps.toChars());
1755 //printf("body = %p\n", ps._body);
1756 if (ps
.ident
== Id
.msg
)
1758 if (!pragmaMsgSemantic(ps
.loc
, sc
, ps
.args
))
1761 else if (ps
.ident
== Id
.lib
)
1765 /* Should this be allowed?
1767 error(ps
.loc
, "`pragma(lib)` not allowed as statement");
1772 if (!ps
.args || ps
.args
.length
!= 1)
1774 error(ps
.loc
, "`string` expected for library name");
1779 auto se
= semanticString(sc
, (*ps
.args
)[0], "library name");
1783 if (global
.params
.v
.verbose
)
1785 message("library %.*s", cast(int)se
.len
, se
.string
);
1790 else if (ps
.ident
== Id
.linkerDirective
)
1792 /* Should this be allowed?
1794 error(ps
.loc
, "`pragma(linkerDirective)` not allowed as statement");
1797 else if (ps
.ident
== Id
.startaddress
)
1799 if (!pragmaStartAddressSemantic(ps
.loc
, sc
, ps
.args
))
1802 else if (ps
.ident
== Id
.Pinline
)
1804 if (auto fd
= sc
.func
)
1806 fd
.inlining
= evalPragmaInline(ps
.loc
, sc
, ps
.args
);
1810 error(ps
.loc
, "`pragma(inline)` is not inside a function");
1814 else if (ps
.ident
== Id
.mangle
)
1816 auto es
= ps
._body ? ps
._body
.isExpStatement() : null;
1817 auto de = es ? es
.exp
.isDeclarationExp() : null;
1820 error(ps
.loc
, "`pragma(mangle)` must be attached to a declaration");
1823 const se
= ps
.args
&& (*ps
.args
).length
== 1 ?
semanticString(sc
, (*ps
.args
)[0], "pragma mangle argument") : null;
1826 error(ps
.loc
, "`pragma(mangle)` takes a single argument that must be a string literal");
1829 const cnt
= setMangleOverride(de.declaration
, cast(const(char)[])se
.peekData());
1833 else if (!global
.params
.ignoreUnsupportedPragmas
)
1835 error(ps
.loc
, "unrecognized `pragma(%s)`", ps
.ident
.toChars());
1841 if (ps
.ident
== Id
.msg || ps
.ident
== Id
.startaddress
)
1843 error(ps
.loc
, "`pragma(%s)` is missing a terminating `;`", ps
.ident
.toChars());
1846 ps
._body
= ps
._body
.statementSemantic(sc
);
1851 void visitStaticAssert(StaticAssertStatement s
)
1858 void visitSwitch(SwitchStatement ss
)
1860 /* https://dlang.org/spec/statement.html#switch-statement
1863 //printf("SwitchStatement::semantic(%p)\n", ss);
1864 ss
.tryBody
= sc
.tryBody
;
1868 result
= ss
; // already run
1875 * If the switch statement is of form `switch(auto a = exp) { body }`,
1876 * rewrite to the following inside it's own scope:
1882 auto statements
= new Statements();
1883 auto vardecl
= new VarDeclaration(ss
.param
.loc
,
1886 new ExpInitializer(ss
.condition
.loc
, ss
.condition
),
1887 ss
.param
.storageClass
);
1889 statements
.push(new ExpStatement(ss
.param
.loc
, vardecl
));
1891 ss
.condition
= new VarExp(ss
.param
.loc
, vardecl
, false);
1894 statements
.push(ss
);
1896 Statement s
= new CompoundStatement(ss
.loc
, statements
);
1897 s
= new ScopeStatement(ss
.loc
, s
, ss
.endloc
);
1898 s
= s
.statementSemantic(sc
);
1903 bool conditionError
= false;
1904 ss
.condition
= ss
.condition
.expressionSemantic(sc
);
1905 ss
.condition
= resolveProperties(sc
, ss
.condition
);
1909 while (!ss
.condition
.isErrorExp())
1911 // preserve enum type for final switches
1912 if (ss
.condition
.type
.ty
== Tenum
)
1913 te
= cast(TypeEnum
)ss
.condition
.type
;
1914 if (ss
.condition
.type
.isString())
1916 // If it's not an array, cast it to one
1917 if (ss
.condition
.type
.ty
!= Tarray
)
1919 ss
.condition
= ss
.condition
.implicitCastTo(sc
, ss
.condition
.type
.nextOf().arrayOf());
1921 ss
.condition
.type
= ss
.condition
.type
.constOf();
1924 ss
.condition
= integralPromotions(ss
.condition
, sc
);
1925 if (!ss
.condition
.isErrorExp() && ss
.condition
.type
.isintegral())
1928 auto ad
= isAggregate(ss
.condition
.type
);
1929 if (ad
&& ad
.aliasthis
&& !isRecursiveAliasThis(att
, ss
.condition
.type
))
1931 if (auto e
= resolveAliasThis(sc
, ss
.condition
, true))
1938 if (!ss
.condition
.isErrorExp())
1940 error(ss
.loc
, "`%s` must be of integral or string type, it is a `%s`",
1941 ss
.condition
.toChars(), ss
.condition
.type
.toChars());
1942 conditionError
= true;
1946 if (checkNonAssignmentArrayOp(ss
.condition
))
1947 ss
.condition
= ErrorExp
.get();
1948 ss
.condition
= ss
.condition
.optimize(WANTvalue
);
1949 ss
.condition
= checkGC(sc
, ss
.condition
);
1950 if (ss
.condition
.op
== EXP
.error
)
1951 conditionError
= true;
1953 bool needswitcherror
= false;
1955 ss
.lastVar
= sc
.lastVar
;
1961 ss
.cases
= new CaseStatements();
1962 const inLoopSave
= sc
.inLoop
;
1963 sc
.inLoop
= true; // BUG: should use Scope::mergeCallSuper() for each case instead
1964 ss
._body
= ss
._body
.statementSemantic(sc
);
1965 sc
.inLoop
= inLoopSave
;
1967 if (conditionError ||
(ss
._body
&& ss
._body
.isErrorStatement()))
1973 // Resolve any goto case's with exp
1975 foreach (gcs
; ss
.gotoCases
)
1979 error(gcs
.loc
, "no `case` statement following `goto case;`");
1984 for (Scope
* scx
= sc
; scx
; scx
= scx
.enclosing
)
1988 foreach (cs
; *scx
.sw
.cases
)
1990 if (cs
.exp
.equals(gcs
.exp
))
1997 error(gcs
.loc
, "`case %s` not found", gcs
.exp
.toChars());
2004 Type t
= ss
.condition
.type
;
2006 EnumDeclaration ed
= null;
2007 if (t
&& ((ds = t
.toDsymbol(sc
)) !is null))
2008 ed
= ds.isEnumDeclaration(); // typedef'ed enum
2009 if (!ed
&& te
&& ((ds = te
.toDsymbol(sc
)) !is null))
2010 ed
= ds.isEnumDeclaration();
2011 if (ed
&& ss
.cases
.length
< ed
.members
.length
)
2013 int missingMembers
= 0;
2014 const maxShown
= global
.params
.v
.errorSupplementCount();
2016 foreach (es
; *ed
.members
)
2018 EnumMember em
= es
.isEnumMember();
2021 foreach (cs
; *ss
.cases
)
2023 if (cs
.exp
.equals(em
.value
) ||
(!cs
.exp
.type
.isString() &&
2024 !em
.value
.type
.isString() && cs
.exp
.toInteger() == em
.value
.toInteger()))
2027 if (missingMembers
== 0)
2028 error(ss
.loc
, "missing cases for `enum` members in `final switch`:");
2030 if (missingMembers
< maxShown
)
2031 errorSupplemental(ss
.loc
, "`%s`", em
.toChars());
2035 if (missingMembers
> 0)
2037 if (missingMembers
> maxShown
)
2038 errorSupplemental(ss
.loc
, "... (%d more, -v to show) ...", missingMembers
- maxShown
);
2044 needswitcherror
= true;
2047 ss
.hasDefault
= sc
.sw
.sdefault ||
2048 !(!ss
.isFinal || needswitcherror || global
.params
.useAssert
== CHECKENABLE
.on || sc
.func
.isSafe
);
2051 if (!ss
.isFinal
&& (!ss
._body ||
!ss
._body
.isErrorStatement()) && !(sc
.flags
& SCOPE
.Cfile
))
2052 error(ss
.loc
, "`switch` statement without a `default`; use `final switch` or add `default: assert(0);` or add `default: break;`");
2054 // Generate runtime error if the default is hit
2055 auto a
= new Statements();
2056 CompoundStatement cs
;
2059 if (sc
.flags
& SCOPE
.Cfile
)
2061 s
= new BreakStatement(ss
.loc
, null); // default for C is `default: break;`
2063 else if (!sc
.needsCodegen())
2065 // something for the interpreter to deal with
2066 s
= new ExpStatement(ss
.loc
, new AssertExp(ss
.loc
, IntegerExp
.literal
!0));
2068 else if (global
.params
.useSwitchError
== CHECKENABLE
.on
&&
2069 global
.params
.checkAction
!= CHECKACTION
.halt
)
2071 if (global
.params
.checkAction
== CHECKACTION
.C
)
2073 /* Rewrite as an assert(0) and let e2ir generate
2074 * the call to the C assert failure function
2076 s
= new ExpStatement(ss
.loc
, new AssertExp(ss
.loc
, IntegerExp
.literal
!0));
2080 if (!verifyHookExist(ss
.loc
, *sc
, Id
.__switch_error
, "generating assert messages"))
2083 Expression sl
= new IdentifierExp(ss
.loc
, Id
.empty
);
2084 sl
= new DotIdExp(ss
.loc
, sl
, Id
.object
);
2085 sl
= new DotIdExp(ss
.loc
, sl
, Id
.__switch_error
);
2087 Expressions
* args
= new Expressions(2);
2088 (*args
)[0] = new StringExp(ss
.loc
, ss
.loc
.filename
.toDString());
2089 (*args
)[1] = new IntegerExp(ss
.loc
.linnum
);
2091 sl
= new CallExp(ss
.loc
, sl
, args
);
2092 sl
= sl
.expressionSemantic(sc
);
2094 s
= new SwitchErrorStatement(ss
.loc
, sl
);
2098 s
= new ExpStatement(ss
.loc
, new HaltExp(ss
.loc
));
2101 sc
.sw
.sdefault
= new DefaultStatement(ss
.loc
, s
);
2103 if (ss
._body
.blockExit(sc
.func
, null) & BE
.fallthru
)
2104 a
.push(new BreakStatement(Loc
.initial
, null));
2105 a
.push(sc
.sw
.sdefault
);
2106 cs
= new CompoundStatement(ss
.loc
, a
);
2110 if (!(sc
.flags
& SCOPE
.Cfile
) && ss
.checkLabel())
2117 if (!(ss
.condition
.type
.isString() && sc
.needsCodegen()))
2124 // Transform a switch with string labels into a switch with integer labels.
2126 // The integer value of each case corresponds to the index of each label
2127 // string in the sorted array of label strings.
2129 // The value of the integer condition is obtained by calling the druntime template
2130 // switch(object.__switch(cond, options...)) {0: {...}, 1: {...}, ...}
2132 // We sort a copy of the array of labels because we want to do a binary search in object.__switch,
2133 // without modifying the order of the case blocks here in the compiler.
2135 if (!verifyHookExist(ss
.loc
, *sc
, Id
.__switch
, "switch cases on strings"))
2138 size_t numcases
= 0;
2140 numcases
= ss
.cases
.length
;
2142 for (size_t i
= 0; i
< numcases
; i
++)
2144 CaseStatement cs
= (*ss
.cases
)[i
];
2145 cs
.index
= cast(int)i
;
2148 // Make a copy of all the cases so that qsort doesn't scramble the actual
2149 // data we pass to codegen (the order of the cases in the switch).
2150 CaseStatements
*csCopy
= (*ss
.cases
).copy();
2154 static int sort_compare(in CaseStatement
* x
, in CaseStatement
* y
) @trusted
2156 auto se1
= x
.exp
.isStringExp();
2157 auto se2
= y
.exp
.isStringExp();
2158 return (se1
&& se2
) ? se1
.compare(se2
) : 0;
2160 // Sort cases for efficient lookup
2161 csCopy
.sort
!sort_compare
;
2164 // The actual lowering
2165 auto arguments
= new Expressions();
2166 arguments
.push(ss
.condition
);
2168 auto compileTimeArgs
= new Objects();
2170 // The type & label no.
2171 compileTimeArgs
.push(new TypeExp(ss
.loc
, ss
.condition
.type
.nextOf()));
2173 // The switch labels
2174 foreach (caseString
; *csCopy
)
2176 compileTimeArgs
.push(caseString
.exp
);
2179 Expression sl
= new IdentifierExp(ss
.loc
, Id
.empty
);
2180 sl
= new DotIdExp(ss
.loc
, sl
, Id
.object
);
2181 sl
= new DotTemplateInstanceExp(ss
.loc
, sl
, Id
.__switch
, compileTimeArgs
);
2183 sl
= new CallExp(ss
.loc
, sl
, arguments
);
2184 sl
= sl
.expressionSemantic(sc
);
2188 foreach (c
; *csCopy
)
2190 (*ss
.cases
)[c
.index
].exp
= new IntegerExp(i
++);
2193 //printf("%s\n", ss._body.toChars());
2194 ss
.statementSemantic(sc
);
2200 void visitCase(CaseStatement cs
)
2202 SwitchStatement sw
= sc
.sw
;
2203 bool errors
= false;
2205 //printf("CaseStatement::semantic() %s\n", toChars());
2206 sc
= sc
.startCTFE();
2207 cs
.exp
= cs
.exp
.expressionSemantic(sc
);
2208 cs
.exp
= resolveProperties(sc
, cs
.exp
);
2213 Expression initialExp
= cs
.exp
;
2215 // The switch'ed value has errors and doesn't provide the actual type
2216 // Omit the cast to enable further semantic (exluding the check for matching types)
2217 if (sw
.condition
.type
&& !sw
.condition
.type
.isTypeError())
2218 cs
.exp
= cs
.exp
.implicitCastTo(sc
, sw
.condition
.type
);
2219 cs
.exp
= cs
.exp
.optimize(WANTvalue | WANTexpand
);
2221 Expression e
= cs
.exp
;
2222 // Remove all the casts the user and/or implicitCastTo may introduce
2223 // otherwise we'd sometimes fail the check below.
2224 while (e
.op
== EXP
.cast_
)
2225 e
= (cast(CastExp
)e
).e1
;
2227 /* This is where variables are allowed as case expressions.
2229 if (e
.op
== EXP
.variable
)
2231 VarExp ve
= cast(VarExp
)e
;
2232 VarDeclaration v
= ve
.var
.isVarDeclaration();
2233 Type t
= cs
.exp
.type
.toBasetype();
2234 if (v
&& (t
.isintegral() || t
.ty
== Tclass
))
2236 /* Flag that we need to do special code generation
2237 * for this, i.e. generate a sequence of if-then-else
2241 /* TODO check if v can be uninitialized at that point.
2243 if (!v
.isConst() && !v
.isImmutable())
2245 error(cs
.loc
, "`case` variables have to be `const` or `immutable`");
2250 error(cs
.loc
, "`case` variables not allowed in `final switch` statements");
2254 /* Find the outermost scope `scx` that set `sw`.
2255 * Then search scope `scx` for a declaration of `v`.
2257 for (Scope
* scx
= sc
; scx
; scx
= scx
.enclosing
)
2259 if (scx
.enclosing
&& scx
.enclosing
.sw
== sw
)
2261 assert(scx
.sw
== sw
);
2263 if (!scx
.search(cs
.exp
.loc
, v
.ident
, null))
2265 error(cs
.loc
, "`case` variable `%s` declared at %s cannot be declared in `switch` body",
2266 v
.toChars(), v
.loc
.toChars());
2275 cs
.exp
= cs
.exp
.ctfeInterpret();
2277 if (StringExp se
= cs
.exp
.toStringExp())
2279 else if (!cs
.exp
.isIntegerExp() && !cs
.exp
.isErrorExp())
2281 error(cs
.loc
, "`case` expression must be a compile-time `string` or an integral constant, not `%s`", cs
.exp
.toChars());
2286 // // Don't check other cases if this has errors
2287 if (!cs
.exp
.isErrorExp())
2288 foreach (cs2
; *sw
.cases
)
2290 //printf("comparing '%s' with '%s'\n", exp.toChars(), cs.exp.toChars());
2291 if (cs2
.exp
.equals(cs
.exp
))
2293 // https://issues.dlang.org/show_bug.cgi?id=15909
2294 error(cs
.loc
, "duplicate `case %s` in `switch` statement", initialExp
.toChars());
2302 // Resolve any goto case's with no exp to this case statement
2303 for (size_t i
= 0; i
< sw
.gotoCases
.length
;)
2305 GotoCaseStatement gcs
= sw
.gotoCases
[i
];
2309 sw
.gotoCases
.remove(i
); // remove from array
2315 if (sc
.sw
.tf
!= sc
.tf
)
2317 error(cs
.loc
, "`switch` and `case` are in different `finally` blocks");
2320 if (sc
.sw
.tryBody
!= sc
.tryBody
)
2322 error(cs
.loc
, "case cannot be in different `try` block level from `switch`");
2328 error(cs
.loc
, "`case` not in `switch` statement");
2332 sc
.ctorflow
.orCSX(CSX
.label
);
2333 cs
.statement
= cs
.statement
.statementSemantic(sc
);
2334 if (cs
.statement
.isErrorStatement())
2336 result
= cs
.statement
;
2339 if (errors || cs
.exp
.op
== EXP
.error
)
2342 cs
.lastVar
= sc
.lastVar
;
2346 void visitCaseRange(CaseRangeStatement crs
)
2348 SwitchStatement sw
= sc
.sw
;
2351 error(crs
.loc
, "case range not in `switch` statement");
2355 //printf("CaseRangeStatement::semantic() %s\n", toChars());
2356 bool errors
= false;
2359 error(crs
.loc
, "case ranges not allowed in `final switch`");
2363 sc
= sc
.startCTFE();
2364 crs
.first
= crs
.first
.expressionSemantic(sc
);
2365 crs
.first
= resolveProperties(sc
, crs
.first
);
2367 crs
.first
= crs
.first
.implicitCastTo(sc
, sw
.condition
.type
);
2368 crs
.first
= crs
.first
.ctfeInterpret();
2370 sc
= sc
.startCTFE();
2371 crs
.last
= crs
.last
.expressionSemantic(sc
);
2372 crs
.last
= resolveProperties(sc
, crs
.last
);
2374 crs
.last
= crs
.last
.implicitCastTo(sc
, sw
.condition
.type
);
2375 crs
.last
= crs
.last
.ctfeInterpret();
2377 if (crs
.first
.op
== EXP
.error || crs
.last
.op
== EXP
.error || errors
)
2380 crs
.statement
.statementSemantic(sc
);
2384 uinteger_t fval
= crs
.first
.toInteger();
2385 uinteger_t lval
= crs
.last
.toInteger();
2386 if ((crs
.first
.type
.isunsigned() && fval
> lval
) ||
(!crs
.first
.type
.isunsigned() && cast(sinteger_t
)fval
> cast(sinteger_t
)lval
))
2388 error(crs
.loc
, "first `case %s` is greater than last `case %s`", crs
.first
.toChars(), crs
.last
.toChars());
2393 if (lval
- fval
> 256)
2395 error(crs
.loc
, "had %llu cases which is more than 257 cases in case range", 1 + lval
- fval
);
2403 /* This works by replacing the CaseRange with an array of Case's.
2405 * case a: .. case b: s;
2413 auto statements
= new Statements();
2414 for (uinteger_t i
= fval
; i
!= lval
+ 1; i
++)
2416 Statement s
= crs
.statement
;
2417 if (i
!= lval
) // if not last case
2418 s
= new ExpStatement(crs
.loc
, cast(Expression
)null);
2419 Expression e
= new IntegerExp(crs
.loc
, i
, crs
.first
.type
);
2420 Statement cs
= new CaseStatement(crs
.loc
, e
, s
);
2421 statements
.push(cs
);
2423 Statement s
= new CompoundStatement(crs
.loc
, statements
);
2424 sc
.ctorflow
.orCSX(CSX
.label
);
2425 s
= s
.statementSemantic(sc
);
2429 void visitDefault(DefaultStatement
ds)
2431 //printf("DefaultStatement::semantic()\n");
2432 bool errors
= false;
2437 error(ds.loc
, "`switch` statement already has a default");
2440 sc
.sw
.sdefault
= ds;
2442 if (sc
.sw
.tf
!= sc
.tf
)
2444 error(ds.loc
, "`switch` and `default` are in different `finally` blocks");
2447 if (sc
.sw
.tryBody
!= sc
.tryBody
)
2449 error(ds.loc
, "default cannot be in different `try` block level from `switch`");
2454 error(ds.loc
, "`default` statement not allowed in `final switch` statement");
2460 error(ds.loc
, "`default` not in `switch` statement");
2464 sc
.ctorflow
.orCSX(CSX
.label
);
2465 ds.statement
= ds.statement
.statementSemantic(sc
);
2466 if (errors ||
ds.statement
.isErrorStatement())
2469 ds.lastVar
= sc
.lastVar
;
2473 void visitGotoDefault(GotoDefaultStatement gds
)
2475 /* https://dlang.org/spec/statement.html#goto-statement
2481 error(gds
.loc
, "`goto default` not in `switch` statement");
2486 error(gds
.loc
, "`goto default` not allowed in `final switch` statement");
2492 void visitGotoCase(GotoCaseStatement gcs
)
2494 /* https://dlang.org/spec/statement.html#goto-statement
2499 error(gcs
.loc
, "`goto case` not in `switch` statement");
2505 gcs
.exp
= gcs
.exp
.expressionSemantic(sc
);
2506 gcs
.exp
= gcs
.exp
.implicitCastTo(sc
, sc
.sw
.condition
.type
);
2507 gcs
.exp
= gcs
.exp
.optimize(WANTvalue
);
2508 if (gcs
.exp
.op
== EXP
.error
)
2512 sc
.sw
.gotoCases
.push(gcs
);
2516 void visitReturn(ReturnStatement rs
)
2518 /* https://dlang.org/spec/statement.html#return-statement
2521 //printf("ReturnStatement.dsymbolSemantic() %p, %s\n", rs, rs.toChars());
2523 FuncDeclaration fd
= sc
.parent
.isFuncDeclaration();
2525 fd
= fd
.fes
.func
; // fd is now function enclosing foreach
2527 TypeFunction tf
= cast(TypeFunction
)fd
.type
;
2528 assert(tf
.ty
== Tfunction
);
2530 if (rs
.exp
&& rs
.exp
.op
== EXP
.variable
&& (cast(VarExp
)rs
.exp
).var
== fd
.vresult
)
2535 assert(rs
.caseDim
== 0);
2536 sc
.fes
.cases
.push(rs
);
2537 result
= new ReturnStatement(Loc
.initial
, new IntegerExp(sc
.fes
.cases
.length
+ 1));
2542 auto gs
= new GotoStatement(rs
.loc
, Id
.returnLabel
);
2543 gs
.label
= fd
.returnLabel
;
2549 fd
.returns
= new ReturnStatements();
2550 fd
.returns
.push(rs
);
2555 Type tret
= tf
.next
;
2556 Type tbret
= tret ? tret
.toBasetype() : null;
2558 bool inferRef
= (tf
.isref
&& (fd
.storage_class
& STC
.auto_
));
2559 Expression e0
= null;
2561 bool errors
= false;
2562 if (sc
.flags
& SCOPE
.contract
)
2564 error(rs
.loc
, "`return` statements cannot be in contracts");
2569 // @@@DEPRECATED_2.112@@@
2570 // Deprecated in 2.100, transform into an error in 2.112
2571 if (sc
.os
.tok
== TOK
.onScopeFailure
)
2573 deprecation(rs
.loc
, "`return` statements cannot be in `scope(failure)` bodies.");
2574 deprecationSupplemental(rs
.loc
, "Use try-catch blocks for this purpose");
2578 error(rs
.loc
, "`return` statements cannot be in `%s` bodies", Token
.toChars(sc
.os
.tok
));
2584 error(rs
.loc
, "`return` statements cannot be in `finally` bodies");
2588 if (fd
.isCtorDeclaration())
2592 error(rs
.loc
, "cannot return expression from constructor");
2596 // Constructors implicitly do:
2598 rs
.exp
= new ThisExp(Loc
.initial
);
2603 fd
.hasReturnExp |
= (fd
.hasReturnExp
& 1 ?
16 : 1);
2605 FuncLiteralDeclaration
fld = fd
.isFuncLiteralDeclaration();
2607 rs
.exp
= inferType(rs
.exp
, tret
);
2608 else if (fld && fld.treq
)
2609 rs
.exp
= inferType(rs
.exp
, fld.treq
.nextOf().nextOf());
2611 rs
.exp
= rs
.exp
.expressionSemantic(sc
);
2612 rs
.exp
= rs
.exp
.arrayFuncConv(sc
);
2613 // If we're returning by ref, allow the expression to be `shared`
2614 const returnSharedRef
= (tf
.isref
&& (fd
.inferRetType || tret
.isShared()));
2615 rs
.exp
.checkSharedAccess(sc
, returnSharedRef
);
2617 // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
2618 if (rs
.exp
.op
== EXP
.type
)
2619 rs
.exp
= resolveAliasThis(sc
, rs
.exp
);
2621 rs
.exp
= resolveProperties(sc
, rs
.exp
);
2622 if (rs
.exp
.checkType())
2623 rs
.exp
= ErrorExp
.get();
2624 if (auto f
= isFuncAddress(rs
.exp
))
2626 if (fd
.inferRetType
&& f
.checkForwardRef(rs
.exp
.loc
))
2627 rs
.exp
= ErrorExp
.get();
2629 if (checkNonAssignmentArrayOp(rs
.exp
))
2630 rs
.exp
= ErrorExp
.get();
2632 // Extract side-effect part
2633 rs
.exp
= Expression
.extractLast(rs
.exp
, e0
);
2634 if (rs
.exp
.op
== EXP
.call)
2635 rs
.exp
= valueNoDtor(rs
.exp
);
2637 /* Void-return function can have void / noreturn typed expression
2638 * on return statement.
2640 auto texp
= rs
.exp
.type
;
2641 const convToVoid
= texp
.ty
== Tvoid || texp
.ty
== Tnoreturn
;
2643 if (tbret
&& tbret
.ty
== Tvoid || convToVoid
)
2647 error(rs
.loc
, "cannot return non-void from `void` function");
2649 rs
.exp
= new CastExp(rs
.loc
, rs
.exp
, Type
.tvoid
);
2650 rs
.exp
= rs
.exp
.expressionSemantic(sc
);
2653 // https://issues.dlang.org/show_bug.cgi?id=23063
2654 rs
.exp
= checkNoreturnVarAccess(rs
.exp
);
2656 // @@@DEPRECATED_2.111@@@
2657 const olderrors
= global
.startGagging();
2658 // uncomment to turn deprecation into an error when
2659 // deprecation cycle is over
2660 if (discardValue(rs
.exp
))
2664 if (global
.endGagging(olderrors
))
2665 deprecation(rs
.exp
.loc
, "`%s` has no effect", rs
.exp
.toChars());
2672 e0
= Expression
.combine(e0
, rs
.exp
);
2677 e0
= e0
.optimize(WANTvalue
);
2678 e0
= checkGC(sc
, e0
);
2684 if (fd
.inferRetType
) // infer return type
2688 tf
.next
= rs
.exp
.type
;
2690 else if (tret
.ty
!= Terror
&& !rs
.exp
.type
.equals(tret
))
2692 int m1
= rs
.exp
.type
.implicitConvTo(tret
);
2693 int m2
= tret
.implicitConvTo(rs
.exp
.type
);
2694 //printf("exp.type = %s m2<-->m1 tret %s\n", exp.type.toChars(), tret.toChars());
2695 //printf("m1 = %d, m2 = %d\n", m1, m2);
2701 tf
.next
= rs
.exp
.type
;
2705 else if (!rs
.exp
.isErrorExp())
2707 error(rs
.loc
, "expected return type of `%s`, not `%s`:",
2709 rs
.exp
.type
.toChars());
2710 errorSupplemental((fd
.returns
) ?
(*fd
.returns
)[0].loc
: fd
.loc
,
2711 "Return type of `%s` inferred here.",
2715 tf
.next
= Type
.terror
;
2720 tbret
= tret
.toBasetype();
2723 if (inferRef
) // deduce 'auto ref'
2725 /* Determine "refness" of function return:
2726 * if it's an lvalue, return by ref, else return by value
2727 * https://dlang.org/spec/function.html#auto-ref-functions
2730 void turnOffRef(scope void delegate() supplemental
)
2732 tf
.isref
= false; // return by value
2733 tf
.isreturn
= false; // ignore 'return' attribute, whether explicit or inferred
2734 fd
.storage_class
&= ~STC
.return_
;
2736 // If we previously assumed the function could be ref when
2737 // checking for `shared`, make sure we were right
2738 if (global
.params
.noSharedAccess
== FeatureState
.enabled
&& rs
.exp
.type
.isShared())
2740 .error(fd
.loc
, "%s `%s` function returns `shared` but cannot be inferred `ref`", fd
.kind
, fd
.toPrettyChars
);
2745 if (rs
.exp
.isLvalue())
2747 /* May return by ref
2749 Scope
* sc2
= sc
.push();
2750 sc2
.eSink
= global
.errorSinkNull
;
2751 bool err
= checkReturnEscapeRef(sc2
, rs
.exp
, true);
2755 turnOffRef(() { checkReturnEscapeRef(sc
, rs
.exp
, false); });
2756 else if (!rs
.exp
.type
.constConv(tf
.next
))
2758 () => rs
.loc
.errorSupplemental("cannot implicitly convert `%s` of type `%s` to `%s`",
2759 rs
.exp
.toChars(), rs
.exp
.type
.toChars(), tf
.next
.toChars())
2764 () => rs
.loc
.errorSupplemental("return value `%s` is not an lvalue", rs
.exp
.toChars())
2767 /* The "refness" is determined by all of return statements.
2769 * return 3; return x; // ok, x can be a value
2770 * return x; return 3; // ok, x can be a value
2776 // Type of the returned expression (if any), might've been moved to e0
2777 auto resType
= e0 ? e0
.type
: Type
.tvoid
;
2779 // infer return type
2780 if (fd
.inferRetType
)
2782 // 1. First `return <noreturn exp>?`
2783 // 2. Potentially found a returning branch, update accordingly
2784 if (!tf
.next || tf
.next
.toBasetype().isTypeNoreturn())
2786 tf
.next
= resType
; // infer void or noreturn
2788 // Found an actual return value before
2789 else if (tf
.next
.ty
!= Tvoid
&& !resType
.toBasetype().isTypeNoreturn())
2791 if (tf
.next
.ty
!= Terror
)
2793 error(rs
.loc
, "mismatched function return type inference of `void` and `%s`", tf
.next
.toChars());
2796 tf
.next
= Type
.terror
;
2800 tbret
= tret
.toBasetype();
2803 // https://issues.dlang.org/show_bug.cgi?id=23914
2804 if (inferRef
&& !resType
.isTypeNoreturn()) // deduce 'auto ref'
2807 if (tbret
.ty
!= Tvoid
&& !resType
.isTypeNoreturn()) // if non-void return
2809 if (tbret
.ty
!= Terror
)
2812 error(rs
.loc
, "expected return type of `%s`, not `%s`", tret
.toChars(), resType
.toChars());
2813 else if (tbret
.isTypeNoreturn())
2815 error(rs
.loc
, "cannot return from `noreturn` function");
2816 .errorSupplemental(rs
.loc
,
2817 "Consider adding an endless loop, `assert(0)`, or another `noreturn` expression");
2820 error(rs
.loc
, "`return` expression expected");
2824 else if (fd
.isMain())
2826 // main() returns 0, even if it returns void
2827 rs
.exp
= IntegerExp
.literal
!0;
2831 // If any branches have called a ctor, but this branch hasn't, it's an error
2832 if (sc
.ctorflow
.callSuper
& CSX
.any_ctor
&& !(sc
.ctorflow
.callSuper
& (CSX
.this_ctor | CSX
.super_ctor
)))
2834 error(rs
.loc
, "`return` without calling constructor");
2838 if (sc
.ctorflow
.fieldinit
.length
) // if aggregate fields are being constructed
2840 auto ad
= fd
.isMemberLocal();
2842 foreach (i
, v
; ad
.fields
)
2844 bool mustInit
= (v
.storage_class
& STC
.nodefaultctor || v
.type
.needsNested());
2845 if (mustInit
&& !(sc
.ctorflow
.fieldinit
[i
].csx
& CSX
.this_ctor
))
2847 error(rs
.loc
, "an earlier `return` statement skips field `%s` initialization", v
.toChars());
2852 sc
.ctorflow
.orCSX(CSX
.return_
);
2861 // Send out "case receiver" statement to the foreach.
2863 Statement s
= new ReturnStatement(Loc
.initial
, rs
.exp
);
2864 sc
.fes
.cases
.push(s
);
2866 // Immediately rewrite "this" return statement as:
2867 // return cases.length+1;
2868 rs
.exp
= new IntegerExp(sc
.fes
.cases
.length
+ 1);
2871 result
= new CompoundStatement(rs
.loc
, new ExpStatement(rs
.loc
, e0
), rs
);
2879 fd
.buildResultVar(null, rs
.exp
.type
);
2880 bool r
= fd
.vresult
.checkNestedReference(sc
, Loc
.initial
);
2881 assert(!r
); // vresult should be always accessible
2883 // Send out "case receiver" statement to the foreach.
2885 Statement s
= new ReturnStatement(Loc
.initial
, new VarExp(Loc
.initial
, fd
.vresult
));
2886 sc
.fes
.cases
.push(s
);
2888 // Save receiver index for the later rewriting from:
2891 // vresult = exp; retrun caseDim;
2892 rs
.caseDim
= sc
.fes
.cases
.length
+ 1;
2898 fd
.returns
= new ReturnStatements();
2899 fd
.returns
.push(rs
);
2903 if (e0
.op
== EXP
.declaration || e0
.op
== EXP
.comma
)
2905 rs
.exp
= Expression
.combine(e0
, rs
.exp
);
2909 auto es
= new ExpStatement(rs
.loc
, e0
);
2910 if (e0
.type
.isTypeNoreturn())
2911 result
= es
; // Omit unreachable return;
2913 result
= new CompoundStatement(rs
.loc
, es
, rs
);
2921 void visitBreak(BreakStatement bs
)
2923 /* https://dlang.org/spec/statement.html#break-statement
2926 //printf("BreakStatement::semantic()\n");
2929 // break Identifier;
2932 bs
.ident
= fixupLabelName(sc
, bs
.ident
);
2934 FuncDeclaration thisfunc
= sc
.func
;
2936 for (Scope
* scx
= sc
; scx
; scx
= scx
.enclosing
)
2938 if (scx
.func
!= thisfunc
) // if in enclosing function
2940 if (sc
.fes
) // if this is the body of a foreach
2942 /* Post this statement to the fes, and replace
2943 * it with a return value that caller will put into
2944 * a switch. Caller will figure out where the break
2945 * label actually is.
2946 * Case numbers start with 2, not 0, as 0 is continue
2949 sc
.fes
.cases
.push(bs
);
2950 result
= new ReturnStatement(Loc
.initial
, new IntegerExp(sc
.fes
.cases
.length
+ 1));
2953 break; // can't break to it
2956 LabelStatement ls
= scx
.slabel
;
2957 if (ls
&& ls
.ident
== bs
.ident
)
2959 Statement s
= ls
.statement
;
2960 if (!s ||
!s
.hasBreak())
2961 error(bs
.loc
, "label `%s` has no `break`", bs
.ident
.toChars());
2962 else if (ls
.tf
!= sc
.tf
)
2963 error(bs
.loc
, "cannot break out of `finally` block");
2973 error(bs
.loc
, "enclosing label `%s` for `break` not found", bs
.ident
.toChars());
2976 else if (!sc
.sbreak
)
2978 if (sc
.os
&& sc
.os
.tok
!= TOK
.onScopeFailure
)
2980 error(bs
.loc
, "`break` is not allowed inside `%s` bodies", Token
.toChars(sc
.os
.tok
));
2984 // Replace break; with return 1;
2985 result
= new ReturnStatement(Loc
.initial
, IntegerExp
.literal
!1);
2989 error(bs
.loc
, "`break` is not inside a loop or `switch`");
2992 else if (sc
.sbreak
.isForwardingStatement())
2994 error(bs
.loc
, "must use labeled `break` within `static foreach`");
2999 void visitContinue(ContinueStatement cs
)
3001 /* https://dlang.org/spec/statement.html#continue-statement
3004 //printf("ContinueStatement::semantic() %p\n", cs);
3007 cs
.ident
= fixupLabelName(sc
, cs
.ident
);
3010 FuncDeclaration thisfunc
= sc
.func
;
3012 for (scx
= sc
; scx
; scx
= scx
.enclosing
)
3015 if (scx
.func
!= thisfunc
) // if in enclosing function
3017 if (sc
.fes
) // if this is the body of a foreach
3019 for (; scx
; scx
= scx
.enclosing
)
3022 if (ls
&& ls
.ident
== cs
.ident
&& ls
.statement
== sc
.fes
)
3024 // Replace continue ident; with return 0;
3025 result
= new ReturnStatement(Loc
.initial
, IntegerExp
.literal
!0);
3030 /* Post this statement to the fes, and replace
3031 * it with a return value that caller will put into
3032 * a switch. Caller will figure out where the break
3033 * label actually is.
3034 * Case numbers start with 2, not 0, as 0 is continue
3037 sc
.fes
.cases
.push(cs
);
3038 result
= new ReturnStatement(Loc
.initial
, new IntegerExp(sc
.fes
.cases
.length
+ 1));
3041 break; // can't continue to it
3045 if (ls
&& ls
.ident
== cs
.ident
)
3047 Statement s
= ls
.statement
;
3048 if (!s ||
!s
.hasContinue())
3049 error(cs
.loc
, "label `%s` has no `continue`", cs
.ident
.toChars());
3050 else if (ls
.tf
!= sc
.tf
)
3051 error(cs
.loc
, "cannot continue out of `finally` block");
3060 error(cs
.loc
, "enclosing label `%s` for `continue` not found", cs
.ident
.toChars());
3063 else if (!sc
.scontinue
)
3065 if (sc
.os
&& sc
.os
.tok
!= TOK
.onScopeFailure
)
3067 error(cs
.loc
, "`continue` is not allowed inside `%s` bodies", Token
.toChars(sc
.os
.tok
));
3071 // Replace continue; with return 0;
3072 result
= new ReturnStatement(Loc
.initial
, IntegerExp
.literal
!0);
3076 error(cs
.loc
, "`continue` is not inside a loop");
3079 else if (sc
.scontinue
.isForwardingStatement())
3081 error(cs
.loc
, "must use labeled `continue` within `static foreach`");
3086 void visitSynchronized(SynchronizedStatement ss
)
3088 /* https://dlang.org/spec/statement.html#synchronized-statement
3093 ss
.exp
= ss
.exp
.expressionSemantic(sc
);
3094 ss
.exp
= resolveProperties(sc
, ss
.exp
);
3095 ss
.exp
= ss
.exp
.optimize(WANTvalue
);
3096 ss
.exp
= checkGC(sc
, ss
.exp
);
3097 if (ss
.exp
.op
== EXP
.error
)
3100 ss
._body
= ss
._body
.statementSemantic(sc
);
3104 ClassDeclaration cd
= ss
.exp
.type
.isClassHandle();
3107 error(ss
.loc
, "can only `synchronize` on class objects, not `%s`", ss
.exp
.type
.toChars());
3110 else if (cd
.isInterfaceDeclaration())
3112 /* Cast the interface to an object, as the object has the monitor,
3113 * not the interface.
3115 if (!ClassDeclaration
.object
)
3117 error(ss
.loc
, "missing or corrupt object.d");
3121 Type t
= ClassDeclaration
.object
.type
;
3122 t
= t
.typeSemantic(Loc
.initial
, sc
).toBasetype();
3123 assert(t
.ty
== Tclass
);
3125 ss
.exp
= new CastExp(ss
.loc
, ss
.exp
, t
);
3126 ss
.exp
= ss
.exp
.expressionSemantic(sc
);
3132 * _d_monitorenter(tmp);
3133 * try { body } finally { _d_monitorexit(tmp); }
3135 auto tmp
= copyToTemp(0, "__sync", ss
.exp
);
3136 tmp
.dsymbolSemantic(sc
);
3138 auto cs
= new Statements();
3139 cs
.push(new ExpStatement(ss
.loc
, tmp
));
3141 auto args
= new Parameters();
3142 args
.push(new Parameter(Loc
.initial
, 0, ClassDeclaration
.object
.type
, null, null, null));
3144 FuncDeclaration fdenter
= FuncDeclaration
.genCfunc(args
, Type
.tvoid
, Id
.monitorenter
);
3145 Expression e
= new CallExp(ss
.loc
, fdenter
, new VarExp(ss
.loc
, tmp
));
3146 e
.type
= Type
.tvoid
; // do not run semantic on e
3148 cs
.push(new ExpStatement(ss
.loc
, e
));
3149 FuncDeclaration fdexit
= FuncDeclaration
.genCfunc(args
, Type
.tvoid
, Id
.monitorexit
);
3150 e
= new CallExp(ss
.loc
, fdexit
, new VarExp(ss
.loc
, tmp
));
3151 e
.type
= Type
.tvoid
; // do not run semantic on e
3152 Statement s
= new ExpStatement(ss
.loc
, e
);
3153 s
= new TryFinallyStatement(ss
.loc
, ss
._body
, s
);
3156 s
= new CompoundStatement(ss
.loc
, cs
);
3157 result
= s
.statementSemantic(sc
);
3162 /* Generate our own critical section, then rewrite as:
3163 * static shared void* __critsec;
3164 * _d_criticalenter2(&__critsec);
3165 * try { body } finally { _d_criticalexit(__critsec); }
3167 auto id
= Identifier
.generateId("__critsec");
3168 auto t
= Type
.tvoidptr
;
3169 auto tmp
= new VarDeclaration(ss
.loc
, t
, id
, null);
3170 tmp
.storage_class |
= STC
.temp | STC
.shared_ | STC
.static_
;
3171 Expression tmpExp
= new VarExp(ss
.loc
, tmp
);
3173 auto cs
= new Statements();
3174 cs
.push(new ExpStatement(ss
.loc
, tmp
));
3176 /* This is just a dummy variable for "goto skips declaration" error.
3177 * Backend optimizer could remove this unused variable.
3179 auto v
= new VarDeclaration(ss
.loc
, Type
.tvoidptr
, Identifier
.generateId("__sync"), null);
3180 v
.dsymbolSemantic(sc
);
3181 cs
.push(new ExpStatement(ss
.loc
, v
));
3183 auto enterArgs
= new Parameters();
3184 enterArgs
.push(new Parameter(Loc
.initial
, 0, t
.pointerTo(), null, null, null));
3186 FuncDeclaration fdenter
= FuncDeclaration
.genCfunc(enterArgs
, Type
.tvoid
, Id
.criticalenter
, STC
.nothrow_
);
3187 Expression e
= new AddrExp(ss
.loc
, tmpExp
);
3188 e
= e
.expressionSemantic(sc
);
3189 e
= new CallExp(ss
.loc
, fdenter
, e
);
3190 e
.type
= Type
.tvoid
; // do not run semantic on e
3191 cs
.push(new ExpStatement(ss
.loc
, e
));
3193 auto exitArgs
= new Parameters();
3194 exitArgs
.push(new Parameter(Loc
.initial
, 0, t
, null, null, null));
3196 FuncDeclaration fdexit
= FuncDeclaration
.genCfunc(exitArgs
, Type
.tvoid
, Id
.criticalexit
, STC
.nothrow_
);
3197 e
= new CallExp(ss
.loc
, fdexit
, tmpExp
);
3198 e
.type
= Type
.tvoid
; // do not run semantic on e
3199 Statement s
= new ExpStatement(ss
.loc
, e
);
3200 s
= new TryFinallyStatement(ss
.loc
, ss
._body
, s
);
3203 s
= new CompoundStatement(ss
.loc
, cs
);
3204 result
= s
.statementSemantic(sc
);
3208 void visitWith(WithStatement ws
)
3210 /* https://dlang.org/spec/statement.html#with-statement
3216 //printf("WithStatement::semantic()\n");
3217 ws
.exp
= ws
.exp
.expressionSemantic(sc
);
3218 ws
.exp
= resolveProperties(sc
, ws
.exp
);
3219 ws
.exp
= ws
.exp
.optimize(WANTvalue
);
3220 ws
.exp
= checkGC(sc
, ws
.exp
);
3221 if (ws
.exp
.op
== EXP
.error
)
3223 if (ws
.exp
.op
== EXP
.scope_
)
3225 sym
= new WithScopeSymbol(ws
);
3226 sym
.parent
= sc
.scopesym
;
3227 sym
.endlinnum
= ws
.endloc
.linnum
;
3229 else if (ws
.exp
.op
== EXP
.type
)
3231 Dsymbol s
= (cast(TypeExp
)ws
.exp
).type
.toDsymbol(sc
);
3232 if (!s ||
!s
.isScopeDsymbol())
3234 error(ws
.loc
, "`with` type `%s` has no members", ws
.exp
.toChars());
3237 sym
= new WithScopeSymbol(ws
);
3238 sym
.parent
= sc
.scopesym
;
3239 sym
.endlinnum
= ws
.endloc
.linnum
;
3243 Type texp
= ws
.exp
.type
;
3244 Type t
= texp
.toBasetype();
3246 Expression olde
= ws
.exp
;
3247 if (t
.ty
== Tpointer
)
3249 ws
.exp
= new PtrExp(ws
.loc
, ws
.exp
);
3250 ws
.exp
= ws
.exp
.expressionSemantic(sc
);
3252 t
= texp
.toBasetype();
3257 if (t
.isClassHandle())
3259 _init
= new ExpInitializer(ws
.loc
, ws
.exp
);
3260 ws
.wthis
= new VarDeclaration(ws
.loc
, ws
.exp
.type
, Id
.withSym
, _init
);
3261 ws
.wthis
.storage_class |
= STC
.temp
;
3262 ws
.wthis
.dsymbolSemantic(sc
);
3264 sym
= new WithScopeSymbol(ws
);
3265 sym
.parent
= sc
.scopesym
;
3266 sym
.endlinnum
= ws
.endloc
.linnum
;
3268 else if (t
.ty
== Tstruct
)
3270 if (!ws
.exp
.isLvalue())
3274 * auto __withtmp = exp
3281 auto tmp
= copyToTemp(0, "__withtmp", ws
.exp
);
3282 tmp
.dsymbolSemantic(sc
);
3283 auto es
= new ExpStatement(ws
.loc
, tmp
);
3284 ws
.exp
= new VarExp(ws
.loc
, tmp
);
3285 Statement ss
= new ScopeStatement(ws
.loc
, new CompoundStatement(ws
.loc
, es
, ws
), ws
.endloc
);
3286 result
= ss
.statementSemantic(sc
);
3289 Expression e
= ws
.exp
.addressOf();
3290 _init
= new ExpInitializer(ws
.loc
, e
);
3291 ws
.wthis
= new VarDeclaration(ws
.loc
, e
.type
, Id
.withSym
, _init
);
3292 ws
.wthis
.storage_class |
= STC
.temp
;
3293 ws
.wthis
.dsymbolSemantic(sc
);
3294 sym
= new WithScopeSymbol(ws
);
3295 // Need to set the scope to make use of resolveAliasThis
3297 sym
.parent
= sc
.scopesym
;
3298 sym
.endlinnum
= ws
.endloc
.linnum
;
3300 else if (auto tenum
= texp
.isTypeEnum())
3302 ws
.exp
= new TypeExp(ws
.exp
.loc
, tenum
);
3303 sym
= new WithScopeSymbol(ws
);
3304 sym
.parent
= sc
.scopesym
;
3305 sym
.endlinnum
= ws
.endloc
.linnum
;
3309 error(ws
.loc
, "`with` expression types must be enums or aggregates or pointers to them, not `%s`", olde
.type
.toChars());
3319 ws
._body
= ws
._body
.statementSemantic(sc
);
3321 if (ws
._body
&& ws
._body
.isErrorStatement())
3331 // https://dlang.org/spec/statement.html#TryStatement
3332 void visitTryCatch(TryCatchStatement tcs
)
3334 //printf("TryCatchStatement.semantic()\n");
3336 if (!global
.params
.useExceptions
)
3338 error(tcs
.loc
, "cannot use try-catch statements with %s", global
.params
.betterC ?
"-betterC".ptr
: "-nothrow".ptr
);
3342 if (!ClassDeclaration
.throwable
)
3344 error(tcs
.loc
, "cannot use try-catch statements because `object.Throwable` was not declared");
3352 tcs
.tryBody
= sc
.tryBody
; // chain on the in-flight tryBody
3353 tcs
._body
= tcs
._body
.semanticScope(sc
, null, null, tcs
);
3355 /* Even if body is empty, still do semantic analysis on catches
3357 bool catchErrors
= false;
3358 foreach (i
, c
; *tcs
.catches
)
3360 c
.catchSemantic(sc
);
3366 auto cd
= c
.type
.toBasetype().isClassHandle();
3367 flags |
= cd
.isCPPclass() ? FLAGcpp
: FLAGd
;
3369 // Determine if current catch 'hides' any previous catches
3372 Catch cj
= (*tcs
.catches
)[j
];
3373 const si
= c
.loc
.toChars();
3374 const sj
= cj
.loc
.toChars();
3375 if (c
.type
.toBasetype().implicitConvTo(cj
.type
.toBasetype()))
3377 error(tcs
.loc
, "`catch` at %s hides `catch` at %s", sj
, si
);
3385 sc
.func
.hasCatches
= true;
3386 if (flags
== (FLAGcpp | FLAGd
))
3388 error(tcs
.loc
, "cannot mix catching D and C++ exceptions in the same try-catch");
3396 // No actual code in the try (i.e. omitted any conditionally compiled code)
3397 // Could also be extended to check for hasCode
3401 if (tcs
._body
.isErrorStatement())
3407 /* If the try body never throws, we can eliminate any catches
3408 * of recoverable exceptions.
3410 if (!(tcs
._body
.blockExit(sc
.func
, null) & BE
.throw_
) && ClassDeclaration
.exception
)
3412 foreach_reverse (i
; 0 .. tcs
.catches
.length
)
3414 Catch c
= (*tcs
.catches
)[i
];
3416 /* If catch exception type is derived from Exception
3418 if (c
.type
.toBasetype().implicitConvTo(ClassDeclaration
.exception
.type
) &&
3419 (!c
.handler ||
!c
.handler
.comeFrom()) && !(sc
.flags
& SCOPE
.debug_
))
3421 // Remove c from the array of catches
3422 tcs
.catches
.remove(i
);
3427 if (tcs
.catches
.length
== 0)
3429 result
= tcs
._body
.hasCode() ? tcs
._body
: null;
3436 void visitTryFinally(TryFinallyStatement tfs
)
3438 //printf("TryFinallyStatement::semantic()\n");
3439 tfs
.tryBody
= sc
.tryBody
; // chain on in-flight tryBody
3440 tfs
._body
= tfs
._body
.semanticScope(sc
, null, null, tfs
);
3445 sc
.scontinue
= null; // no break or continue out of finally block
3446 tfs
.finalbody
= tfs
.finalbody
.semanticNoScope(sc
);
3451 result
= tfs
.finalbody
;
3460 auto blockexit
= tfs
._body
.blockExit(sc
.func
, null);
3462 // if not worrying about exceptions
3463 if (!(global
.params
.useExceptions
&& ClassDeclaration
.throwable
))
3464 blockexit
&= ~BE
.throw_
; // don't worry about paths that otherwise may throw
3466 // Don't care about paths that halt, either
3467 if ((blockexit
& ~BE
.halt
) == BE
.fallthru
)
3469 result
= new CompoundStatement(tfs
.loc
, tfs
._body
, tfs
.finalbody
);
3472 tfs
.bodyFallsThru
= (blockexit
& BE
.fallthru
) != 0;
3476 void visitScopeGuard(ScopeGuardStatement oss
)
3478 /* https://dlang.org/spec/statement.html#scope-guard-statement
3481 if (oss
.tok
!= TOK
.onScopeExit
)
3483 // https://issues.dlang.org/show_bug.cgi?id=23159
3484 if (!global
.params
.useExceptions
)
3486 error(oss
.loc
, "`%s` cannot be used with -betterC", Token
.toChars(oss
.tok
));
3490 // scope(success) and scope(failure) are rewritten to try-catch(-finally) statement,
3491 // so the generated catch block cannot be placed in finally block.
3492 // See also Catch::semantic.
3493 if (sc
.os
&& sc
.os
.tok
!= TOK
.onScopeFailure
)
3495 // If enclosing is scope(success) or scope(exit), this will be placed in finally block.
3496 error(oss
.loc
, "cannot put `%s` statement inside `%s`", Token
.toChars(oss
.tok
), Token
.toChars(sc
.os
.tok
));
3501 error(oss
.loc
, "cannot put `%s` statement inside `finally` block", Token
.toChars(oss
.tok
));
3509 if (oss
.tok
!= TOK
.onScopeFailure
)
3511 // Jump out from scope(failure) block is allowed.
3513 sc
.scontinue
= null;
3515 oss
.statement
= oss
.statement
.semanticNoScope(sc
);
3518 if (!oss
.statement || oss
.statement
.isErrorStatement())
3520 result
= oss
.statement
;
3526 void visitThrow(ThrowStatement ts
)
3528 /* https://dlang.org/spec/statement.html#throw-statement
3531 //printf("ThrowStatement::semantic()\n");
3532 if (throwSemantic(ts
.loc
, ts
.exp
, sc
))
3539 void visitDebug(DebugStatement
ds)
3544 sc
.flags |
= SCOPE
.debug_
;
3545 ds.statement
= ds.statement
.statementSemantic(sc
);
3548 result
= ds.statement
;
3551 void visitGoto(GotoStatement gs
)
3553 /* https://dlang.org/spec/statement.html#goto-statement
3556 //printf("GotoStatement::semantic()\n");
3557 FuncDeclaration fd
= sc
.func
;
3559 gs
.ident
= fixupLabelName(sc
, gs
.ident
);
3560 gs
.label
= fd
.searchLabel(gs
.ident
, gs
.loc
);
3561 gs
.tryBody
= sc
.tryBody
;
3564 gs
.lastVar
= sc
.lastVar
;
3565 gs
.inCtfeBlock
= (sc
.flags
& SCOPE
.ctfeBlock
) != 0;
3567 if (!gs
.label
.statement
&& sc
.fes
)
3569 /* Either the goto label is forward referenced or it
3570 * is in the function that the enclosing foreach is in.
3571 * Can't know yet, so wrap the goto in a scope statement
3572 * so we can patch it later, and add it to a 'look at this later'
3575 gs
.label
.deleted
= true;
3576 auto ss
= new ScopeStatement(gs
.loc
, gs
, gs
.loc
);
3577 sc
.fes
.gotos
.push(ss
); // 'look at this later' list
3582 // Add to fwdref list to check later
3583 if (!gs
.label
.statement
)
3586 fd
.gotos
= new GotoStatements();
3589 else if (!(sc
.flags
& SCOPE
.Cfile
) && gs
.checkLabel())
3595 void visitLabel(LabelStatement ls
)
3597 //printf("LabelStatement::semantic()\n");
3598 FuncDeclaration fd
= sc
.parent
.isFuncDeclaration();
3600 ls
.ident
= fixupLabelName(sc
, ls
.ident
);
3601 ls
.tryBody
= sc
.tryBody
;
3604 ls
.lastVar
= sc
.lastVar
;
3605 ls
.inCtfeBlock
= (sc
.flags
& SCOPE
.ctfeBlock
) != 0;
3607 LabelDsymbol ls2
= fd
.searchLabel(ls
.ident
, ls
.loc
);
3608 if (ls2
.statement
&& !ls2
.duplicated
)
3610 if (ls
.loc
== ls2
.loc
)
3612 ls2
.duplicated
= true;
3613 error(ls
.loc
, "label `%s` is duplicated", ls2
.toChars());
3614 .errorSupplemental(ls2
.loc
, "labels cannot be used in a static foreach with more than 1 iteration");
3618 error(ls
.loc
, "label `%s` is already defined", ls2
.toChars());
3619 .errorSupplemental(ls2
.loc
, "first definition is here");
3627 sc
.scopesym
= sc
.enclosing
.scopesym
;
3629 sc
.ctorflow
.orCSX(CSX
.label
);
3633 ls
.statement
= ls
.statement
.statementSemantic(sc
);
3639 void visitAsm(AsmStatement s
)
3641 /* https://dlang.org/spec/statement.html#asm
3644 //printf("AsmStatement()::semantic()\n");
3645 result
= asmSemantic(s
, sc
);
3648 void visitCompoundAsm(CompoundAsmStatement cas
)
3650 //printf("CompoundAsmStatement()::semantic()\n");
3651 // Apply postfix attributes of the asm block to each statement.
3655 /* Go through the statements twice, first to declare any labels,
3656 * second for anything else.
3659 foreach (ref s
; *cas
.statements
)
3663 if (auto ls
= s
.isLabelStatement())
3665 sc
.func
.searchLabel(ls
.ident
, ls
.loc
);
3670 foreach (ref s
; *cas
.statements
)
3672 s
= s ? s
.statementSemantic(sc
) : null;
3676 if (!(cas
.stc & STC
.pure_
) && sc
.func
.setImpure(cas
.loc
, "`asm` statement is assumed to be impure - mark it with `pure` if it is not"))
3677 error(cas
.loc
, "`asm` statement is assumed to be impure - mark it with `pure` if it is not");
3678 if (!(cas
.stc & STC
.nogc
) && sc
.func
.setGC(cas
.loc
, "`asm` statement in %s `%s` is assumed to use the GC - mark it with `@nogc` if it does not"))
3679 error(cas
.loc
, "`asm` statement is assumed to use the GC - mark it with `@nogc` if it does not");
3680 // @@@DEPRECATED_2.114@@@
3681 // change deprecation() to error(), add `else` and remove `| STC.safe`
3682 // to turn deprecation into an error when deprecation cycle is over
3683 if (cas
.stc & STC
.safe
)
3684 deprecation(cas
.loc
, "`asm` statement cannot be marked `@safe`, use `@system` or `@trusted` instead");
3685 if (!(cas
.stc & (STC
.trusted | STC
.safe
)))
3687 sc
.setUnsafe(false, cas
.loc
, "`asm` statement is assumed to be `@system` - mark it with `@trusted` if it is not");
3694 void visitImport(ImportStatement imps
)
3696 /* https://dlang.org/spec/module.html#ImportDeclaration
3699 foreach (i
; 0 .. imps
.imports
.length
)
3701 Import s
= (*imps
.imports
)[i
].isImport();
3702 assert(!s
.aliasdecls
.length
);
3703 foreach (j
, name
; s
.names
)
3705 Identifier _alias
= s
.aliases
[j
];
3709 auto tname
= new TypeIdentifier(s
.loc
, name
);
3710 auto ad
= new AliasDeclaration(s
.loc
, _alias
, tname
);
3712 s
.aliasdecls
.push(ad
);
3715 s
.dsymbolSemantic(sc
);
3717 // https://issues.dlang.org/show_bug.cgi?id=19942
3718 // If the module that's being imported doesn't exist, don't add it to the symbol table
3719 // for the current scope.
3722 Module
.addDeferredSemantic2(s
); // https://issues.dlang.org/show_bug.cgi?id=14666
3725 foreach (aliasdecl
; s
.aliasdecls
)
3727 sc
.insert(aliasdecl
);
3734 mixin VisitStatement
!void visit
;
3735 visit
.VisitStatement(s
);
3740 * Run semantic on `throw <exp>`.
3743 * loc = location of the `throw`
3744 * exp = value to be thrown
3745 * sc = enclosing scope
3747 * Returns: true if the `throw` is valid, or false if an error was found
3749 public bool throwSemantic(const ref Loc loc
, ref Expression exp
, Scope
* sc
)
3751 if (!global
.params
.useExceptions
)
3753 loc
.error("cannot use `throw` statements with %s", global
.params
.betterC ?
"-betterC".ptr
: "-nothrow".ptr
);
3757 if (!ClassDeclaration
.throwable
)
3759 loc
.error("cannot use `throw` statements because `object.Throwable` was not declared");
3763 if (FuncDeclaration fd
= sc
.parent
.isFuncDeclaration())
3764 fd
.hasReturnExp |
= 2;
3766 if (exp
.op
== EXP
.new_
)
3768 NewExp ne
= cast(NewExp
) exp
;
3772 exp
= exp
.expressionSemantic(sc
);
3773 exp
= resolveProperties(sc
, exp
);
3774 exp
= checkGC(sc
, exp
);
3775 if (exp
.op
== EXP
.error
)
3777 if (!exp
.type
.isNaked())
3779 // @@@DEPRECATED_2.112@@@
3780 // Deprecated in 2.102, change into an error & return false in 2.112
3781 exp
.loc
.deprecation("cannot throw object of qualified type `%s`", exp
.type
.toChars());
3784 checkThrowEscape(sc
, exp
, false);
3786 ClassDeclaration cd
= exp
.type
.toBasetype().isClassHandle();
3787 if (!cd ||
((cd
!= ClassDeclaration
.throwable
) && !ClassDeclaration
.throwable
.isBaseOf(cd
, null)))
3789 loc
.error("can only throw class objects derived from `Throwable`, not type `%s`", exp
.type
.toChars());
3795 private extern(D
) Expression
applyOpApply(ForeachStatement fs
, Expression flde
,
3796 Type tab
, Scope
* sc2
, Dsymbol sapply
)
3800 if (global
.params
.useDIP1000
== FeatureState
.enabled
)
3802 message(loc
, "To enforce `@safe`, the compiler allocates a closure unless `opApply()` uses `scope`");
3804 (cast(FuncExp
)flde
).fd
.tookAddressOf
= 1;
3808 if (global
.params
.useDIP1000
== FeatureState
.enabled
)
3809 ++(cast(FuncExp
)flde
).fd
.tookAddressOf
; // allocate a closure unless the opApply() uses 'scope'
3811 assert(tab
.ty
== Tstruct || tab
.ty
== Tclass
);
3817 ec
= new DotIdExp(fs
.loc
, fs
.aggr
, sapply
.ident
);
3818 ec
= new CallExp(fs
.loc
, ec
, flde
);
3819 ec
= ec
.expressionSemantic(sc2
);
3820 if (ec
.op
== EXP
.error
)
3822 if (ec
.type
!= Type
.tint32
)
3824 error(fs
.loc
, "`opApply()` function for `%s` must return an `int`", tab
.toChars());
3830 private extern(D
) Expression
applyDelegate(ForeachStatement fs
, Expression flde
,
3831 Type tab
, Scope
* sc2
)
3837 if (fs
.aggr
.op
== EXP
.delegate_
&& (cast(DelegateExp
)fs
.aggr
).func
.isNested() &&
3838 !(cast(DelegateExp
)fs
.aggr
).func
.needThis())
3840 // https://issues.dlang.org/show_bug.cgi?id=3560
3841 fs
.aggr
= (cast(DelegateExp
)fs
.aggr
).e1
;
3843 ec
= new CallExp(fs
.loc
, fs
.aggr
, flde
);
3844 ec
= ec
.expressionSemantic(sc2
);
3845 if (ec
.op
== EXP
.error
)
3847 if (ec
.type
!= Type
.tint32
)
3849 error(fs
.loc
, "`opApply()` function for `%s` must return an `int`", tab
.toChars());
3855 private extern(D
) Expression
applyArray(ForeachStatement fs
, Expression flde
,
3856 Type tab
, Scope
* sc2
, Type tn
, Type tnv
)
3859 const dim
= fs
.parameters
.length
;
3862 * _aApply(aggr, flde)
3864 static immutable fntab
=
3871 const(size_t
) BUFFER_LEN
= 7 + 1 + 2 + dim
.sizeof
* 3 + 1;
3872 char[BUFFER_LEN
] fdname
;
3877 case Tchar
: flag
= 0; break;
3878 case Twchar
: flag
= 3; break;
3879 case Tdchar
: flag
= 6; break;
3885 case Tchar
: flag
+= 0; break;
3886 case Twchar
: flag
+= 1; break;
3887 case Tdchar
: flag
+= 2; break;
3891 const(char)* r
= (fs
.op
== TOK
.foreach_reverse_
) ?
"R" : "";
3892 int j
= snprintf(fdname
.ptr
, BUFFER_LEN
, "_aApply%s%.*s%llu", r
, 2, fntab
[flag
].ptr
, cast(ulong)dim
);
3893 assert(j
< BUFFER_LEN
);
3895 FuncDeclaration fdapply
;
3897 auto params
= new Parameters();
3898 params
.push(new Parameter(Loc
.initial
, STC
.in_
, tn
.arrayOf(), null, null, null));
3899 auto dgparams
= new Parameters();
3900 dgparams
.push(new Parameter(Loc
.initial
, 0, Type
.tvoidptr
, null, null, null));
3902 dgparams
.push(new Parameter(Loc
.initial
, 0, Type
.tvoidptr
, null, null, null));
3903 dgty
= new TypeDelegate(new TypeFunction(ParameterList(dgparams
), Type
.tint32
, LINK
.d
));
3904 params
.push(new Parameter(Loc
.initial
, 0, dgty
, null, null, null));
3905 fdapply
= FuncDeclaration
.genCfunc(params
, Type
.tint32
, fdname
.ptr
);
3907 if (tab
.isTypeSArray())
3908 fs
.aggr
= fs
.aggr
.castTo(sc2
, tn
.arrayOf());
3909 // paint delegate argument to the type runtime expects
3910 Expression fexp
= flde
;
3911 if (!dgty
.equals(flde
.type
))
3913 fexp
= new CastExp(loc
, flde
, flde
.type
);
3916 ec
= new VarExp(Loc
.initial
, fdapply
, false);
3917 ec
= new CallExp(loc
, ec
, fs
.aggr
, fexp
);
3918 ec
.type
= Type
.tint32
; // don't run semantic() on ec
3922 private extern(D
) Expression
applyAssocArray(ForeachStatement fs
, Expression flde
, Type tab
)
3924 auto taa
= tab
.isTypeAArray();
3926 const dim
= fs
.parameters
.length
;
3928 Parameter p
= (*fs
.parameters
)[0];
3929 bool isRef
= (p
.storageClass
& STC
.ref_
) != 0;
3933 Type ti
= (isRef ? taa
.index
.addMod(MODFlags
.const_
) : taa
.index
);
3934 if (isRef ?
!ti
.constConv(ta
) : !ti
.implicitConvTo(ta
))
3936 error(fs
.loc
, "`foreach`: index must be type `%s`, not `%s`",
3937 ti
.toChars(), ta
.toChars());
3940 p
= (*fs
.parameters
)[1];
3941 isRef
= (p
.storageClass
& STC
.ref_
) != 0;
3944 Type taav
= taa
.nextOf();
3945 if (isRef ?
!taav
.constConv(ta
) : !taav
.implicitConvTo(ta
))
3947 error(fs
.loc
, "`foreach`: value must be type `%s`, not `%s`",
3948 taav
.toChars(), ta
.toChars());
3953 * extern(C) int _aaApply(void*, in size_t, int delegate(void*))
3954 * _aaApply(aggr, keysize, flde)
3956 * extern(C) int _aaApply2(void*, in size_t, int delegate(void*, void*))
3957 * _aaApply2(aggr, keysize, flde)
3959 __gshared FuncDeclaration
* fdapply
= [null, null];
3960 __gshared TypeDelegate
* fldeTy
= [null, null];
3961 ubyte i
= (dim
== 2 ?
1 : 0);
3964 auto params
= new Parameters();
3965 params
.push(new Parameter(Loc
.initial
, 0, Type
.tvoid
.pointerTo(), null, null, null));
3966 params
.push(new Parameter(Loc
.initial
, STC
.const_
, Type
.tsize_t
, null, null, null));
3967 auto dgparams
= new Parameters();
3968 dgparams
.push(new Parameter(Loc
.initial
, 0, Type
.tvoidptr
, null, null, null));
3970 dgparams
.push(new Parameter(Loc
.initial
, 0, Type
.tvoidptr
, null, null, null));
3971 fldeTy
[i
] = new TypeDelegate(new TypeFunction(ParameterList(dgparams
), Type
.tint32
, LINK
.d
));
3972 params
.push(new Parameter(Loc
.initial
, 0, fldeTy
[i
], null, null, null));
3973 fdapply
[i
] = FuncDeclaration
.genCfunc(params
, Type
.tint32
, i ? Id
._aaApply2
: Id
._aaApply
);
3976 auto exps
= new Expressions();
3978 auto keysize
= taa
.index
.size();
3979 if (keysize
== SIZE_INVALID
)
3981 assert(keysize
< keysize
.max
- target
.ptrsize
);
3982 keysize
= (keysize
+ (target
.ptrsize
- 1)) & ~(target
.ptrsize
- 1);
3983 // paint delegate argument to the type runtime expects
3984 Expression fexp
= flde
;
3985 if (!fldeTy
[i
].equals(flde
.type
))
3987 fexp
= new CastExp(fs
.loc
, flde
, flde
.type
);
3988 fexp
.type
= fldeTy
[i
];
3990 exps
.push(new IntegerExp(Loc
.initial
, keysize
, Type
.tsize_t
));
3992 ec
= new VarExp(Loc
.initial
, fdapply
[i
], false);
3993 ec
= new CallExp(fs
.loc
, ec
, exps
);
3994 ec
.type
= Type
.tint32
; // don't run semantic() on ec
3998 private extern(D
) Statement
loopReturn(Expression e
, Statements
* cases
, const ref Loc loc
)
4002 // Easy case, a clean exit from the loop
4003 e
= new CastExp(loc
, e
, Type
.tvoid
); // https://issues.dlang.org/show_bug.cgi?id=13899
4004 return new ExpStatement(loc
, e
);
4006 // Construct a switch statement around the return value
4007 // of the apply function.
4009 auto a
= new Statements();
4011 // default: break; takes care of cases 0 and 1
4012 s
= new BreakStatement(Loc
.initial
, null);
4013 s
= new DefaultStatement(Loc
.initial
, s
);
4017 foreach (i
, c
; *cases
)
4019 s
= new CaseStatement(Loc
.initial
, new IntegerExp(i
+ 2), c
);
4023 s
= new CompoundStatement(loc
, a
);
4024 return new SwitchStatement(loc
, null, e
, s
, false, loc
);
4027 /*************************************
4028 * Turn foreach body into the function literal:
4029 * int delegate(ref T param) { body }
4032 * fs = ForeachStatement
4033 * tfld = type of function literal to be created (type of opApply() function if any), can be null
4035 * Function literal created, as an expression
4038 private FuncExp
foreachBodyToFunction(Scope
* sc
, ForeachStatement fs
, TypeFunction tfld
)
4040 auto params
= new Parameters();
4041 foreach (i
, p
; *fs
.parameters
)
4043 StorageClass
stc = STC
.ref_ |
(p
.storageClass
& STC
.scope_
);
4046 p
.type
= p
.type
.typeSemantic(fs
.loc
, sc
);
4047 p
.type
= p
.type
.addStorageClass(p
.storageClass
);
4050 Parameter prm
= tfld
.parameterList
[i
];
4051 //printf("\tprm = %s%s\n", (prm.storageClass&STC.ref_?"ref ":"").ptr, prm.ident.toChars());
4052 stc = (prm
.storageClass
& STC
.ref_
) |
(p
.storageClass
& STC
.scope_
);
4053 if ((p
.storageClass
& STC
.ref_
) != (prm
.storageClass
& STC
.ref_
))
4055 if (!(prm
.storageClass
& STC
.ref_
))
4057 error(fs
.loc
, "`foreach`: cannot make `%s` `ref`", p
.ident
.toChars());
4062 id
= p
.ident
; // argument copy is not need.
4064 else if (p
.storageClass
& STC
.ref_
)
4066 // default delegate parameters are marked as ref, then
4067 // argument copy is not need.
4072 // Make a copy of the ref argument so it isn't
4075 id
= Identifier
.generateId("__applyArg", cast(int)i
);
4077 Initializer ie
= new ExpInitializer(fs
.loc
, new IdentifierExp(fs
.loc
, id
));
4078 auto v
= new VarDeclaration(fs
.loc
, p
.type
, p
.ident
, ie
);
4079 v
.storage_class |
= STC
.temp |
(stc & STC
.scope_
);
4080 Statement s
= new ExpStatement(fs
.loc
, v
);
4081 fs
._body
= new CompoundStatement(fs
.loc
, s
, fs
._body
);
4083 params
.push(new Parameter(fs
.loc
, stc, p
.type
, id
, null, null));
4085 // https://issues.dlang.org/show_bug.cgi?id=13840
4086 // Throwable nested function inside nothrow function is acceptable.
4087 StorageClass
stc = mergeFuncAttrs(STC
.safe | STC
.pure_ | STC
.nogc
, fs
.func
);
4088 auto tf
= new TypeFunction(ParameterList(params
), Type
.tint32
, LINK
.d
, stc);
4089 fs
.cases
= new Statements();
4090 fs
.gotos
= new ScopeStatements();
4091 auto fld = new FuncLiteralDeclaration(fs
.loc
, fs
.endloc
, tf
, TOK
.delegate_
, fs
);
4092 fld.fbody
= fs
._body
;
4093 Expression flde
= new FuncExp(fs
.loc
, fld);
4094 flde
= flde
.expressionSemantic(sc
);
4095 fld.tookAddressOf
= 0;
4096 if (flde
.op
== EXP
.error
)
4098 return cast(FuncExp
)flde
;
4102 void catchSemantic(Catch c
, Scope
* sc
)
4104 //printf("Catch::semantic(%s)\n", ident.toChars());
4106 if (sc
.os
&& sc
.os
.tok
!= TOK
.onScopeFailure
)
4108 // If enclosing is scope(success) or scope(exit), this will be placed in finally block.
4109 error(c
.loc
, "cannot put `catch` statement inside `%s`", Token
.toChars(sc
.os
.tok
));
4114 /* This is because the _d_local_unwind() gets the stack munged
4115 * up on this. The workaround is to place any try-catches into
4116 * a separate function, and call that.
4117 * To fix, have the compiler automatically convert the finally
4118 * body into a nested function.
4120 error(c
.loc
, "cannot put `catch` statement inside `finally` block");
4124 auto sym
= new ScopeDsymbol();
4125 sym
.parent
= sc
.scopesym
;
4130 error(c
.loc
, "`catch` statement without an exception specification is deprecated");
4131 errorSupplemental(c
.loc
, "use `catch(Throwable)` for old behavior");
4134 // reference .object.Throwable
4135 c
.type
= getThrowable();
4137 else if (!c
.type
.isNaked() && !c
.type
.isConst())
4139 // @@@DEPRECATED_2.115@@@
4140 // Deprecated in 2.105, change into an error & uncomment assign in 2.115
4141 deprecation(c
.loc
, "can only catch mutable or const qualified types, not `%s`", c
.type
.toChars());
4144 c
.type
= c
.type
.typeSemantic(c
.loc
, sc
);
4145 if (c
.type
== Type
.terror
)
4153 auto cd
= c
.type
.toBasetype().isClassHandle();
4156 error(c
.loc
, "can only catch class objects, not `%s`", c
.type
.toChars());
4159 else if (cd
.isCPPclass())
4161 if (!target
.cpp
.exceptions
)
4163 error(c
.loc
, "catching C++ class objects not supported for this target");
4166 if (!c
.internalCatch
)
4168 if (sc
.setUnsafe(false, c
.loc
, "cannot catch C++ class objects in `@safe` code"))
4172 else if (cd
!= ClassDeclaration
.throwable
&& !ClassDeclaration
.throwable
.isBaseOf(cd
, null))
4174 error(c
.loc
, "can only catch class objects derived from `Throwable`, not `%s`", c
.type
.toChars());
4177 else if (!c
.internalCatch
&& ClassDeclaration
.exception
&&
4178 cd
!= ClassDeclaration
.exception
&& !ClassDeclaration
.exception
.isBaseOf(cd
, null) &&
4179 sc
.setUnsafe(false, c
.loc
,
4180 "can only catch class objects derived from `Exception` in `@safe` code, not `%s`", c
.type
))
4184 else if (global
.params
.ehnogc
)
4189 // DIP1008 requires destruction of the Throwable, even if the user didn't specify an identifier
4190 auto ident
= c
.ident
;
4191 if (!ident
&& global
.params
.ehnogc
)
4192 ident
= Identifier
.generateAnonymousId("var");
4196 c
.var
= new VarDeclaration(c
.loc
, c
.type
, ident
, null, stc);
4197 c
.var
.iscatchvar
= true;
4198 c
.var
.dsymbolSemantic(sc
);
4201 if (global
.params
.ehnogc
&& stc & STC
.scope_
)
4203 /* Add a destructor for c.var
4204 * try { handler } finally { if (!__ctfe) _d_delThrowable(var); }
4206 assert(!c
.var
.edtor
); // ensure we didn't create one in callScopeDtor()
4209 Expression e
= new VarExp(loc
, c
.var
);
4210 e
= new CallExp(loc
, new IdentifierExp(loc
, Id
._d_delThrowable
), e
);
4212 Expression ec
= new IdentifierExp(loc
, Id
.ctfe
);
4213 ec
= new NotExp(loc
, ec
);
4214 Statement s
= new IfStatement(loc
, null, ec
, new ExpStatement(loc
, e
), null, loc
);
4215 c
.handler
= new TryFinallyStatement(loc
, c
.handler
, s
);
4219 c
.handler
= c
.handler
.statementSemantic(sc
);
4220 if (c
.handler
&& c
.handler
.isErrorStatement())
4226 Statement
semanticNoScope(Statement s
, Scope
* sc
)
4228 //printf("Statement::semanticNoScope() %s\n", toChars());
4229 if (!s
.isCompoundStatement() && !s
.isScopeStatement())
4231 s
= new CompoundStatement(s
.loc
, s
); // so scopeCode() gets called
4233 s
= s
.statementSemantic(sc
);
4237 // Same as semanticNoScope(), but do create a new scope
4238 private Statement
semanticScope(Statement s
, Scope
* sc
, Statement sbreak
, Statement scontinue
, Statement tryBody
)
4240 auto sym
= new ScopeDsymbol();
4241 sym
.parent
= sc
.scopesym
;
4242 Scope
* scd
= sc
.push(sym
);
4244 scd
.sbreak
= sbreak
;
4246 scd
.scontinue
= scontinue
;
4248 scd
.tryBody
= tryBody
;
4249 s
= s
.semanticNoScope(scd
);
4255 /****************************************
4256 * If `statement` has code that needs to run in a finally clause
4257 * at the end of the current scope, return that code in the form of
4260 * statement = the statement
4262 * sentry = set to code executed upon entry to the scope
4263 * sexception = set to code executed upon exit from the scope via exception
4264 * sfinally = set to code executed in finally block
4266 * code to be run in the finally clause
4268 Statement
scopeCode(Statement statement
, Scope
* sc
, out Statement sentry
, out Statement sexception
, out Statement sfinally
)
4270 if (auto es
= statement
.isExpStatement())
4272 if (es
.exp
&& es
.exp
.op
== EXP
.declaration
)
4274 auto de = cast(DeclarationExp
)es
.exp
;
4275 auto v
= de.declaration
.isVarDeclaration();
4276 if (v
&& !v
.isDataseg())
4278 if (v
.needsScopeDtor())
4280 sfinally
= new DtorExpStatement(es
.loc
, v
.edtor
, v
);
4281 v
.storage_class |
= STC
.nodtor
; // don't add in dtor again
4288 else if (auto sgs
= statement
.isScopeGuardStatement())
4290 Statement s
= new PeelStatement(sgs
.statement
);
4294 case TOK
.onScopeExit
:
4298 case TOK
.onScopeFailure
:
4302 case TOK
.onScopeSuccess
:
4305 * sentry: bool x = false;
4306 * sexception: x = true;
4307 * sfinally: if (!x) statement;
4309 auto v
= copyToTemp(0, "__os", IntegerExp
.createBool(false));
4310 v
.dsymbolSemantic(sc
);
4311 sentry
= new ExpStatement(statement
.loc
, v
);
4313 Expression e
= IntegerExp
.createBool(true);
4314 e
= new AssignExp(Loc
.initial
, new VarExp(Loc
.initial
, v
), e
);
4315 sexception
= new ExpStatement(Loc
.initial
, e
);
4317 e
= new VarExp(Loc
.initial
, v
);
4318 e
= new NotExp(Loc
.initial
, e
);
4319 sfinally
= new IfStatement(Loc
.initial
, null, e
, s
, null, Loc
.initial
);
4328 else if (auto ls
= statement
.isLabelStatement())
4331 ls
.statement
= ls
.statement
.scopeCode(sc
, sentry
, sexception
, sfinally
);
4338 /*******************
4339 * Type check and unroll `foreach` over an expression tuple as well
4340 * as `static foreach` statements and `static foreach`
4341 * declarations. For `static foreach` statements and `static
4342 * foreach` declarations, the visitor interface is used (and the
4343 * result is written into the `result` field.) For `static
4344 * foreach` declarations, the resulting Dsymbols* are returned
4347 * The unrolled body is wrapped into a
4348 * - UnrolledLoopStatement, for `foreach` over an expression tuple.
4349 * - ForwardingStatement, for `static foreach` statements.
4350 * - ForwardingAttribDeclaration, for `static foreach` declarations.
4352 * `static foreach` variables are declared as `STC.local`, such
4353 * that they are inserted into the local symbol tables of the
4354 * forwarding constructs instead of forwarded. For `static
4355 * foreach` with multiple foreach loop variables whose aggregate
4356 * has been lowered into a sequence of tuples, this function
4357 * expands the tuples into multiple `STC.local` `static foreach`
4360 public auto makeTupleForeach(Scope
* sc
, bool isStatic
, bool isDecl
, ForeachStatement fs
, Dsymbols
* dbody
, bool needExpansion
)
4362 // Voldemort return type
4365 Statement statement
;
4376 result
.statement
= new ErrorStatement();
4381 size_t dim
= fs
.parameters
.length
;
4382 const bool skipCheck
= isStatic
&& needExpansion
;
4383 if (!skipCheck
&& (dim
< 1 || dim
> 2))
4385 error(fs
.loc
, "only one (value) or two (key,value) arguments allowed for sequence `foreach`");
4386 return returnEarly();
4389 Type paramtype
= (*fs
.parameters
)[dim
- 1].type
;
4392 paramtype
= paramtype
.typeSemantic(loc
, sc
);
4393 if (paramtype
.ty
== Terror
)
4395 return returnEarly();
4399 Type tab
= fs
.aggr
.type
.toBasetype();
4400 TypeTuple tuple
= cast(TypeTuple
)tab
;
4402 Statements
* statements
;
4403 Dsymbols
* declarations
;
4405 declarations
= new Dsymbols();
4407 statements
= new Statements();
4409 //printf("aggr: op = %d, %s\n", fs.aggr.op, fs.aggr.toChars());
4412 if (fs
.aggr
.op
== EXP
.tuple
) // expression tuple
4414 te
= cast(TupleExp
)fs
.aggr
;
4417 else if (fs
.aggr
.op
== EXP
.type
) // type tuple
4419 n
= Parameter
.dim(tuple
.arguments
);
4425 size_t k
= (fs
.op
== TOK
.foreach_
) ? j
: n
- 1 - j
;
4426 Expression e
= null;
4431 t
= Parameter
.getNth(tuple
.arguments
, k
).type
;
4432 Parameter p
= (*fs
.parameters
)[0];
4437 decls
= new Dsymbols();
4439 stmts
= new Statements();
4441 const bool skip
= isStatic
&& needExpansion
;
4442 if (!skip
&& dim
== 2)
4445 if (p
.isReference() || p
.isLazy())
4447 error(fs
.loc
, "no storage class for key `%s`", p
.ident
.toChars());
4448 return returnEarly();
4455 p
.type
= Type
.tsize_t
;
4458 p
.type
= p
.type
.typeSemantic(loc
, sc
);
4460 if (!p
.type
.isintegral())
4462 error(fs
.loc
, "foreach: key cannot be of non-integral type `%s`",
4464 return returnEarly();
4467 const length
= te ? te
.exps
.length
: tuple
.arguments
.length
;
4468 IntRange dimrange
= IntRange(SignExtendedNumber(length
))._cast(Type
.tsize_t
);
4469 // https://issues.dlang.org/show_bug.cgi?id=12504
4470 dimrange
.imax
= SignExtendedNumber(dimrange
.imax
.value
-1);
4471 if (!IntRange
.fromType(p
.type
).contains(dimrange
))
4473 error(fs
.loc
, "index type `%s` cannot cover index range 0..%llu",
4474 p
.type
.toChars(), cast(ulong)length
);
4475 return returnEarly();
4477 Initializer ie
= new ExpInitializer(Loc
.initial
, new IntegerExp(k
));
4478 auto var
= new VarDeclaration(loc
, p
.type
, p
.ident
, ie
);
4479 var
.storage_class |
= STC
.foreach_ | STC
.manifest
;
4481 var
.storage_class |
= STC
.local
;
4486 stmts
.push(new ExpStatement(loc
, var
));
4488 p
= (*fs
.parameters
)[1]; // value
4490 /***********************
4491 * Declares a unrolled `foreach` loop variable or a `static foreach` variable.
4494 * storageClass = The storage class of the variable.
4495 * type = The declared type of the variable.
4496 * ident = The name of the variable.
4497 * e = The initializer of the variable (i.e. the current element of the looped over aggregate).
4498 * t = The type of the initializer.
4500 * `true` iff the declaration was successful.
4502 bool declareVariable(StorageClass storageClass
, Type type
, Identifier ident
, Expression e
, Type t
)
4504 if (storageClass
& (STC
.out_ | STC
.lazy_
) ||
4505 storageClass
& STC
.ref_
&& !te
)
4507 error(fs
.loc
, "no storage class for value `%s`", ident
.toChars());
4513 Type tb
= e
.type
.toBasetype();
4515 if (!(storageClass
& STC
.manifest
))
4517 if (isStatic || tb
.ty
== Tfunction || storageClass
& STC
.alias_
)
4519 if (auto ve
= e
.isVarExp())
4521 else if (auto dve
= e
.isDotVarExp())
4524 if (auto te
= e
.isTemplateExp())
4526 else if (auto se
= e
.isScopeExp())
4528 else if (auto fe
= e
.isFuncExp())
4529 ds = fe
.td ? fe
.td
: fe
.fd
;
4530 else if (auto oe
= e
.isOverExp())
4533 else if (storageClass
& STC
.alias_
)
4535 error(fs
.loc
, "`foreach` loop variable cannot be both `enum` and `alias`");
4541 var
= new AliasDeclaration(loc
, ident
, ds);
4542 if (storageClass
& STC
.ref_
)
4544 error(fs
.loc
, "symbol `%s` cannot be `ref`", ds.toChars());
4549 error(fs
.loc
, "cannot specify element type for symbol `%s`", ds.toChars());
4553 else if (e
.op
== EXP
.type
)
4555 var
= new AliasDeclaration(loc
, ident
, e
.type
);
4558 error(fs
.loc
, "cannot specify element type for type `%s`", e
.type
.toChars());
4564 e
= resolveProperties(sc
, e
);
4565 Initializer ie
= new ExpInitializer(Loc
.initial
, e
);
4566 auto v
= new VarDeclaration(loc
, type
, ident
, ie
, storageClass
);
4567 v
.storage_class |
= STC
.foreach_
;
4568 if (storageClass
& STC
.ref_
)
4569 v
.storage_class |
= STC
.ref_
;
4570 if (isStatic || storageClass
&STC
.manifest || e
.isConst() ||
4571 e
.op
== EXP
.string_ ||
4572 e
.op
== EXP
.structLiteral ||
4573 e
.op
== EXP
.arrayLiteral
)
4575 if (v
.storage_class
& STC
.ref_
)
4579 error(fs
.loc
, "constant value `%s` cannot be `ref`", toChars(ie
));
4585 error(fs
.loc
, "constant value `%s` cannot be `ref`", toChars(ie
));
4589 error(fs
.loc
, "constant value `%s` cannot be `ref`", ident
.toChars());
4595 v
.storage_class |
= STC
.manifest
;
4602 var
= new AliasDeclaration(loc
, ident
, t
);
4605 error(fs
.loc
, "cannot specify element type for symbol `%s`", fs
.toChars());
4611 var
.storage_class |
= STC
.local
;
4617 stmts
.push(new ExpStatement(loc
, var
));
4624 if (!declareVariable(p
.storageClass
, p
.type
, p
.ident
, e
, t
))
4626 return returnEarly();
4634 if (!declareVariable(p
.storageClass
, p
.type
, p
.ident
, e
, t
))
4636 return returnEarly();
4640 { // expand tuples into multiple `static foreach` variables.
4642 auto ident
= Identifier
.generateId("__value");
4643 declareVariable(0, e
.type
, ident
, e
, null);
4644 import dmd
.cond
: StaticForeach
;
4645 auto field
= Identifier
.idPool(StaticForeach
.tupleFieldName
.ptr
,StaticForeach
.tupleFieldName
.length
);
4646 Expression access
= new DotIdExp(loc
, e
, field
);
4647 access
= expressionSemantic(access
, sc
);
4648 access
= access
.optimize(WANTvalue
);
4649 if (!tuple
) return returnEarly();
4650 //printf("%s\n",tuple.toChars());
4651 foreach (l
; 0 .. dim
)
4653 auto cp
= (*fs
.parameters
)[l
];
4654 Expression init_
= new IndexExp(loc
, access
, new IntegerExp(loc
, l
, Type
.tsize_t
));
4655 init_
= init_
.expressionSemantic(sc
);
4657 declareVariable(p
.storageClass
, init_
.type
, cp
.ident
, init_
, null);
4665 decls
.append(Dsymbol
.arraySyntaxCopy(dbody
));
4668 stmts
.push(fs
._body
.syntaxCopy());
4669 s
= new CompoundStatement(loc
, stmts
);
4674 s
= new ScopeStatement(loc
, s
, fs
.endloc
);
4678 import dmd
.attrib
: ForwardingAttribDeclaration
;
4679 d
= new ForwardingAttribDeclaration(decls
);
4683 s
= new ForwardingStatement(loc
, s
);
4687 declarations
.push(d
);
4694 Statement res
= new UnrolledLoopStatement(loc
, statements
);
4695 if (LabelStatement ls
= checkLabeledLoop(sc
, fs
))
4696 ls
.gotoTarget
= res
;
4698 res
= new CompoundStatement(loc
, new ExpStatement(te
.e0
.loc
, te
.e0
), res
);
4699 result
.statement
= res
;
4702 result
.decl
= declarations
;
4704 result
.statement
= new CompoundStatement(loc
, statements
);
4709 /*********************************
4710 * Flatten out the scope by presenting `statement`
4711 * as an array of statements.
4713 * statement = the statement to flatten
4716 * The array of `Statements`, or `null` if no flattening necessary
4718 private Statements
* flatten(Statement statement
, Scope
* sc
)
4720 static auto errorStatements()
4722 auto a
= new Statements();
4723 a
.push(new ErrorStatement());
4728 /*compound and expression statements have classes that inherit from them with the same
4729 *flattening behavior, so the isXXX methods won't work
4731 switch(statement
.stmt
)
4734 case STMT
.CompoundDeclaration
:
4735 return (cast(CompoundStatement
)statement
).statements
;
4739 auto es
= cast(ExpStatement
)statement
;
4740 /* https://issues.dlang.org/show_bug.cgi?id=14243
4741 * expand template mixin in statement scope
4742 * to handle variable destructors.
4744 if (!es
.exp ||
!es
.exp
.isDeclarationExp())
4747 Dsymbol d
= es
.exp
.isDeclarationExp().declaration
;
4748 auto tm
= d
.isTemplateMixin();
4752 Expression e
= es
.exp
.expressionSemantic(sc
);
4753 if (e
.op
== EXP
.error || tm
.errors
)
4754 return errorStatements();
4757 Statement s
= toStatement(tm
);
4764 toCBuffer(s
, &buf
, &hgs
);
4765 printf("tm ==> s = %s\n", buf
.peekChars());
4767 auto a
= new Statements();
4771 case STMT
.Forwarding
:
4772 /***********************
4773 * ForwardingStatements are distributed over the flattened
4774 * sequence of statements. This prevents flattening to be
4775 * "blocked" by a ForwardingStatement and is necessary, for
4776 * example, to support generating scope guards with `static
4779 * static foreach(i; 0 .. 10) scope(exit) writeln(i);
4780 * writeln("this is printed first");
4781 * // then, it prints 10, 9, 8, 7, ...
4783 auto fs
= statement
.isForwardingStatement();
4788 sc
= sc
.push(fs
.sym
);
4789 auto a
= fs
.statement
.flatten(sc
);
4795 auto b
= new Statements(a
.length
);
4798 (*b
)[i
] = s ?
new ForwardingStatement(s
.loc
, fs
.sym
, s
) : null;
4802 case STMT
.Conditional
:
4803 auto cs
= statement
.isConditionalStatement();
4806 //printf("ConditionalStatement::flatten()\n");
4807 if (cs
.condition
.include(sc
))
4809 DebugCondition dc
= cs
.condition
.isDebugCondition();
4812 s
= new DebugStatement(cs
.loc
, cs
.ifbody
);
4813 debugThrowWalker(cs
.ifbody
);
4821 auto a
= new Statements();
4825 case STMT
.StaticForeach
:
4826 auto sfs
= statement
.isStaticForeachStatement();
4827 sfs
.sfe
.prepare(sc
);
4828 if (sfs
.sfe
.ready())
4830 Statement s
= makeTupleForeach(sc
, true, false, sfs
.sfe
.aggrfe
, null, sfs
.sfe
.needExpansion
).statement
;
4831 auto result
= s
.flatten(sc
);
4836 result
= new Statements();
4841 return errorStatements();
4844 auto ds = statement
.isDebugStatement();
4845 Statements
* a
= ds.statement ?
ds.statement
.flatten(sc
) : null;
4851 s
= new DebugStatement(ds.loc
, s
);
4856 auto ls
= statement
.isLabelStatement();
4860 Statements
* a
= null;
4861 a
= ls
.statement
.flatten(sc
);
4867 a
.push(new ExpStatement(ls
.loc
, cast(Expression
)null));
4870 // reuse 'this' LabelStatement
4871 ls
.statement
= (*a
)[0];
4876 auto cs
= statement
.isMixinStatement();
4880 if (expressionsToString(buf
, sc
, cs
.exps
))
4881 return errorStatements();
4883 const errors
= global
.errors
;
4884 const len
= buf
.length
;
4886 const str = buf
.extractSlice()[0 .. len
];
4887 const bool doUnittests
= global
.params
.useUnitTests || global
.params
.ddoc
.doOutput || global
.params
.dihdr
.doOutput
;
4888 auto loc
= adjustLocForMixin(str, cs
.loc
, global
.params
.mixinOut
);
4889 scope p
= new Parser
!ASTCodegen(loc
, sc
._module
, str, false, global
.errorSink
, &global
.compileEnv
, doUnittests
);
4890 p
.transitionIn
= global
.params
.v
.vin
;
4893 auto a
= new Statements();
4894 while (p
.token
.value
!= TOK
.endOfFile
)
4896 Statement s
= p
.parseStatement(ParseStatementFlags
.curlyScope
);
4897 if (!s || global
.errors
!= errors
)
4898 return errorStatements();
4907 /***********************************************************
4908 * Convert TemplateMixin members (which are Dsymbols) to Statements.
4910 * s = the symbol to convert to a Statement
4912 * s redone as a Statement
4914 private Statement
toStatement(Dsymbol s
)
4918 if (auto tm
= s
.isTemplateMixin())
4920 auto a
= new Statements();
4921 foreach (m
; *tm
.members
)
4923 if (Statement sx
= toStatement(m
))
4926 result
= new CompoundStatement(tm
.loc
, a
);
4928 else if (s
.isVarDeclaration() ||
4929 s
.isAggregateDeclaration() ||
4930 s
.isFuncDeclaration() ||
4931 s
.isEnumDeclaration() ||
4932 s
.isAliasDeclaration() ||
4933 s
.isTemplateDeclaration())
4935 /* Perhaps replace the above with isScopeDsymbol() || isDeclaration()
4937 /* An actual declaration symbol will be converted to DeclarationExp
4938 * with ExpStatement.
4940 auto de = new DeclarationExp(s
.loc
, s
);
4941 de.type
= Type
.tvoid
; // avoid repeated semantic
4942 result
= new ExpStatement(s
.loc
, de);
4944 else if (auto d
= s
.isAttribDeclaration())
4946 /* All attributes have been already picked by the semantic analysis of
4947 * 'bottom' declarations (function, struct, class, etc).
4948 * So we don't have to copy them.
4950 if (Dsymbols
* a
= d
.include(null))
4952 auto statements
= new Statements();
4955 statements
.push(toStatement(sx
));
4957 result
= new CompoundStatement(d
.loc
, statements
);
4960 else if (s
.isStaticAssert() ||
4963 /* Ignore as they are not Statements
4968 .error(Loc
.initial
, "internal compiler error: cannot mixin %s `%s`\n", s
.kind(), s
.toChars());
4969 result
= new ErrorStatement();
4976 Marks all occurring ThrowStatements as internalThrows.
4977 This is intended to be called from a DebugStatement as it allows
4978 to mark all its nodes as nothrow.
4981 s = AST Node to traverse
4983 private void debugThrowWalker(Statement s
)
4986 extern(C
++) final class DebugWalker
: SemanticTimeTransitiveVisitor
4988 alias visit
= SemanticTimeTransitiveVisitor
.visit
;
4991 override void visit(ThrowStatement s
)
4993 s
.internalThrow
= true;
4996 override void visit(CallExp s
)
4998 s
.inDebugStatement
= true;
5002 scope walker
= new DebugWalker();
5006 /***********************************************************
5007 * Evaluate and print a `pragma(msg, args)`
5010 * loc = location for error messages
5011 * sc = scope for argument interpretation
5012 * args = expressions to print
5016 bool pragmaMsgSemantic(Loc loc
, Scope
* sc
, Expressions
* args
)
5020 foreach (arg
; *args
)
5022 sc
= sc
.startCTFE();
5023 auto e
= arg
.expressionSemantic(sc
);
5024 e
= resolveProperties(sc
, e
);
5027 // pragma(msg) is allowed to contain types as well as expressions
5028 e
= ctfeInterpretForPragmaMsg(e
);
5029 if (e
.op
== EXP
.error
)
5031 errorSupplemental(loc
, "while evaluating `pragma(msg, %s)`", arg
.toChars());
5034 if (auto se
= e
.toStringExp())
5036 const slice
= se
.toUTF8(sc
).peekString();
5037 fprintf(stderr
, "%.*s", cast(int)slice
.length
, slice
.ptr
);
5040 fprintf(stderr
, "%s", e
.toChars());
5042 fprintf(stderr
, "\n");
5046 /***********************************************************
5047 * Evaluate `pragma(startAddress, func)` and store the resolved symbol in `args`
5050 * loc = location for error messages
5051 * sc = scope for argument interpretation
5052 * args = pragma arguments
5056 bool pragmaStartAddressSemantic(Loc loc
, Scope
* sc
, Expressions
* args
)
5058 if (!args || args
.length
!= 1)
5060 .error(loc
, "function name expected for start address");
5065 /* https://issues.dlang.org/show_bug.cgi?id=11980
5066 * resolveProperties and ctfeInterpret call are not necessary.
5068 Expression e
= (*args
)[0];
5069 sc
= sc
.startCTFE();
5070 e
= e
.expressionSemantic(sc
);
5071 // e = resolveProperties(sc, e);
5074 // e = e.ctfeInterpret();
5076 Dsymbol sa
= getDsymbol(e
);
5077 if (!sa ||
!sa
.isFuncDeclaration())
5079 .error(loc
, "function name expected for start address, not `%s`", e
.toChars());
5086 /************************************
5087 * Check for skipped variable declarations.
5089 * ss = statement to check
5093 private bool checkLabel(SwitchStatement ss
)
5096 * Checks the scope of a label for existing variable declaration.
5098 * vd = last variable declared before this case/default label
5099 * Returns: `true` if the variables declared in this label would be skipped.
5101 bool checkVar(VarDeclaration vd
)
5103 for (auto v
= vd
; v
&& v
!= ss
.lastVar
; v
= v
.lastVar
)
5105 if (v
.isDataseg() ||
(v
.storage_class
& (STC
.manifest | STC
.temp
) && vd
.ident
!= Id
.withSym
) || v
._init
.isVoidInitializer())
5107 if (vd
.ident
== Id
.withSym
)
5108 error(ss
.loc
, "`switch` skips declaration of `with` temporary");
5110 error(ss
.loc
, "`switch` skips declaration of variable `%s`", v
.toPrettyChars());
5111 errorSupplemental(v
.loc
, "declared here");
5119 if (ss
.sdefault
&& checkVar(ss
.sdefault
.lastVar
))
5120 return !error
; // return error once fully deprecated
5122 foreach (scase
; *ss
.cases
)
5124 if (scase
&& checkVar(scase
.lastVar
))
5125 return !error
; // return error once fully deprecated
5132 * Check for skipped variable declarations.
5134 * gs = statement to check
5135 * Returns: true for error
5137 bool checkLabel(GotoStatement gs
)
5139 if (!gs
.label
.statement
)
5140 return true; // error should have been issued for this already
5142 if (gs
.label
.statement
.os
!= gs
.os
)
5144 if (gs
.os
&& gs
.os
.tok
== TOK
.onScopeFailure
&& !gs
.label
.statement
.os
)
5146 // Jump out from scope(failure) block is allowed.
5150 if (gs
.label
.statement
.os
)
5151 error(gs
.loc
, "cannot `goto` in to `%s` block", Token
.toChars(gs
.label
.statement
.os
.tok
));
5153 error(gs
.loc
, "cannot `goto` out of `%s` block", Token
.toChars(gs
.os
.tok
));
5158 if (gs
.label
.statement
.tf
!= gs
.tf
)
5160 error(gs
.loc
, "cannot `goto` in or out of `finally` block");
5164 if (gs
.label
.statement
.inCtfeBlock
&& !gs
.inCtfeBlock
)
5166 error(gs
.loc
, "cannot `goto` into `if (__ctfe)` block");
5171 for (auto stb
= gs
.tryBody
; stb
!= gs
.label
.statement
.tryBody
; stb
= stbnext
)
5175 error(gs
.loc
, "cannot `goto` into `try` block");
5178 if (auto stf
= stb
.isTryFinallyStatement())
5179 stbnext
= stf
.tryBody
;
5180 else if (auto stc = stb
.isTryCatchStatement())
5181 stbnext
= stc.tryBody
;
5186 VarDeclaration vd
= gs
.label
.statement
.lastVar
;
5187 if (!vd || vd
.isDataseg() ||
(vd
.storage_class
& STC
.manifest
))
5190 VarDeclaration last
= gs
.lastVar
;
5191 while (last
&& last
!= vd
)
5192 last
= last
.lastVar
;
5195 // All good, the label's scope has no variables
5197 else if (vd
.storage_class
& STC
.exptemp
)
5199 // Lifetime ends at end of expression, so no issue with skipping the statement
5203 if (vd
.ident
== Id
.withSym
)
5204 error(gs
.loc
, "`goto` skips declaration of `with` temporary");
5206 error(gs
.loc
, "`goto` skips declaration of variable `%s`", vd
.toPrettyChars());
5207 errorSupplemental(vd
.loc
, "declared here");