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
;
61 import dmd
.common
.outbuffer
;
62 import dmd
.root
.string
;
64 import dmd
.sideeffect
;
66 import dmd
.staticassert
;
74 version = CallbackAPI
;
77 /*****************************************
78 * CTFE requires FuncDeclaration::labtab for the interpretation.
79 * So fixing the label name inside in/out contracts is necessary
80 * for the uniqueness in labtab.
83 * ident = statement label name to be adjusted
87 private Identifier
fixupLabelName(Scope
* sc
, Identifier ident
)
89 uint flags
= (sc
.flags
& SCOPE
.contract
);
90 const id
= ident
.toString();
91 if (flags
&& flags
!= SCOPE
.invariant_
&&
92 !(id
.length
>= 2 && id
[0] == '_' && id
[1] == '_')) // does not start with "__"
95 buf
.writestring(flags
== SCOPE
.require ?
"__in_" : "__out_");
96 buf
.writestring(ident
.toString());
98 ident
= Identifier
.idPool(buf
[]);
103 /*******************************************
104 * Check to see if statement is the innermost labeled statement.
107 * statement = Statement to check
109 * if `true`, then the `LabelStatement`, otherwise `null`
111 private LabelStatement
checkLabeledLoop(Scope
* sc
, Statement statement
) @safe
113 if (sc
.slabel
&& sc
.slabel
.statement
== statement
)
120 /***********************************************************
121 * Check an assignment is used as a condition.
122 * Intended to be use before the `semantic` call on `e`.
124 * e = condition expression which is not yet run semantic analysis.
128 private Expression
checkAssignmentAsCondition(Expression e
, Scope
* sc
)
130 if (sc
.flags
& SCOPE
.Cfile
)
132 auto ec
= lastComma(e
);
133 if (ec
.op
== EXP
.assign
)
135 error(ec
.loc
, "assignment cannot be used as a condition, perhaps `==` was meant?");
136 return ErrorExp
.get();
141 // Performs semantic analysis in Statement AST nodes
142 extern(C
++) Statement
statementSemantic(Statement s
, Scope
* sc
)
146 version (CallbackAPI
)
147 Compiler
.onStatementSemanticStart(s
, sc
);
149 Statement result
= statementSemanticVisit(s
, sc
);
151 version (CallbackAPI
)
152 Compiler
.onStatementSemanticDone(s
, sc
);
158 Statement
statementSemanticVisit(Statement s
, Scope
* sc
)
164 result
= new ErrorStatement();
167 void visitDefaultCase(Statement s
)
172 void visitError(ErrorStatement s
)
177 void visitPeel(PeelStatement s
)
179 /* "peel" off this wrapper, and don't run semantic()
185 void visitExp(ExpStatement s
)
187 /* https://dlang.org/spec/statement.html#expression-statement
195 //printf("ExpStatement::semantic() %s\n", exp.toChars());
197 // Allow CommaExp in ExpStatement because return isn't used
198 CommaExp
.allow(s
.exp
);
200 s
.exp
= s
.exp
.expressionSemantic(sc
);
201 s
.exp
= resolveProperties(sc
, s
.exp
);
202 s
.exp
= s
.exp
.addDtorHook(sc
);
203 if (checkNonAssignmentArrayOp(s
.exp
))
204 s
.exp
= ErrorExp
.get();
205 if (auto f
= isFuncAddress(s
.exp
))
207 if (f
.checkForwardRef(s
.exp
.loc
))
208 s
.exp
= ErrorExp
.get();
210 if (checkMustUse(s
.exp
, sc
))
211 s
.exp
= ErrorExp
.get();
212 if (!(sc
.flags
& SCOPE
.Cfile
) && discardValue(s
.exp
))
213 s
.exp
= ErrorExp
.get();
215 s
.exp
= s
.exp
.optimize(WANTvalue
);
216 s
.exp
= checkGC(sc
, s
.exp
);
217 if (s
.exp
.op
== EXP
.error
)
222 void visitDtorExp(DtorExpStatement s
)
227 void visitMixin(MixinStatement cs
)
229 /* https://dlang.org/spec/statement.html#mixin-statement
232 //printf("MixinStatement::semantic() %s\n", exp.toChars());
233 Statements
* a
= cs
.flatten(sc
);
236 Statement s
= new CompoundStatement(cs
.loc
, a
);
237 result
= s
.statementSemantic(sc
);
240 void visitCompound(CompoundStatement cs
)
242 //printf("CompoundStatement::semantic(this = %p, sc = %p)\n", cs, sc);
245 foreach (i
, s
; cs
.statements
)
248 printf("[%d]: %s", i
, s
.toChars());
252 for (size_t i
= 0; i
< cs
.statements
.length
;)
254 Statement s
= (*cs
.statements
)[i
];
261 if (auto flt
= s
.flatten(sc
))
263 cs
.statements
.remove(i
);
264 cs
.statements
.insert(i
, flt
);
267 s
= s
.statementSemantic(sc
);
268 (*cs
.statements
)[i
] = s
;
271 /* Remove NULL statements from the list.
273 cs
.statements
.remove(i
);
276 if (s
.isErrorStatement())
278 result
= s
; // propagate error up the AST
280 continue; // look for errors in rest of statements
283 // expand tuple variables in order to attach destruction/exception logic
284 if (auto es
= s
.isExpStatement())
286 if (es
.exp
&& es
.exp
.isDeclarationExp())
288 auto de = es
.exp
.isDeclarationExp();
289 auto vd
= de.declaration
.isVarDeclaration();
290 if (vd
&& vd
.aliasTuple
&& vd
.aliasTuple
.objects
.length
)
293 cs
.statements
.insert(i
, vd
.aliasTuple
.objects
.length
- 1, null);
294 vd
.aliasTuple
.foreachVar((v
) { (*cs
.statements
)[j
++] = toStatement(v
); });
295 s
= (*cs
.statements
)[i
];
301 Statement sexception
;
304 (*cs
.statements
)[i
] = s
.scopeCode(sc
, sentry
, sexception
, sfinally
);
307 sentry
= sentry
.statementSemantic(sc
);
308 cs
.statements
.insert(i
, sentry
);
312 sexception
= sexception
.statementSemantic(sc
);
315 /* Returns: true if statements[] are empty statements
317 static bool isEmpty(const Statement
[] statements
)
319 foreach (s
; statements
)
321 if (const cs
= s
.isCompoundStatement())
323 if (!isEmpty((*cs
.statements
)[]))
332 if (!sfinally
&& isEmpty((*cs
.statements
)[i
+ 1 .. cs
.statements
.length
]))
342 * catch (Throwable __o)
343 * { sexception; throw __o; }
345 auto a
= new Statements();
346 a
.pushSlice((*cs
.statements
)[i
+ 1 .. cs
.statements
.length
]);
347 cs
.statements
.setDim(i
+ 1);
349 Statement _body
= new CompoundStatement(Loc
.initial
, a
);
350 _body
= new ScopeStatement(Loc
.initial
, _body
, Loc
.initial
);
352 Identifier id
= Identifier
.generateId("__o");
354 Statement handler
= new PeelStatement(sexception
);
355 if (sexception
.blockExit(sc
.func
, null) & BE
.fallthru
)
357 auto ts
= new ThrowStatement(Loc
.initial
, new IdentifierExp(Loc
.initial
, id
));
358 ts
.internalThrow
= true;
359 handler
= new CompoundStatement(Loc
.initial
, handler
, ts
);
362 auto catches
= new Catches();
363 auto ctch
= new Catch(Loc
.initial
, getThrowable(), id
, handler
);
364 ctch
.internalCatch
= true;
367 Statement st
= new TryCatchStatement(Loc
.initial
, _body
, catches
);
369 st
= new TryFinallyStatement(Loc
.initial
, st
, sfinally
);
370 st
= st
.statementSemantic(sc
);
372 cs
.statements
.push(st
);
378 if (0 && i
+ 1 == cs
.statements
.length
)
380 cs
.statements
.push(sfinally
);
387 * s; try { s1; s2; } finally { sfinally; }
389 auto a
= new Statements();
390 a
.pushSlice((*cs
.statements
)[i
+ 1 .. cs
.statements
.length
]);
391 cs
.statements
.setDim(i
+ 1);
393 auto _body
= new CompoundStatement(Loc
.initial
, a
);
394 Statement stf
= new TryFinallyStatement(Loc
.initial
, _body
, sfinally
);
395 stf
= stf
.statementSemantic(sc
);
396 cs
.statements
.push(stf
);
403 /* Flatten them in place
405 void flattenStatements(ref Statements statements
)
407 for (size_t i
= 0; i
< statements
.length
;)
409 if (auto s
= statements
[i
])
411 if (auto flt
= s
.flatten(sc
))
413 statements
.remove(i
);
414 statements
.insert(i
, flt
);
422 /* https://issues.dlang.org/show_bug.cgi?id=11653
423 * 'semantic' may return another CompoundStatement
424 * (eg. CaseRangeStatement), so flatten it here.
426 flattenStatements(*cs
.statements
);
428 foreach (s
; *cs
.statements
)
433 if (auto se
= s
.isErrorStatement())
440 if (cs
.statements
.length
== 1)
442 result
= (*cs
.statements
)[0];
448 void visitUnrolledLoop(UnrolledLoopStatement uls
)
450 //printf("UnrolledLoopStatement::semantic(this = %p, sc = %p)\n", uls, sc);
451 Scope
* scd
= sc
.push();
455 Statement serror
= null;
456 foreach (i
, ref s
; *uls
.statements
)
460 //printf("[%d]: %s\n", i, s.toChars());
461 s
= s
.statementSemantic(scd
);
463 serror
= s
.isErrorStatement();
468 result
= serror ? serror
: uls
;
471 void visitScope(ScopeStatement ss
)
473 //printf("ScopeStatement::semantic(sc = %p)\n", sc);
480 ScopeDsymbol sym
= new ScopeDsymbol();
481 sym
.parent
= sc
.scopesym
;
482 sym
.endlinnum
= ss
.endloc
.linnum
;
485 Statements
* a
= ss
.statement
.flatten(sc
);
488 ss
.statement
= new CompoundStatement(ss
.loc
, a
);
491 ss
.statement
= ss
.statement
.statementSemantic(sc
);
494 if (ss
.statement
.isErrorStatement())
497 result
= ss
.statement
;
502 Statement sexception
;
504 ss
.statement
= ss
.statement
.scopeCode(sc
, sentry
, sexception
, sfinally
);
509 //printf("adding sfinally\n");
510 sfinally
= sfinally
.statementSemantic(sc
);
511 ss
.statement
= new CompoundStatement(ss
.loc
, ss
.statement
, sfinally
);
518 void visitForwarding(ForwardingStatement ss
)
521 for (Scope
* csc
= sc
; !ss
.sym
.parent
; csc
= csc
.enclosing
)
524 ss
.sym
.parent
= csc
.scopesym
;
526 sc
= sc
.push(ss
.sym
);
529 ss
.statement
= ss
.statement
.statementSemantic(sc
);
531 result
= ss
.statement
;
534 void visitWhile(WhileStatement ws
)
536 /* Rewrite as a for(;condition;) loop
537 * https://dlang.org/spec/statement.html#while-statement
539 Expression cond
= ws
.condition
;
540 Statement _body
= ws
._body
;
544 * If the while loop is of form `while(auto a = exp) { loop_body }`,
553 _body
= new IfStatement(ws
.loc
, ws
.param
, ws
.condition
, ws
._body
, new BreakStatement(ws
.loc
, null), ws
.endloc
);
554 cond
= IntegerExp
.createBool(true);
556 Statement s
= new ForStatement(ws
.loc
, null, cond
, null, _body
, ws
.endloc
);
557 s
= s
.statementSemantic(sc
);
561 void visitDo(DoStatement
ds)
563 /* https://dlang.org/spec/statement.html#do-statement
565 const inLoopSave
= sc
.inLoop
;
568 ds._body
= ds._body
.semanticScope(sc
, ds, ds, null);
569 sc
.inLoop
= inLoopSave
;
571 if (ds.condition
.op
== EXP
.dotIdentifier
)
572 (cast(DotIdExp
)ds.condition
).noderef
= true;
574 // check in syntax level
575 ds.condition
= checkAssignmentAsCondition(ds.condition
, sc
);
577 ds.condition
= ds.condition
.expressionSemantic(sc
);
578 ds.condition
= resolveProperties(sc
, ds.condition
);
579 if (checkNonAssignmentArrayOp(ds.condition
))
580 ds.condition
= ErrorExp
.get();
581 ds.condition
= ds.condition
.optimize(WANTvalue
);
582 ds.condition
= checkGC(sc
, ds.condition
);
584 ds.condition
= ds.condition
.toBoolean(sc
);
586 if (ds.condition
.op
== EXP
.error
)
588 if (ds._body
&& ds._body
.isErrorStatement())
597 void visitFor(ForStatement fs
)
599 /* https://dlang.org/spec/statement.html#for-statement
601 //printf("ForStatement::semantic %s\n", fs.toChars());
606 * for (auto v1 = i1, v2 = i2; condition; increment) { ... }
608 * { auto v1 = i1, v2 = i2; for (; condition; increment) { ... } }
614 * for (; condition; increment) { ... }
615 * } finally { v2.~this(); }
616 * } finally { v1.~this(); }
618 auto ainit
= new Statements();
619 ainit
.push(fs
._init
);
622 Statement s
= new CompoundStatement(fs
.loc
, ainit
);
623 s
= new ScopeStatement(fs
.loc
, s
, fs
.endloc
);
624 s
= s
.statementSemantic(sc
);
625 if (!s
.isErrorStatement())
627 if (LabelStatement ls
= checkLabeledLoop(sc
, fs
))
629 fs
.relatedLabeled
= s
;
634 assert(fs
._init
is null);
636 auto sym
= new ScopeDsymbol();
637 sym
.parent
= sc
.scopesym
;
638 sym
.endlinnum
= fs
.endloc
.linnum
;
644 if (fs
.condition
.op
== EXP
.dotIdentifier
)
645 (cast(DotIdExp
)fs
.condition
).noderef
= true;
647 // check in syntax level
648 fs
.condition
= checkAssignmentAsCondition(fs
.condition
, sc
);
650 fs
.condition
= fs
.condition
.expressionSemantic(sc
);
651 fs
.condition
= resolveProperties(sc
, fs
.condition
);
652 if (checkNonAssignmentArrayOp(fs
.condition
))
653 fs
.condition
= ErrorExp
.get();
654 fs
.condition
= fs
.condition
.optimize(WANTvalue
);
655 fs
.condition
= checkGC(sc
, fs
.condition
);
657 fs
.condition
= fs
.condition
.toBoolean(sc
);
661 CommaExp
.allow(fs
.increment
);
662 fs
.increment
= fs
.increment
.expressionSemantic(sc
);
663 fs
.increment
= resolveProperties(sc
, fs
.increment
);
664 // @@@DEPRECATED_2.112@@@
665 // remove gagging and deprecation() to turn deprecation into an error when
666 // deprecation cycle is over
667 const olderrors
= global
.startGagging();
668 discardValue(fs
.increment
);
669 if (global
.endGagging(olderrors
))
670 deprecation(fs
.increment
.loc
, "`%s` has no effect", fs
.increment
.toChars());
671 if (checkNonAssignmentArrayOp(fs
.increment
))
672 fs
.increment
= ErrorExp
.get();
673 fs
.increment
= fs
.increment
.optimize(WANTvalue
);
674 fs
.increment
= checkGC(sc
, fs
.increment
);
680 fs
._body
= fs
._body
.semanticNoScope(sc
);
684 if (fs
.condition
&& fs
.condition
.op
== EXP
.error ||
685 fs
.increment
&& fs
.increment
.op
== EXP
.error ||
686 fs
._body
&& fs
._body
.isErrorStatement())
691 void visitForeach(ForeachStatement fs
)
693 /* https://dlang.org/spec/statement.html#foreach-statement
696 //printf("ForeachStatement::semantic() %p\n", fs);
699 * Issue error if any of the ForeachTypes were not supplied and could not be inferred.
701 * true if error issued
703 static bool checkForArgTypes(ForeachStatement fs
)
706 foreach (p
; *fs
.parameters
)
710 error(fs
.loc
, "cannot infer type for `foreach` variable `%s`, perhaps set it explicitly", p
.ident
.toChars());
711 p
.type
= Type
.terror
;
719 const dim
= fs
.parameters
.length
;
723 fs
.func
= fs
.func
.fes
.func
;
725 VarDeclaration vinit
= null;
726 fs
.aggr
= fs
.aggr
.expressionSemantic(sc
);
727 fs
.aggr
= resolveProperties(sc
, fs
.aggr
);
728 fs
.aggr
= fs
.aggr
.optimize(WANTvalue
);
729 if (fs
.aggr
.op
== EXP
.error
)
731 Expression oaggr
= fs
.aggr
; // remember original for error messages
732 if (fs
.aggr
.type
&& fs
.aggr
.type
.toBasetype().ty
== Tstruct
&&
733 (cast(TypeStruct
)(fs
.aggr
.type
.toBasetype())).sym
.dtor
&&
734 !fs
.aggr
.isTypeExp() && !fs
.aggr
.isLvalue())
736 // https://issues.dlang.org/show_bug.cgi?id=14653
737 // Extend the life of rvalue aggregate till the end of foreach.
738 vinit
= copyToTemp(STC
.rvalue
, "__aggr", fs
.aggr
);
739 vinit
.endlinnum
= fs
.endloc
.linnum
;
740 vinit
.dsymbolSemantic(sc
);
741 fs
.aggr
= new VarExp(fs
.aggr
.loc
, vinit
);
744 /* If aggregate is a vector type, add the .array to make it a static array
747 if (auto tv
= fs
.aggr
.type
.toBasetype().isTypeVector())
749 auto vae
= new VectorArrayExp(fs
.aggr
.loc
, fs
.aggr
);
750 vae
.type
= tv
.basetype
;
754 Dsymbol sapply
= null; // the inferred opApply() or front() function
755 if (!inferForeachAggregate(sc
, fs
.op
== TOK
.foreach_
, fs
.aggr
, sapply
))
759 error(fs
.loc
, "invalid `%s` aggregate `%s` of type `%s`",
760 Token
.toChars(fs
.op
), oaggr
.toChars(), oaggr
.type
.toPrettyChars());
762 if (auto ad
= isAggregate(fs
.aggr
.type
))
764 if (fs
.op
== TOK
.foreach_reverse_
)
766 fs
.loc
.errorSupplemental("`foreach_reverse` works with bidirectional ranges"~
767 " (implementing `back` and `popBack`), aggregates implementing" ~
768 " `opApplyReverse`, or the result of an aggregate's `.tupleof` property");
769 fs
.loc
.errorSupplemental("https://dlang.org/phobos/std_range_primitives.html#isBidirectionalRange");
773 fs
.loc
.errorSupplemental("`foreach` works with input ranges"~
774 " (implementing `front` and `popFront`), aggregates implementing" ~
775 " `opApply`, or the result of an aggregate's `.tupleof` property");
776 fs
.loc
.errorSupplemental("https://dlang.org/phobos/std_range_primitives.html#isInputRange");
783 Dsymbol sapplyOld
= sapply
; // 'sapply' will be NULL if and after 'inferApplyArgTypes' errors
785 /* Check for inference errors
787 if (!inferApplyArgTypes(fs
, sc
, sapply
))
790 Try and extract the parameter count of the opApply callback function, e.g.:
791 int opApply(int delegate(int, float)) => 2 args
793 bool foundMismatch
= false;
794 size_t foreachParamCount
= 0;
797 if (FuncDeclaration fd
= sapplyOld
.isFuncDeclaration())
799 auto fparameters
= fd
.getParameterList();
801 if (fparameters
.length
== 1)
803 // first param should be the callback function
804 Parameter fparam
= fparameters
[0];
805 if ((fparam
.type
.ty
== Tpointer ||
806 fparam
.type
.ty
== Tdelegate
) &&
807 fparam
.type
.nextOf().ty
== Tfunction
)
809 TypeFunction tf
= cast(TypeFunction
)fparam
.type
.nextOf();
810 foreachParamCount
= tf
.parameterList
.length
;
811 foundMismatch
= true;
817 //printf("dim = %d, parameters.length = %d\n", dim, parameters.length);
818 if (foundMismatch
&& dim
!= foreachParamCount
)
820 const(char)* plural
= foreachParamCount
> 1 ?
"s" : "";
821 error(fs
.loc
, "cannot infer argument types, expected %llu argument%s, not %llu",
822 cast(ulong) foreachParamCount
, plural
, cast(ulong) dim
);
825 error(fs
.loc
, "cannot uniquely infer `foreach` argument types");
830 Type tab
= fs
.aggr
.type
.toBasetype();
832 if (tab
.ty
== Ttuple
) // don't generate new scope for tuple loops
834 Statement s
= makeTupleForeach(sc
, false, false, fs
, null, false).statement
;
836 s
= new CompoundStatement(loc
, new ExpStatement(loc
, vinit
), s
);
837 result
= s
.statementSemantic(sc
);
841 auto sym
= new ScopeDsymbol();
842 sym
.parent
= sc
.scopesym
;
843 sym
.endlinnum
= fs
.endloc
.linnum
;
844 auto sc2
= sc
.push(sym
);
847 foreach (Parameter p
; *fs
.parameters
)
849 if (p
.storageClass
& STC
.manifest
)
851 error(fs
.loc
, "cannot declare `enum` loop variables for non-unrolled foreach");
853 if (p
.storageClass
& STC
.alias_
)
855 error(fs
.loc
, "cannot declare `alias` loop variables for non-unrolled foreach");
862 result
= new ErrorStatement();
867 error(fs
.loc
, "cannot infer argument types");
871 void retStmt(Statement s
)
875 s
= s
.statementSemantic(sc2
);
884 if (checkForArgTypes(fs
))
887 TypeFunction tfld
= null;
890 if (auto fdapply
= sapply
.isFuncDeclaration())
892 assert(fdapply
.type
&& fdapply
.type
.isTypeFunction());
893 tfld
= fdapply
.type
.typeSemantic(loc
, sc2
).isTypeFunction();
896 else if (tab
.isTypeDelegate())
898 tfld
= tab
.nextOf().isTypeFunction();
900 //printf("tfld = %s\n", tfld.toChars());
901 if (tfld
.parameterList
.parameters
.length
== 1)
903 Parameter p
= tfld
.parameterList
[0];
904 if (p
.type
&& p
.type
.isTypeDelegate())
906 auto t
= p
.type
.typeSemantic(loc
, sc2
);
907 assert(t
.ty
== Tdelegate
);
908 tfld
= t
.nextOf().isTypeFunction();
914 FuncExp flde
= foreachBodyToFunction(sc2
, fs
, tfld
);
918 // Resolve any forward referenced goto's
919 foreach (ScopeStatement ss
; *fs
.gotos
)
921 GotoStatement gs
= ss
.statement
.isGotoStatement();
922 if (!gs
.label
.statement
)
924 // 'Promote' it to this scope, and replace with a return
926 ss
.statement
= new ReturnStatement(Loc
.initial
, new IntegerExp(fs
.cases
.length
+ 1));
933 e
= new DeclarationExp(loc
, vinit
);
934 e
= e
.expressionSemantic(sc2
);
935 if (e
.op
== EXP
.error
)
943 case Tsarray
: ec
= applyArray (fs
, flde
, tab
, sc2
, tn
, tnv
); break;
944 case Tdelegate
: ec
= applyDelegate (fs
, flde
, tab
, sc2
); break;
945 case Taarray
: ec
= applyAssocArray(fs
, flde
, tab
); break;
946 default: ec
= applyOpApply (fs
, flde
, tab
, sc2
, sapply
); break;
951 e
= Expression
.combine(e
, ec
);
952 return loopReturn(e
, fs
.cases
, loc
);
960 if (checkForArgTypes(fs
))
963 if (dim
< 1 || dim
> 2)
965 error(fs
.loc
, "only one or two arguments for array `foreach`");
969 // Finish semantic on all foreach parameter types.
970 foreach (i
; 0 .. dim
)
972 Parameter p
= (*fs
.parameters
)[i
];
973 p
.type
= p
.type
.typeSemantic(loc
, sc2
);
974 p
.type
= p
.type
.addStorageClass(p
.storageClass
);
977 tn
= tab
.nextOf().toBasetype();
981 Type tindex
= (*fs
.parameters
)[0].type
;
982 if (!tindex
.isintegral())
984 error(fs
.loc
, "foreach: key cannot be of non-integral type `%s`", tindex
.toChars());
987 /* What cases to deprecate implicit conversions for:
988 * 1. foreach aggregate is a dynamic array
989 * 2. foreach body is lowered to _aApply (see special case below).
991 Type tv
= (*fs
.parameters
)[1].type
.toBasetype();
992 if ((tab
.isTypeDArray() ||
993 (tn
.ty
!= tv
.ty
&& tn
.ty
.isSomeChar
&& tv
.ty
.isSomeChar
)) &&
994 !Type
.tsize_t
.implicitConvTo(tindex
))
996 deprecation(fs
.loc
, "foreach: loop index implicitly converted from `size_t` to `%s`",
1001 /* Look for special case of parsing char types out of char type
1004 if (tn
.ty
.isSomeChar
)
1006 int i
= (dim
== 1) ?
0 : 1; // index of value
1007 Parameter p
= (*fs
.parameters
)[i
];
1008 tnv
= p
.type
.toBasetype();
1009 if (tnv
.ty
!= tn
.ty
&& tnv
.ty
.isSomeChar
)
1011 if (p
.storageClass
& STC
.ref_
)
1013 error(fs
.loc
, "`foreach`: value of UTF conversion cannot be `ref`");
1018 p
= (*fs
.parameters
)[0];
1019 if (p
.storageClass
& STC
.ref_
)
1021 error(fs
.loc
, "`foreach`: key cannot be `ref`");
1025 return retStmt(apply());
1032 Parameter p
= (*fs
.parameters
)[0];
1033 fs
.key
= new VarDeclaration(loc
, p
.type
.mutableOf(), Identifier
.generateId("__key"), null);
1034 fs
.key
.storage_class |
= STC
.temp | STC
.foreach_
;
1035 if (fs
.key
.isReference())
1036 fs
.key
.storage_class |
= STC
.nodtor
;
1038 if (p
.storageClass
& STC
.ref_
)
1040 if (fs
.key
.type
.constConv(p
.type
) == MATCH
.nomatch
)
1042 error(fs
.loc
, "key type mismatch, `%s` to `ref %s`",
1043 fs
.key
.type
.toChars(), p
.type
.toChars());
1047 if (auto ta
= tab
.isTypeSArray())
1049 IntRange dimrange
= getIntRange(ta
.dim
);
1050 // https://issues.dlang.org/show_bug.cgi?id=12504
1051 dimrange
.imax
= SignExtendedNumber(dimrange
.imax
.value
-1);
1052 if (!IntRange
.fromType(fs
.key
.type
).contains(dimrange
))
1054 error(fs
.loc
, "index type `%s` cannot cover index range 0..%llu",
1055 p
.type
.toChars(), ta
.dim
.toInteger());
1058 fs
.key
.range
= new IntRange(SignExtendedNumber(0), dimrange
.imax
);
1061 // Now declare the value
1063 Parameter p
= (*fs
.parameters
)[dim
- 1];
1064 fs
.value
= new VarDeclaration(loc
, p
.type
, p
.ident
, null);
1065 fs
.value
.storage_class |
= STC
.foreach_
;
1066 fs
.value
.storage_class |
= p
.storageClass
& (STC
.scope_ | STC
.IOR | STC
.TYPECTOR
);
1067 if (fs
.value
.isReference())
1069 fs
.value
.storage_class |
= STC
.nodtor
;
1071 if (fs
.aggr
.checkModifiable(sc2
, ModifyFlags
.noError
) == Modifiable
.initialization
)
1072 fs
.value
.setInCtorOnly
= true;
1074 Type t
= tab
.nextOf();
1075 if (t
.constConv(p
.type
) == MATCH
.nomatch
)
1077 error(fs
.loc
, "argument type mismatch, `%s` to `ref %s`",
1078 t
.toChars(), p
.type
.toChars());
1084 /* Convert to a ForStatement
1085 * foreach (key, value; a) body =>
1086 * for (T[] tmp = a[], size_t key; key < tmp.length; ++key)
1087 * { T value = tmp[k]; body }
1089 * foreach_reverse (key, value; a) body =>
1090 * for (T[] tmp = a[], size_t key = tmp.length; key--; )
1091 * { T value = tmp[k]; body }
1093 auto id
= Identifier
.generateId("__r");
1094 auto ie
= new ExpInitializer(loc
, new SliceExp(loc
, fs
.aggr
, null, null));
1095 const valueIsRef
= (*fs
.parameters
)[$ - 1].isReference();
1097 if (fs
.aggr
.isArrayLiteralExp() && !valueIsRef
)
1099 auto ale
= fs
.aggr
.isArrayLiteralExp();
1100 size_t edim
= ale
.elements ? ale
.elements
.length
: 0;
1101 auto telem
= (*fs
.parameters
)[dim
- 1].type
;
1103 // https://issues.dlang.org/show_bug.cgi?id=12936
1104 // if telem has been specified explicitly,
1105 // converting array literal elements to telem might make it @nogc.
1106 fs
.aggr
= fs
.aggr
.implicitCastTo(sc
, telem
.sarrayOf(edim
));
1107 if (fs
.aggr
.op
== EXP
.error
)
1110 // for (T[edim] tmp = a, ...)
1111 tmp
= new VarDeclaration(loc
, fs
.aggr
.type
, id
, ie
);
1115 tmp
= new VarDeclaration(loc
, tab
.nextOf().arrayOf(), id
, ie
);
1117 tmp
.storage_class |
= STC
.scope_
;
1119 tmp
.storage_class |
= STC
.temp
;
1121 Expression tmp_length
= new DotIdExp(loc
, new VarExp(loc
, tmp
), Id
.length
);
1125 Identifier idkey
= Identifier
.generateId("__key");
1126 fs
.key
= new VarDeclaration(loc
, Type
.tsize_t
, idkey
, null);
1127 fs
.key
.storage_class |
= STC
.temp
;
1129 else if (fs
.key
.type
.ty
!= Type
.tsize_t
.ty
)
1131 tmp_length
= new CastExp(loc
, tmp_length
, fs
.key
.type
);
1133 if (fs
.op
== TOK
.foreach_reverse_
)
1134 fs
.key
._init
= new ExpInitializer(loc
, tmp_length
);
1136 fs
.key
._init
= new ExpInitializer(loc
, new IntegerExp(loc
, 0, fs
.key
.type
));
1138 auto cs
= new Statements();
1140 cs
.push(new ExpStatement(loc
, vinit
));
1141 cs
.push(new ExpStatement(loc
, tmp
));
1142 cs
.push(new ExpStatement(loc
, fs
.key
));
1143 Statement forinit
= new CompoundDeclarationStatement(loc
, cs
);
1146 if (fs
.op
== TOK
.foreach_reverse_
)
1149 cond
= new PostExp(EXP
.minusMinus
, loc
, new VarExp(loc
, fs
.key
));
1154 cond
= new CmpExp(EXP
.lessThan
, loc
, new VarExp(loc
, fs
.key
), tmp_length
);
1157 Expression increment
= null;
1158 if (fs
.op
== TOK
.foreach_
)
1161 increment
= new AddAssignExp(loc
, new VarExp(loc
, fs
.key
), new IntegerExp(loc
, 1, fs
.key
.type
));
1164 // T value = tmp[key];
1165 IndexExp indexExp
= new IndexExp(loc
, new VarExp(loc
, tmp
), new VarExp(loc
, fs
.key
));
1166 indexExp
.indexIsInBounds
= true; // disabling bounds checking in foreach statements.
1167 fs
.value
._init
= new ExpInitializer(loc
, indexExp
);
1168 Statement
ds = new ExpStatement(loc
, fs
.value
);
1172 Parameter p
= (*fs
.parameters
)[0];
1173 if ((p
.storageClass
& STC
.ref_
) && p
.type
.equals(fs
.key
.type
))
1175 fs
.key
.range
= null;
1176 auto v
= new AliasDeclaration(loc
, p
.ident
, fs
.key
);
1177 fs
._body
= new CompoundStatement(loc
, new ExpStatement(loc
, v
), fs
._body
);
1181 auto ei
= new ExpInitializer(loc
, new IdentifierExp(loc
, fs
.key
.ident
));
1182 auto v
= new VarDeclaration(loc
, p
.type
, p
.ident
, ei
);
1183 v
.storage_class |
= STC
.foreach_ |
(p
.storageClass
& STC
.ref_
);
1184 fs
._body
= new CompoundStatement(loc
, new ExpStatement(loc
, v
), fs
._body
);
1185 if (fs
.key
.range
&& !p
.type
.isMutable())
1187 /* Limit the range of the key to the specified range
1189 v
.range
= new IntRange(fs
.key
.range
.imin
, fs
.key
.range
.imax
- SignExtendedNumber(1));
1193 fs
._body
= new CompoundStatement(loc
, ds, fs
._body
);
1195 Statement s
= new ForStatement(loc
, forinit
, cond
, increment
, fs
._body
, fs
.endloc
);
1196 if (auto ls
= checkLabeledLoop(sc
, fs
)) // https://issues.dlang.org/show_bug.cgi?id=15450
1202 if (fs
.op
== TOK
.foreach_reverse_
)
1203 warning(fs
.loc
, "cannot use `foreach_reverse` with an associative array");
1204 if (checkForArgTypes(fs
))
1207 if (dim
< 1 || dim
> 2)
1209 error(fs
.loc
, "only one or two arguments for associative array `foreach`");
1212 return retStmt(apply());
1216 /* Prefer using opApply, if it exists
1219 return retStmt(apply());
1221 /* Look for range iteration, i.e. the properties
1222 * .empty, .popFront, .popBack, .front and .back
1223 * foreach (e; aggr) { ... }
1225 * for (auto __r = aggr[]; !__r.empty; __r.popFront()) {
1226 * auto e = __r.front;
1230 auto ad
= (tab
.ty
== Tclass
) ?
1231 cast(AggregateDeclaration
)tab
.isTypeClass().sym
:
1232 cast(AggregateDeclaration
)tab
.isTypeStruct().sym
;
1234 Identifier idpopFront
;
1235 if (fs
.op
== TOK
.foreach_
)
1237 idfront
= Id
.Ffront
;
1238 idpopFront
= Id
.FpopFront
;
1243 idpopFront
= Id
.FpopBack
;
1245 auto sfront
= ad
.search(Loc
.initial
, idfront
);
1247 return retStmt(apply());
1249 /* Generate a temporary __r and initialize it with the aggregate.
1253 if (vinit
&& fs
.aggr
.isVarExp() && fs
.aggr
.isVarExp().var
== vinit
)
1256 _init
= new ExpStatement(loc
, vinit
);
1260 r
= copyToTemp(0, "__r", fs
.aggr
);
1261 r
.dsymbolSemantic(sc
);
1262 _init
= new ExpStatement(loc
, r
);
1264 _init
= new CompoundStatement(loc
, new ExpStatement(loc
, vinit
), _init
);
1268 Expression e
= new VarExp(loc
, r
);
1269 e
= new DotIdExp(loc
, e
, Id
.Fempty
);
1270 Expression condition
= new NotExp(loc
, e
);
1273 e
= new VarExp(loc
, r
);
1274 Expression increment
= new CallExp(loc
, new DotIdExp(loc
, e
, idpopFront
));
1276 /* Declaration statement for e:
1277 * auto e = __r.idfront;
1279 e
= new VarExp(loc
, r
);
1280 Expression einit
= new DotIdExp(loc
, e
, idfront
);
1281 Statement makeargs
, forbody
;
1282 bool ignoreRef
= false; // If a range returns a non-ref front we ignore ref on foreach
1285 if (auto fd
= sfront
.isFuncDeclaration())
1287 if (!fd
.functionSemantic())
1288 return rangeError();
1291 else if (auto td
= sfront
.isTemplateDeclaration())
1293 if (auto f
= resolveFuncCall(loc
, sc
, td
, null, tab
, ArgumentList(), FuncResolveFlag
.quiet
))
1296 else if (auto d
= sfront
.toAlias().isDeclaration())
1300 if (!tfront || tfront
.ty
== Terror
)
1301 return rangeError();
1302 if (auto ftt
= tfront
.toBasetype().isTypeFunction())
1304 tfront
= tfront
.toBasetype().nextOf();
1307 // .front() does not return a ref. We ignore ref on foreach arg.
1308 // see https://issues.dlang.org/show_bug.cgi?id=11934
1309 if (tfront
.needsDestruction()) ignoreRef
= true;
1312 if (tfront
.ty
== Tvoid
)
1314 error(fs
.loc
, "`%s.front` is `void` and has no value", oaggr
.toChars());
1320 auto p
= (*fs
.parameters
)[0];
1321 auto ve
= new VarDeclaration(loc
, p
.type
, p
.ident
, new ExpInitializer(loc
, einit
));
1322 ve
.storage_class |
= STC
.foreach_
;
1323 ve
.storage_class |
= p
.storageClass
& (STC
.scope_ | STC
.IOR | STC
.TYPECTOR
);
1326 ve
.storage_class
&= ~STC
.ref_
;
1328 makeargs
= new ExpStatement(loc
, ve
);
1332 auto vd
= copyToTemp(STC
.ref_
, "__front", einit
);
1333 vd
.dsymbolSemantic(sc
);
1334 makeargs
= new ExpStatement(loc
, vd
);
1336 // Resolve inout qualifier of front type
1337 tfront
= tfront
.substWildTo(tab
.mod
);
1339 Expression ve
= new VarExp(loc
, vd
);
1342 auto exps
= new Expressions();
1345 while (exps
.length
< dim
)
1347 pos
= expandAliasThisTuples(exps
, pos
);
1351 if (exps
.length
!= dim
)
1353 const(char)* plural
= exps
.length
> 1 ?
"s" : "";
1354 error(fs
.loc
, "cannot infer argument types, expected %llu argument%s, not %llu",
1355 cast(ulong) exps
.length
, plural
, cast(ulong) dim
);
1359 foreach (i
; 0 .. dim
)
1361 auto p
= (*fs
.parameters
)[i
];
1362 auto exp
= (*exps
)[i
];
1365 printf("[%lu] p = %s %s, exp = %s %s\n", i
,
1366 p
.type ? p
.type
.toChars() : "?", p
.ident
.toChars(),
1367 exp
.type
.toChars(), exp
.toChars());
1372 auto sc
= p
.storageClass
;
1373 if (ignoreRef
) sc
&= ~STC
.ref_
;
1374 p
.type
= p
.type
.addStorageClass(sc
).typeSemantic(loc
, sc2
);
1375 if (!exp
.implicitConvTo(p
.type
))
1377 error(fs
.loc
, "cannot implicilty convert range element of type `%s` to variable `%s` of type `%s`",
1378 exp
.type
.toChars(), p
.toChars(), p
.type
.toChars());
1382 auto var
= new VarDeclaration(loc
, p
.type
, p
.ident
, new ExpInitializer(loc
, exp
));
1383 var
.storage_class |
= STC
.ctfe | STC
.ref_ | STC
.foreach_
;
1384 makeargs
= new CompoundStatement(loc
, makeargs
, new ExpStatement(loc
, var
));
1388 forbody
= new CompoundStatement(loc
, makeargs
, fs
._body
);
1390 Statement s
= new ForStatement(loc
, _init
, condition
, increment
, forbody
, fs
.endloc
);
1391 if (auto ls
= checkLabeledLoop(sc
, fs
))
1396 printf("init: %s\n", toChars(_init
));
1397 printf("condition: %s\n", condition
.toChars());
1398 printf("increment: %s\n", increment
.toChars());
1399 printf("body: %s\n", forbody
.toChars());
1404 if (fs
.op
== TOK
.foreach_reverse_
)
1405 deprecation(fs
.loc
, "cannot use `foreach_reverse` with a delegate");
1406 return retStmt(apply());
1410 error(fs
.loc
, "`foreach`: `%s` is not an aggregate type", fs
.aggr
.type
.toChars());
1415 void visitForeachRange(ForeachRangeStatement fs
)
1417 /* https://dlang.org/spec/statement.html#foreach-range-statement
1420 //printf("ForeachRangeStatement::semantic() %p\n", fs);
1422 fs
.lwr
= fs
.lwr
.expressionSemantic(sc
);
1423 fs
.lwr
= resolveProperties(sc
, fs
.lwr
);
1424 fs
.lwr
= fs
.lwr
.optimize(WANTvalue
);
1427 error(fs
.loc
, "invalid range lower bound `%s`", fs
.lwr
.toChars());
1431 fs
.upr
= fs
.upr
.expressionSemantic(sc
);
1432 fs
.upr
= resolveProperties(sc
, fs
.upr
);
1433 fs
.upr
= fs
.upr
.optimize(WANTvalue
);
1436 error(fs
.loc
, "invalid range upper bound `%s`", fs
.upr
.toChars());
1442 fs
.prm
.type
= fs
.prm
.type
.typeSemantic(loc
, sc
);
1443 fs
.prm
.type
= fs
.prm
.type
.addStorageClass(fs
.prm
.storageClass
);
1444 fs
.lwr
= fs
.lwr
.implicitCastTo(sc
, fs
.prm
.type
);
1446 if (fs
.upr
.implicitConvTo(fs
.prm
.type
) ||
(fs
.prm
.storageClass
& STC
.ref_
))
1448 fs
.upr
= fs
.upr
.implicitCastTo(sc
, fs
.prm
.type
);
1452 // See if upr-1 fits in prm.type
1453 Expression limit
= new MinExp(loc
, fs
.upr
, IntegerExp
.literal
!1);
1454 limit
= limit
.expressionSemantic(sc
);
1455 limit
= limit
.optimize(WANTvalue
);
1456 if (!limit
.implicitConvTo(fs
.prm
.type
))
1458 fs
.upr
= fs
.upr
.implicitCastTo(sc
, fs
.prm
.type
);
1464 /* Must infer types from lwr and upr
1466 Type tlwr
= fs
.lwr
.type
.toBasetype();
1467 if (tlwr
.ty
== Tstruct || tlwr
.ty
== Tclass
)
1469 /* Just picking the first really isn't good enough.
1471 fs
.prm
.type
= fs
.lwr
.type
;
1473 else if (fs
.lwr
.type
== fs
.upr
.type
)
1475 /* Same logic as CondExp ?lwr:upr
1477 fs
.prm
.type
= fs
.lwr
.type
;
1481 scope AddExp ea
= new AddExp(loc
, fs
.lwr
, fs
.upr
);
1482 if (typeCombine(ea
, sc
))
1484 fs
.prm
.type
= ea
.type
;
1488 fs
.prm
.type
= fs
.prm
.type
.addStorageClass(fs
.prm
.storageClass
);
1490 if (fs
.prm
.type
.ty
== Terror || fs
.lwr
.op
== EXP
.error || fs
.upr
.op
== EXP
.error
)
1495 /* Convert to a for loop:
1496 * foreach (key; lwr .. upr) =>
1497 * for (auto key = lwr, auto tmp = upr; key < tmp; ++key)
1499 * foreach_reverse (key; lwr .. upr) =>
1500 * for (auto tmp = lwr, auto key = upr; key-- > tmp;)
1502 auto ie
= new ExpInitializer(loc
, (fs
.op
== TOK
.foreach_
) ? fs
.lwr
: fs
.upr
);
1503 fs
.key
= new VarDeclaration(loc
, fs
.upr
.type
.mutableOf(), Identifier
.generateId("__key"), ie
);
1504 fs
.key
.storage_class |
= STC
.temp
;
1505 SignExtendedNumber lower
= getIntRange(fs
.lwr
).imin
;
1506 SignExtendedNumber upper
= getIntRange(fs
.upr
).imax
;
1509 fs
.key
.range
= new IntRange(lower
, upper
);
1512 Identifier id
= Identifier
.generateId("__limit");
1513 ie
= new ExpInitializer(loc
, (fs
.op
== TOK
.foreach_
) ? fs
.upr
: fs
.lwr
);
1514 auto tmp
= new VarDeclaration(loc
, fs
.upr
.type
, id
, ie
);
1515 tmp
.storage_class |
= STC
.temp
;
1517 auto cs
= new Statements();
1518 // Keep order of evaluation as lwr, then upr
1519 if (fs
.op
== TOK
.foreach_
)
1521 cs
.push(new ExpStatement(loc
, fs
.key
));
1522 cs
.push(new ExpStatement(loc
, tmp
));
1526 cs
.push(new ExpStatement(loc
, tmp
));
1527 cs
.push(new ExpStatement(loc
, fs
.key
));
1529 Statement forinit
= new CompoundDeclarationStatement(loc
, cs
);
1532 if (fs
.op
== TOK
.foreach_reverse_
)
1534 cond
= new PostExp(EXP
.minusMinus
, loc
, new VarExp(loc
, fs
.key
));
1535 if (fs
.prm
.type
.isscalar())
1538 cond
= new CmpExp(EXP
.greaterThan
, loc
, cond
, new VarExp(loc
, tmp
));
1543 cond
= new EqualExp(EXP
.notEqual
, loc
, cond
, new VarExp(loc
, tmp
));
1548 if (fs
.prm
.type
.isscalar())
1551 cond
= new CmpExp(EXP
.lessThan
, loc
, new VarExp(loc
, fs
.key
), new VarExp(loc
, tmp
));
1556 cond
= new EqualExp(EXP
.notEqual
, loc
, new VarExp(loc
, fs
.key
), new VarExp(loc
, tmp
));
1560 Expression increment
= null;
1561 if (fs
.op
== TOK
.foreach_
)
1564 //increment = new AddAssignExp(loc, new VarExp(loc, fs.key), IntegerExp.literal!1);
1565 increment
= new PreExp(EXP
.prePlusPlus
, loc
, new VarExp(loc
, fs
.key
));
1567 if ((fs
.prm
.storageClass
& STC
.ref_
) && fs
.prm
.type
.equals(fs
.key
.type
))
1569 fs
.key
.range
= null;
1570 auto v
= new AliasDeclaration(loc
, fs
.prm
.ident
, fs
.key
);
1571 fs
._body
= new CompoundStatement(loc
, new ExpStatement(loc
, v
), fs
._body
);
1575 ie
= new ExpInitializer(loc
, new CastExp(loc
, new VarExp(loc
, fs
.key
), fs
.prm
.type
));
1576 auto v
= new VarDeclaration(loc
, fs
.prm
.type
, fs
.prm
.ident
, ie
);
1577 v
.storage_class |
= STC
.temp | STC
.foreach_ |
(fs
.prm
.storageClass
& STC
.ref_
);
1578 fs
._body
= new CompoundStatement(loc
, new ExpStatement(loc
, v
), fs
._body
);
1579 if (fs
.key
.range
&& !fs
.prm
.type
.isMutable())
1581 /* Limit the range of the key to the specified range
1583 v
.range
= new IntRange(fs
.key
.range
.imin
, fs
.key
.range
.imax
- SignExtendedNumber(1));
1586 if (fs
.prm
.storageClass
& STC
.ref_
)
1588 if (fs
.key
.type
.constConv(fs
.prm
.type
) == MATCH
.nomatch
)
1590 error(fs
.loc
, "argument type mismatch, `%s` to `ref %s`", fs
.key
.type
.toChars(), fs
.prm
.type
.toChars());
1595 auto s
= new ForStatement(loc
, forinit
, cond
, increment
, fs
._body
, fs
.endloc
);
1596 if (LabelStatement ls
= checkLabeledLoop(sc
, fs
))
1598 result
= s
.statementSemantic(sc
);
1601 void visitIf(IfStatement ifs
)
1603 /* https://dlang.org/spec/statement.html#IfStatement
1606 // check in syntax level
1607 ifs
.condition
= checkAssignmentAsCondition(ifs
.condition
, sc
);
1609 auto sym
= new ScopeDsymbol();
1610 sym
.parent
= sc
.scopesym
;
1611 sym
.endlinnum
= ifs
.endloc
.linnum
;
1612 Scope
* scd
= sc
.push(sym
);
1615 /* Declare prm, which we will set to be the
1616 * result of condition.
1618 auto ei
= new ExpInitializer(ifs
.loc
, ifs
.condition
);
1619 ifs
.match
= new VarDeclaration(ifs
.loc
, ifs
.prm
.type
, ifs
.prm
.ident
, ei
);
1620 ifs
.match
.parent
= scd
.func
;
1621 ifs
.match
.storage_class |
= ifs
.prm
.storageClass
;
1622 ifs
.match
.dsymbolSemantic(scd
);
1624 auto de = new DeclarationExp(ifs
.loc
, ifs
.match
);
1625 auto ve
= new VarExp(ifs
.loc
, ifs
.match
);
1626 ifs
.condition
= new CommaExp(ifs
.loc
, de, ve
);
1627 ifs
.condition
= ifs
.condition
.expressionSemantic(scd
);
1629 if (ifs
.match
.edtor
)
1631 Statement sdtor
= new DtorExpStatement(ifs
.loc
, ifs
.match
.edtor
, ifs
.match
);
1632 sdtor
= new ScopeGuardStatement(ifs
.loc
, TOK
.onScopeExit
, sdtor
);
1633 ifs
.ifbody
= new CompoundStatement(ifs
.loc
, sdtor
, ifs
.ifbody
);
1634 ifs
.match
.storage_class |
= STC
.nodtor
;
1636 // the destructor is always called
1637 // whether the 'ifbody' is executed or not
1638 Statement sdtor2
= new DtorExpStatement(ifs
.loc
, ifs
.match
.edtor
, ifs
.match
);
1640 ifs
.elsebody
= new CompoundStatement(ifs
.loc
, sdtor2
, ifs
.elsebody
);
1642 ifs
.elsebody
= sdtor2
;
1647 if (ifs
.condition
.op
== EXP
.dotIdentifier
)
1648 (cast(DotIdExp
)ifs
.condition
).noderef
= true;
1650 ifs
.condition
= ifs
.condition
.expressionSemantic(scd
);
1651 ifs
.condition
= resolveProperties(scd
, ifs
.condition
);
1652 ifs
.condition
= ifs
.condition
.addDtorHook(scd
);
1654 if (checkNonAssignmentArrayOp(ifs
.condition
))
1655 ifs
.condition
= ErrorExp
.get();
1657 // Convert to boolean after declaring prm so this works:
1658 // if (S prm = S()) {}
1659 // where S is a struct that defines opCast!bool.
1660 ifs
.condition
= ifs
.condition
.toBoolean(scd
);
1662 // If we can short-circuit evaluate the if statement, don't do the
1663 // semantic analysis of the skipped code.
1664 // This feature allows a limited form of conditional compilation.
1665 ifs
.condition
= ifs
.condition
.optimize(WANTvalue
);
1667 // checkGC after optimizing the condition so that
1668 // compile time constants are reduced.
1669 ifs
.condition
= checkGC(scd
, ifs
.condition
);
1671 // Save 'root' of two branches (then and else) at the point where it forks
1672 CtorFlow ctorflow_root
= scd
.ctorflow
.clone();
1674 /* Rewrite `if (!__ctfe) A else B` as `if (__ctfe) B else A`
1678 (notExp
= ifs
.condition
.isNotExp()) !is null &&
1679 notExp
.e1
.isVarExp() &&
1680 notExp
.e1
.isVarExp().var
.ident
== Id
.ctfe
)
1682 ifs
.condition
= notExp
.e1
;
1683 auto sbody
= ifs
.ifbody
;
1684 ifs
.ifbody
= ifs
.elsebody
;
1685 ifs
.elsebody
= sbody
;
1688 /* Detect `if (__ctfe)`
1690 if (ifs
.isIfCtfeBlock())
1692 Scope
* scd2
= scd
.push();
1693 scd2
.flags |
= SCOPE
.ctfeBlock
;
1694 ifs
.ifbody
= ifs
.ifbody
.semanticNoScope(scd2
);
1698 ifs
.ifbody
= ifs
.ifbody
.semanticNoScope(scd
);
1701 CtorFlow ctorflow_then
= sc
.ctorflow
; // move flow results
1702 sc
.ctorflow
= ctorflow_root
; // reset flow analysis back to root
1704 ifs
.elsebody
= ifs
.elsebody
.semanticScope(sc
, null, null, null);
1706 // Merge 'then' results into 'else' results
1707 sc
.merge(ifs
.loc
, ctorflow_then
);
1709 ctorflow_then
.freeFieldinit(); // free extra copy of the data
1711 if (ifs
.condition
.op
== EXP
.error ||
1712 (ifs
.ifbody
&& ifs
.ifbody
.isErrorStatement()) ||
1713 (ifs
.elsebody
&& ifs
.elsebody
.isErrorStatement()))
1720 void visitConditional(ConditionalStatement cs
)
1722 //printf("ConditionalStatement::semantic()\n");
1724 // If we can short-circuit evaluate the if statement, don't do the
1725 // semantic analysis of the skipped code.
1726 // This feature allows a limited form of conditional compilation.
1727 if (cs
.condition
.include(sc
))
1729 DebugCondition dc
= cs
.condition
.isDebugCondition();
1733 sc
.flags |
= SCOPE
.debug_
;
1734 cs
.ifbody
= cs
.ifbody
.statementSemantic(sc
);
1738 cs
.ifbody
= cs
.ifbody
.statementSemantic(sc
);
1744 cs
.elsebody
= cs
.elsebody
.statementSemantic(sc
);
1745 result
= cs
.elsebody
;
1749 void visitPragma(PragmaStatement ps
)
1751 /* https://dlang.org/spec/statement.html#pragma-statement
1753 // Should be merged with PragmaDeclaration
1755 //printf("PragmaStatement::semantic() %s\n", ps.toChars());
1756 //printf("body = %p\n", ps._body);
1757 if (ps
.ident
== Id
.msg
)
1759 if (!pragmaMsgSemantic(ps
.loc
, sc
, ps
.args
))
1762 else if (ps
.ident
== Id
.lib
)
1766 /* Should this be allowed?
1768 error(ps
.loc
, "`pragma(lib)` not allowed as statement");
1773 if (!ps
.args || ps
.args
.length
!= 1)
1775 error(ps
.loc
, "`string` expected for library name");
1780 auto se
= semanticString(sc
, (*ps
.args
)[0], "library name");
1784 if (global
.params
.v
.verbose
)
1786 message("library %.*s", cast(int)se
.len
, se
.string
);
1791 else if (ps
.ident
== Id
.linkerDirective
)
1793 /* Should this be allowed?
1795 error(ps
.loc
, "`pragma(linkerDirective)` not allowed as statement");
1798 else if (ps
.ident
== Id
.startaddress
)
1800 if (!pragmaStartAddressSemantic(ps
.loc
, sc
, ps
.args
))
1803 else if (ps
.ident
== Id
.Pinline
)
1805 if (auto fd
= sc
.func
)
1807 fd
.inlining
= evalPragmaInline(ps
.loc
, sc
, ps
.args
);
1811 error(ps
.loc
, "`pragma(inline)` is not inside a function");
1815 else if (ps
.ident
== Id
.mangle
)
1817 auto es
= ps
._body ? ps
._body
.isExpStatement() : null;
1818 auto de = es ? es
.exp
.isDeclarationExp() : null;
1821 error(ps
.loc
, "`pragma(mangle)` must be attached to a declaration");
1824 const se
= ps
.args
&& (*ps
.args
).length
== 1 ?
semanticString(sc
, (*ps
.args
)[0], "pragma mangle argument") : null;
1827 error(ps
.loc
, "`pragma(mangle)` takes a single argument that must be a string literal");
1830 const cnt
= setMangleOverride(de.declaration
, cast(const(char)[])se
.peekData());
1834 else if (!global
.params
.ignoreUnsupportedPragmas
)
1836 error(ps
.loc
, "unrecognized `pragma(%s)`", ps
.ident
.toChars());
1842 if (ps
.ident
== Id
.msg || ps
.ident
== Id
.startaddress
)
1844 error(ps
.loc
, "`pragma(%s)` is missing a terminating `;`", ps
.ident
.toChars());
1847 ps
._body
= ps
._body
.statementSemantic(sc
);
1852 void visitStaticAssert(StaticAssertStatement s
)
1859 void visitSwitch(SwitchStatement ss
)
1861 /* https://dlang.org/spec/statement.html#switch-statement
1864 //printf("SwitchStatement::semantic(%p)\n", ss);
1865 ss
.tryBody
= sc
.tryBody
;
1869 result
= ss
; // already run
1876 * If the switch statement is of form `switch(auto a = exp) { body }`,
1877 * rewrite to the following inside it's own scope:
1883 auto statements
= new Statements();
1884 auto vardecl
= new VarDeclaration(ss
.param
.loc
,
1887 new ExpInitializer(ss
.condition
.loc
, ss
.condition
),
1888 ss
.param
.storageClass
);
1890 statements
.push(new ExpStatement(ss
.param
.loc
, vardecl
));
1892 ss
.condition
= new VarExp(ss
.param
.loc
, vardecl
, false);
1895 statements
.push(ss
);
1897 Statement s
= new CompoundStatement(ss
.loc
, statements
);
1898 s
= new ScopeStatement(ss
.loc
, s
, ss
.endloc
);
1899 s
= s
.statementSemantic(sc
);
1904 bool conditionError
= false;
1905 ss
.condition
= ss
.condition
.expressionSemantic(sc
);
1906 ss
.condition
= resolveProperties(sc
, ss
.condition
);
1910 while (!ss
.condition
.isErrorExp())
1912 // preserve enum type for final switches
1913 if (ss
.condition
.type
.ty
== Tenum
)
1914 te
= cast(TypeEnum
)ss
.condition
.type
;
1915 if (ss
.condition
.type
.isString())
1917 // If it's not an array, cast it to one
1918 if (ss
.condition
.type
.ty
!= Tarray
)
1920 ss
.condition
= ss
.condition
.implicitCastTo(sc
, ss
.condition
.type
.nextOf().arrayOf());
1922 ss
.condition
.type
= ss
.condition
.type
.constOf();
1925 ss
.condition
= integralPromotions(ss
.condition
, sc
);
1926 if (!ss
.condition
.isErrorExp() && ss
.condition
.type
.isintegral())
1929 auto ad
= isAggregate(ss
.condition
.type
);
1930 if (ad
&& ad
.aliasthis
&& !isRecursiveAliasThis(att
, ss
.condition
.type
))
1932 if (auto e
= resolveAliasThis(sc
, ss
.condition
, true))
1939 if (!ss
.condition
.isErrorExp())
1941 error(ss
.loc
, "`%s` must be of integral or string type, it is a `%s`",
1942 ss
.condition
.toChars(), ss
.condition
.type
.toChars());
1943 conditionError
= true;
1947 if (checkNonAssignmentArrayOp(ss
.condition
))
1948 ss
.condition
= ErrorExp
.get();
1949 ss
.condition
= ss
.condition
.optimize(WANTvalue
);
1950 ss
.condition
= checkGC(sc
, ss
.condition
);
1951 if (ss
.condition
.op
== EXP
.error
)
1952 conditionError
= true;
1954 bool needswitcherror
= false;
1956 ss
.lastVar
= sc
.lastVar
;
1962 ss
.cases
= new CaseStatements();
1963 const inLoopSave
= sc
.inLoop
;
1964 sc
.inLoop
= true; // BUG: should use Scope::mergeCallSuper() for each case instead
1965 ss
._body
= ss
._body
.statementSemantic(sc
);
1966 sc
.inLoop
= inLoopSave
;
1968 if (conditionError ||
(ss
._body
&& ss
._body
.isErrorStatement()))
1974 // Resolve any goto case's with exp
1976 foreach (gcs
; ss
.gotoCases
)
1980 error(gcs
.loc
, "no `case` statement following `goto case;`");
1985 for (Scope
* scx
= sc
; scx
; scx
= scx
.enclosing
)
1989 foreach (cs
; *scx
.sw
.cases
)
1991 if (cs
.exp
.equals(gcs
.exp
))
1998 error(gcs
.loc
, "`case %s` not found", gcs
.exp
.toChars());
2005 Type t
= ss
.condition
.type
;
2007 EnumDeclaration ed
= null;
2008 if (t
&& ((ds = t
.toDsymbol(sc
)) !is null))
2009 ed
= ds.isEnumDeclaration(); // typedef'ed enum
2010 if (!ed
&& te
&& ((ds = te
.toDsymbol(sc
)) !is null))
2011 ed
= ds.isEnumDeclaration();
2012 if (ed
&& ss
.cases
.length
< ed
.members
.length
)
2014 int missingMembers
= 0;
2015 const maxShown
= global
.params
.v
.errorSupplementCount();
2017 foreach (es
; *ed
.members
)
2019 EnumMember em
= es
.isEnumMember();
2022 foreach (cs
; *ss
.cases
)
2024 if (cs
.exp
.equals(em
.value
) ||
(!cs
.exp
.type
.isString() &&
2025 !em
.value
.type
.isString() && cs
.exp
.toInteger() == em
.value
.toInteger()))
2028 if (missingMembers
== 0)
2029 error(ss
.loc
, "missing cases for `enum` members in `final switch`:");
2031 if (missingMembers
< maxShown
)
2032 errorSupplemental(ss
.loc
, "`%s`", em
.toChars());
2036 if (missingMembers
> 0)
2038 if (missingMembers
> maxShown
)
2039 errorSupplemental(ss
.loc
, "... (%d more, -v to show) ...", missingMembers
- maxShown
);
2045 needswitcherror
= true;
2048 ss
.hasDefault
= sc
.sw
.sdefault ||
2049 !(!ss
.isFinal || needswitcherror || global
.params
.useAssert
== CHECKENABLE
.on || sc
.func
.isSafe
);
2052 if (!ss
.isFinal
&& (!ss
._body ||
!ss
._body
.isErrorStatement()) && !(sc
.flags
& SCOPE
.Cfile
))
2053 error(ss
.loc
, "`switch` statement without a `default`; use `final switch` or add `default: assert(0);` or add `default: break;`");
2055 // Generate runtime error if the default is hit
2056 auto a
= new Statements();
2057 CompoundStatement cs
;
2060 if (sc
.flags
& SCOPE
.Cfile
)
2062 s
= new BreakStatement(ss
.loc
, null); // default for C is `default: break;`
2064 else if (!sc
.needsCodegen())
2066 // something for the interpreter to deal with
2067 s
= new ExpStatement(ss
.loc
, new AssertExp(ss
.loc
, IntegerExp
.literal
!0));
2069 else if (global
.params
.useSwitchError
== CHECKENABLE
.on
&&
2070 global
.params
.checkAction
!= CHECKACTION
.halt
)
2072 if (global
.params
.checkAction
== CHECKACTION
.C
)
2074 /* Rewrite as an assert(0) and let e2ir generate
2075 * the call to the C assert failure function
2077 s
= new ExpStatement(ss
.loc
, new AssertExp(ss
.loc
, IntegerExp
.literal
!0));
2081 if (!verifyHookExist(ss
.loc
, *sc
, Id
.__switch_error
, "generating assert messages"))
2084 Expression sl
= new IdentifierExp(ss
.loc
, Id
.empty
);
2085 sl
= new DotIdExp(ss
.loc
, sl
, Id
.object
);
2086 sl
= new DotIdExp(ss
.loc
, sl
, Id
.__switch_error
);
2088 Expressions
* args
= new Expressions(2);
2089 (*args
)[0] = new StringExp(ss
.loc
, ss
.loc
.filename
.toDString());
2090 (*args
)[1] = new IntegerExp(ss
.loc
.linnum
);
2092 sl
= new CallExp(ss
.loc
, sl
, args
);
2093 sl
= sl
.expressionSemantic(sc
);
2095 s
= new SwitchErrorStatement(ss
.loc
, sl
);
2099 s
= new ExpStatement(ss
.loc
, new HaltExp(ss
.loc
));
2102 sc
.sw
.sdefault
= new DefaultStatement(ss
.loc
, s
);
2104 if (ss
._body
.blockExit(sc
.func
, null) & BE
.fallthru
)
2105 a
.push(new BreakStatement(Loc
.initial
, null));
2106 a
.push(sc
.sw
.sdefault
);
2107 cs
= new CompoundStatement(ss
.loc
, a
);
2111 if (!(sc
.flags
& SCOPE
.Cfile
) && ss
.checkLabel())
2118 if (!(ss
.condition
.type
.isString() && sc
.needsCodegen()))
2125 // Transform a switch with string labels into a switch with integer labels.
2127 // The integer value of each case corresponds to the index of each label
2128 // string in the sorted array of label strings.
2130 // The value of the integer condition is obtained by calling the druntime template
2131 // switch(object.__switch(cond, options...)) {0: {...}, 1: {...}, ...}
2133 // We sort a copy of the array of labels because we want to do a binary search in object.__switch,
2134 // without modifying the order of the case blocks here in the compiler.
2136 if (!verifyHookExist(ss
.loc
, *sc
, Id
.__switch
, "switch cases on strings"))
2139 size_t numcases
= 0;
2141 numcases
= ss
.cases
.length
;
2143 for (size_t i
= 0; i
< numcases
; i
++)
2145 CaseStatement cs
= (*ss
.cases
)[i
];
2146 cs
.index
= cast(int)i
;
2149 // Make a copy of all the cases so that qsort doesn't scramble the actual
2150 // data we pass to codegen (the order of the cases in the switch).
2151 CaseStatements
*csCopy
= (*ss
.cases
).copy();
2155 static int sort_compare(in CaseStatement
* x
, in CaseStatement
* y
) @trusted
2157 auto se1
= x
.exp
.isStringExp();
2158 auto se2
= y
.exp
.isStringExp();
2159 return (se1
&& se2
) ? se1
.compare(se2
) : 0;
2161 // Sort cases for efficient lookup
2162 csCopy
.sort
!sort_compare
;
2165 // The actual lowering
2166 auto arguments
= new Expressions();
2167 arguments
.push(ss
.condition
);
2169 auto compileTimeArgs
= new Objects();
2171 // The type & label no.
2172 compileTimeArgs
.push(new TypeExp(ss
.loc
, ss
.condition
.type
.nextOf()));
2174 // The switch labels
2175 foreach (caseString
; *csCopy
)
2177 compileTimeArgs
.push(caseString
.exp
);
2180 Expression sl
= new IdentifierExp(ss
.loc
, Id
.empty
);
2181 sl
= new DotIdExp(ss
.loc
, sl
, Id
.object
);
2182 sl
= new DotTemplateInstanceExp(ss
.loc
, sl
, Id
.__switch
, compileTimeArgs
);
2184 sl
= new CallExp(ss
.loc
, sl
, arguments
);
2185 sl
= sl
.expressionSemantic(sc
);
2189 foreach (c
; *csCopy
)
2191 (*ss
.cases
)[c
.index
].exp
= new IntegerExp(i
++);
2194 //printf("%s\n", ss._body.toChars());
2195 ss
.statementSemantic(sc
);
2201 void visitCase(CaseStatement cs
)
2203 SwitchStatement sw
= sc
.sw
;
2204 bool errors
= false;
2206 //printf("CaseStatement::semantic() %s\n", toChars());
2207 sc
= sc
.startCTFE();
2208 cs
.exp
= cs
.exp
.expressionSemantic(sc
);
2209 cs
.exp
= resolveProperties(sc
, cs
.exp
);
2214 Expression initialExp
= cs
.exp
;
2216 // The switch'ed value has errors and doesn't provide the actual type
2217 // Omit the cast to enable further semantic (exluding the check for matching types)
2218 if (sw
.condition
.type
&& !sw
.condition
.type
.isTypeError())
2219 cs
.exp
= cs
.exp
.implicitCastTo(sc
, sw
.condition
.type
);
2220 cs
.exp
= cs
.exp
.optimize(WANTvalue | WANTexpand
);
2222 Expression e
= cs
.exp
;
2223 // Remove all the casts the user and/or implicitCastTo may introduce
2224 // otherwise we'd sometimes fail the check below.
2225 while (e
.op
== EXP
.cast_
)
2226 e
= (cast(CastExp
)e
).e1
;
2228 /* This is where variables are allowed as case expressions.
2230 if (e
.op
== EXP
.variable
)
2232 VarExp ve
= cast(VarExp
)e
;
2233 VarDeclaration v
= ve
.var
.isVarDeclaration();
2234 Type t
= cs
.exp
.type
.toBasetype();
2235 if (v
&& (t
.isintegral() || t
.ty
== Tclass
))
2237 /* Flag that we need to do special code generation
2238 * for this, i.e. generate a sequence of if-then-else
2242 /* TODO check if v can be uninitialized at that point.
2244 if (!v
.isConst() && !v
.isImmutable())
2246 error(cs
.loc
, "`case` variables have to be `const` or `immutable`");
2251 error(cs
.loc
, "`case` variables not allowed in `final switch` statements");
2255 /* Find the outermost scope `scx` that set `sw`.
2256 * Then search scope `scx` for a declaration of `v`.
2258 for (Scope
* scx
= sc
; scx
; scx
= scx
.enclosing
)
2260 if (scx
.enclosing
&& scx
.enclosing
.sw
== sw
)
2262 assert(scx
.sw
== sw
);
2265 if (!scx
.search(cs
.exp
.loc
, v
.ident
, pscopesym
))
2267 error(cs
.loc
, "`case` variable `%s` declared at %s cannot be declared in `switch` body",
2268 v
.toChars(), v
.loc
.toChars());
2277 cs
.exp
= cs
.exp
.ctfeInterpret();
2279 if (StringExp se
= cs
.exp
.toStringExp())
2281 else if (!cs
.exp
.isIntegerExp() && !cs
.exp
.isErrorExp())
2283 error(cs
.loc
, "`case` expression must be a compile-time `string` or an integral constant, not `%s`", cs
.exp
.toChars());
2288 // // Don't check other cases if this has errors
2289 if (!cs
.exp
.isErrorExp())
2290 foreach (cs2
; *sw
.cases
)
2292 //printf("comparing '%s' with '%s'\n", exp.toChars(), cs.exp.toChars());
2293 if (cs2
.exp
.equals(cs
.exp
))
2295 // https://issues.dlang.org/show_bug.cgi?id=15909
2296 error(cs
.loc
, "duplicate `case %s` in `switch` statement", initialExp
.toChars());
2304 // Resolve any goto case's with no exp to this case statement
2305 for (size_t i
= 0; i
< sw
.gotoCases
.length
;)
2307 GotoCaseStatement gcs
= sw
.gotoCases
[i
];
2311 sw
.gotoCases
.remove(i
); // remove from array
2317 if (sc
.sw
.tf
!= sc
.tf
)
2319 error(cs
.loc
, "`switch` and `case` are in different `finally` blocks");
2322 if (sc
.sw
.tryBody
!= sc
.tryBody
)
2324 error(cs
.loc
, "case cannot be in different `try` block level from `switch`");
2330 error(cs
.loc
, "`case` not in `switch` statement");
2334 sc
.ctorflow
.orCSX(CSX
.label
);
2335 cs
.statement
= cs
.statement
.statementSemantic(sc
);
2336 if (cs
.statement
.isErrorStatement())
2338 result
= cs
.statement
;
2341 if (errors || cs
.exp
.op
== EXP
.error
)
2344 cs
.lastVar
= sc
.lastVar
;
2348 void visitCaseRange(CaseRangeStatement crs
)
2350 SwitchStatement sw
= sc
.sw
;
2353 error(crs
.loc
, "case range not in `switch` statement");
2357 //printf("CaseRangeStatement::semantic() %s\n", toChars());
2358 bool errors
= false;
2361 error(crs
.loc
, "case ranges not allowed in `final switch`");
2365 sc
= sc
.startCTFE();
2366 crs
.first
= crs
.first
.expressionSemantic(sc
);
2367 crs
.first
= resolveProperties(sc
, crs
.first
);
2369 crs
.first
= crs
.first
.implicitCastTo(sc
, sw
.condition
.type
);
2370 crs
.first
= crs
.first
.ctfeInterpret();
2372 sc
= sc
.startCTFE();
2373 crs
.last
= crs
.last
.expressionSemantic(sc
);
2374 crs
.last
= resolveProperties(sc
, crs
.last
);
2376 crs
.last
= crs
.last
.implicitCastTo(sc
, sw
.condition
.type
);
2377 crs
.last
= crs
.last
.ctfeInterpret();
2379 if (crs
.first
.op
== EXP
.error || crs
.last
.op
== EXP
.error || errors
)
2382 crs
.statement
.statementSemantic(sc
);
2386 uinteger_t fval
= crs
.first
.toInteger();
2387 uinteger_t lval
= crs
.last
.toInteger();
2388 if ((crs
.first
.type
.isunsigned() && fval
> lval
) ||
(!crs
.first
.type
.isunsigned() && cast(sinteger_t
)fval
> cast(sinteger_t
)lval
))
2390 error(crs
.loc
, "first `case %s` is greater than last `case %s`", crs
.first
.toChars(), crs
.last
.toChars());
2395 if (lval
- fval
> 256)
2397 error(crs
.loc
, "had %llu cases which is more than 257 cases in case range", 1 + lval
- fval
);
2405 /* This works by replacing the CaseRange with an array of Case's.
2407 * case a: .. case b: s;
2415 auto statements
= new Statements();
2416 for (uinteger_t i
= fval
; i
!= lval
+ 1; i
++)
2418 Statement s
= crs
.statement
;
2419 if (i
!= lval
) // if not last case
2420 s
= new ExpStatement(crs
.loc
, cast(Expression
)null);
2421 Expression e
= new IntegerExp(crs
.loc
, i
, crs
.first
.type
);
2422 Statement cs
= new CaseStatement(crs
.loc
, e
, s
);
2423 statements
.push(cs
);
2425 Statement s
= new CompoundStatement(crs
.loc
, statements
);
2426 sc
.ctorflow
.orCSX(CSX
.label
);
2427 s
= s
.statementSemantic(sc
);
2431 void visitDefault(DefaultStatement
ds)
2433 //printf("DefaultStatement::semantic()\n");
2434 bool errors
= false;
2439 error(ds.loc
, "`switch` statement already has a default");
2442 sc
.sw
.sdefault
= ds;
2444 if (sc
.sw
.tf
!= sc
.tf
)
2446 error(ds.loc
, "`switch` and `default` are in different `finally` blocks");
2449 if (sc
.sw
.tryBody
!= sc
.tryBody
)
2451 error(ds.loc
, "default cannot be in different `try` block level from `switch`");
2456 error(ds.loc
, "`default` statement not allowed in `final switch` statement");
2462 error(ds.loc
, "`default` not in `switch` statement");
2466 sc
.ctorflow
.orCSX(CSX
.label
);
2467 ds.statement
= ds.statement
.statementSemantic(sc
);
2468 if (errors ||
ds.statement
.isErrorStatement())
2471 ds.lastVar
= sc
.lastVar
;
2475 void visitGotoDefault(GotoDefaultStatement gds
)
2477 /* https://dlang.org/spec/statement.html#goto-statement
2483 error(gds
.loc
, "`goto default` not in `switch` statement");
2488 error(gds
.loc
, "`goto default` not allowed in `final switch` statement");
2494 void visitGotoCase(GotoCaseStatement gcs
)
2496 /* https://dlang.org/spec/statement.html#goto-statement
2501 error(gcs
.loc
, "`goto case` not in `switch` statement");
2507 gcs
.exp
= gcs
.exp
.expressionSemantic(sc
);
2508 gcs
.exp
= gcs
.exp
.implicitCastTo(sc
, sc
.sw
.condition
.type
);
2509 gcs
.exp
= gcs
.exp
.optimize(WANTvalue
);
2510 if (gcs
.exp
.op
== EXP
.error
)
2514 sc
.sw
.gotoCases
.push(gcs
);
2518 void visitReturn(ReturnStatement rs
)
2520 /* https://dlang.org/spec/statement.html#return-statement
2523 //printf("ReturnStatement.dsymbolSemantic() %p, %s\n", rs, rs.toChars());
2525 FuncDeclaration fd
= sc
.parent
.isFuncDeclaration();
2527 fd
= fd
.fes
.func
; // fd is now function enclosing foreach
2529 auto tf
= fd
.type
.isTypeFunction();
2531 if (rs
.exp
&& rs
.exp
.isVarExp() && rs
.exp
.isVarExp().var
== fd
.vresult
)
2536 assert(rs
.caseDim
== 0);
2537 sc
.fes
.cases
.push(rs
);
2538 result
= new ReturnStatement(Loc
.initial
, new IntegerExp(sc
.fes
.cases
.length
+ 1));
2543 auto gs
= new GotoStatement(rs
.loc
, Id
.returnLabel
);
2544 gs
.label
= fd
.returnLabel
;
2550 fd
.returns
= new ReturnStatements();
2551 fd
.returns
.push(rs
);
2556 Type tret
= tf
.next
;
2557 Type tbret
= tret ? tret
.toBasetype() : null;
2559 bool inferRef
= (tf
.isref
&& (fd
.storage_class
& STC
.auto_
));
2560 Expression e0
= null;
2562 bool errors
= false;
2563 if (sc
.flags
& SCOPE
.contract
)
2565 error(rs
.loc
, "`return` statements cannot be in contracts");
2570 // @@@DEPRECATED_2.112@@@
2571 // Deprecated in 2.100, transform into an error in 2.112
2572 if (sc
.os
.tok
== TOK
.onScopeFailure
)
2574 deprecation(rs
.loc
, "`return` statements cannot be in `scope(failure)` bodies.");
2575 deprecationSupplemental(rs
.loc
, "Use try-catch blocks for this purpose");
2579 error(rs
.loc
, "`return` statements cannot be in `%s` bodies", Token
.toChars(sc
.os
.tok
));
2585 error(rs
.loc
, "`return` statements cannot be in `finally` bodies");
2589 if (fd
.isCtorDeclaration())
2593 error(rs
.loc
, "cannot return expression from constructor");
2597 // Constructors implicitly do:
2599 rs
.exp
= new ThisExp(Loc
.initial
);
2604 fd
.hasReturnExp |
= (fd
.hasReturnExp
& 1 ?
16 : 1);
2606 FuncLiteralDeclaration
fld = fd
.isFuncLiteralDeclaration();
2608 rs
.exp
= inferType(rs
.exp
, tret
);
2609 else if (fld && fld.treq
)
2610 rs
.exp
= inferType(rs
.exp
, fld.treq
.nextOf().nextOf());
2612 rs
.exp
= rs
.exp
.expressionSemantic(sc
);
2613 rs
.exp
= rs
.exp
.arrayFuncConv(sc
);
2614 // If we're returning by ref, allow the expression to be `shared`
2615 const returnSharedRef
= (tf
.isref
&& (fd
.inferRetType || tret
.isShared()));
2616 rs
.exp
.checkSharedAccess(sc
, returnSharedRef
);
2618 // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
2619 if (rs
.exp
.isTypeExp())
2620 rs
.exp
= resolveAliasThis(sc
, rs
.exp
);
2622 rs
.exp
= resolveProperties(sc
, rs
.exp
);
2623 if (rs
.exp
.checkType())
2624 rs
.exp
= ErrorExp
.get();
2625 if (auto f
= isFuncAddress(rs
.exp
))
2627 if (fd
.inferRetType
&& f
.checkForwardRef(rs
.exp
.loc
))
2628 rs
.exp
= ErrorExp
.get();
2630 if (checkNonAssignmentArrayOp(rs
.exp
))
2631 rs
.exp
= ErrorExp
.get();
2633 // Extract side-effect part
2634 rs
.exp
= Expression
.extractLast(rs
.exp
, e0
);
2635 if (rs
.exp
.isCallExp())
2636 rs
.exp
= valueNoDtor(rs
.exp
);
2638 /* Void-return function can have void / noreturn typed expression
2639 * on return statement.
2641 auto texp
= rs
.exp
.type
;
2642 const convToVoid
= texp
.ty
== Tvoid || texp
.isTypeNoreturn();
2644 if (tbret
&& tbret
.ty
== Tvoid || convToVoid
)
2648 error(rs
.loc
, "cannot return non-void from `void` function");
2650 rs
.exp
= new CastExp(rs
.loc
, rs
.exp
, Type
.tvoid
);
2651 rs
.exp
= rs
.exp
.expressionSemantic(sc
);
2654 // https://issues.dlang.org/show_bug.cgi?id=23063
2655 rs
.exp
= checkNoreturnVarAccess(rs
.exp
);
2657 // @@@DEPRECATED_2.111@@@
2658 const olderrors
= global
.startGagging();
2659 // uncomment to turn deprecation into an error when
2660 // deprecation cycle is over
2661 if (discardValue(rs
.exp
))
2665 if (global
.endGagging(olderrors
))
2666 deprecation(rs
.exp
.loc
, "`%s` has no effect", rs
.exp
.toChars());
2673 e0
= Expression
.combine(e0
, rs
.exp
);
2678 e0
= e0
.optimize(WANTvalue
);
2679 e0
= checkGC(sc
, e0
);
2685 if (fd
.inferRetType
) // infer return type
2689 tf
.next
= rs
.exp
.type
;
2691 else if (!tret
.isTypeError() && !rs
.exp
.type
.equals(tret
))
2693 int m1
= rs
.exp
.type
.implicitConvTo(tret
);
2694 int m2
= tret
.implicitConvTo(rs
.exp
.type
);
2695 //printf("exp.type = %s m2<-->m1 tret %s\n", exp.type.toChars(), tret.toChars());
2696 //printf("m1 = %d, m2 = %d\n", m1, m2);
2702 tf
.next
= rs
.exp
.type
;
2706 else if (!rs
.exp
.isErrorExp())
2708 error(rs
.loc
, "expected return type of `%s`, not `%s`:",
2710 rs
.exp
.type
.toChars());
2711 errorSupplemental((fd
.returns
) ?
(*fd
.returns
)[0].loc
: fd
.loc
,
2712 "Return type of `%s` inferred here.",
2716 tf
.next
= Type
.terror
;
2721 tbret
= tret
.toBasetype();
2724 if (inferRef
) // deduce 'auto ref'
2726 /* Determine "refness" of function return:
2727 * if it's an lvalue, return by ref, else return by value
2728 * https://dlang.org/spec/function.html#auto-ref-functions
2731 void turnOffRef(scope void delegate() supplemental
)
2733 tf
.isref
= false; // return by value
2734 tf
.isreturn
= false; // ignore 'return' attribute, whether explicit or inferred
2735 fd
.storage_class
&= ~STC
.return_
;
2737 // If we previously assumed the function could be ref when
2738 // checking for `shared`, make sure we were right
2739 if (global
.params
.noSharedAccess
== FeatureState
.enabled
&& rs
.exp
.type
.isShared())
2741 .error(fd
.loc
, "%s `%s` function returns `shared` but cannot be inferred `ref`", fd
.kind
, fd
.toPrettyChars
);
2746 if (rs
.exp
.isLvalue())
2748 /* May return by ref
2750 Scope
* sc2
= sc
.push();
2751 sc2
.eSink
= global
.errorSinkNull
;
2752 bool err
= checkReturnEscapeRef(sc2
, rs
.exp
, true);
2756 turnOffRef(() { checkReturnEscapeRef(sc
, rs
.exp
, false); });
2757 else if (!rs
.exp
.type
.constConv(tf
.next
))
2759 () => rs
.loc
.errorSupplemental("cannot implicitly convert `%s` of type `%s` to `%s`",
2760 rs
.exp
.toChars(), rs
.exp
.type
.toChars(), tf
.next
.toChars())
2765 () => rs
.loc
.errorSupplemental("return value `%s` is not an lvalue", rs
.exp
.toChars())
2768 /* The "refness" is determined by all of return statements.
2770 * return 3; return x; // ok, x can be a value
2771 * return x; return 3; // ok, x can be a value
2777 // Type of the returned expression (if any), might've been moved to e0
2778 auto resType
= e0 ? e0
.type
: Type
.tvoid
;
2780 // infer return type
2781 if (fd
.inferRetType
)
2783 // 1. First `return <noreturn exp>?`
2784 // 2. Potentially found a returning branch, update accordingly
2785 if (!tf
.next || tf
.next
.toBasetype().isTypeNoreturn())
2787 tf
.next
= resType
; // infer void or noreturn
2789 // Found an actual return value before
2790 else if (tf
.next
.ty
!= Tvoid
&& !resType
.toBasetype().isTypeNoreturn())
2792 if (!tf
.next
.isTypeError())
2794 error(rs
.loc
, "mismatched function return type inference of `void` and `%s`", tf
.next
.toChars());
2797 tf
.next
= Type
.terror
;
2801 tbret
= tret
.toBasetype();
2804 // https://issues.dlang.org/show_bug.cgi?id=23914
2805 if (inferRef
&& !resType
.isTypeNoreturn()) // deduce 'auto ref'
2808 if (tbret
.ty
!= Tvoid
&& !resType
.isTypeNoreturn()) // if non-void return
2810 if (!tbret
.isTypeError())
2813 error(rs
.loc
, "expected return type of `%s`, not `%s`", tret
.toChars(), resType
.toChars());
2814 else if (tbret
.isTypeNoreturn())
2816 error(rs
.loc
, "cannot return from `noreturn` function");
2817 .errorSupplemental(rs
.loc
,
2818 "Consider adding an endless loop, `assert(0)`, or another `noreturn` expression");
2821 error(rs
.loc
, "`return` expression expected");
2825 else if (fd
.isMain())
2827 // main() returns 0, even if it returns void
2828 rs
.exp
= IntegerExp
.literal
!0;
2832 // If any branches have called a ctor, but this branch hasn't, it's an error
2833 if (sc
.ctorflow
.callSuper
& CSX
.any_ctor
&& !(sc
.ctorflow
.callSuper
& (CSX
.this_ctor | CSX
.super_ctor
)))
2835 error(rs
.loc
, "`return` without calling constructor");
2839 if (sc
.ctorflow
.fieldinit
.length
) // if aggregate fields are being constructed
2841 auto ad
= fd
.isMemberLocal();
2843 foreach (i
, v
; ad
.fields
)
2845 bool mustInit
= (v
.storage_class
& STC
.nodefaultctor || v
.type
.needsNested());
2846 if (mustInit
&& !(sc
.ctorflow
.fieldinit
[i
].csx
& CSX
.this_ctor
))
2848 error(rs
.loc
, "an earlier `return` statement skips field `%s` initialization", v
.toChars());
2853 sc
.ctorflow
.orCSX(CSX
.return_
);
2862 // Send out "case receiver" statement to the foreach.
2864 Statement s
= new ReturnStatement(Loc
.initial
, rs
.exp
);
2865 sc
.fes
.cases
.push(s
);
2867 // Immediately rewrite "this" return statement as:
2868 // return cases.length+1;
2869 rs
.exp
= new IntegerExp(sc
.fes
.cases
.length
+ 1);
2872 result
= new CompoundStatement(rs
.loc
, new ExpStatement(rs
.loc
, e0
), rs
);
2880 fd
.buildResultVar(null, rs
.exp
.type
);
2881 bool r
= fd
.vresult
.checkNestedReference(sc
, Loc
.initial
);
2882 assert(!r
); // vresult should be always accessible
2884 // Send out "case receiver" statement to the foreach.
2886 Statement s
= new ReturnStatement(Loc
.initial
, new VarExp(Loc
.initial
, fd
.vresult
));
2887 sc
.fes
.cases
.push(s
);
2889 // Save receiver index for the later rewriting from:
2892 // vresult = exp; retrun caseDim;
2893 rs
.caseDim
= sc
.fes
.cases
.length
+ 1;
2899 fd
.returns
= new ReturnStatements();
2900 fd
.returns
.push(rs
);
2904 if (e0
.isDeclarationExp() || e0
.isCommaExp())
2906 rs
.exp
= Expression
.combine(e0
, rs
.exp
);
2910 auto es
= new ExpStatement(rs
.loc
, e0
);
2911 if (e0
.type
.isTypeNoreturn())
2912 result
= es
; // Omit unreachable return;
2914 result
= new CompoundStatement(rs
.loc
, es
, rs
);
2922 void visitBreak(BreakStatement bs
)
2924 /* https://dlang.org/spec/statement.html#break-statement
2927 //printf("BreakStatement::semantic()\n");
2930 // break Identifier;
2933 bs
.ident
= fixupLabelName(sc
, bs
.ident
);
2935 FuncDeclaration thisfunc
= sc
.func
;
2937 for (Scope
* scx
= sc
; scx
; scx
= scx
.enclosing
)
2939 if (scx
.func
!= thisfunc
) // if in enclosing function
2941 if (sc
.fes
) // if this is the body of a foreach
2943 /* Post this statement to the fes, and replace
2944 * it with a return value that caller will put into
2945 * a switch. Caller will figure out where the break
2946 * label actually is.
2947 * Case numbers start with 2, not 0, as 0 is continue
2950 sc
.fes
.cases
.push(bs
);
2951 result
= new ReturnStatement(Loc
.initial
, new IntegerExp(sc
.fes
.cases
.length
+ 1));
2954 break; // can't break to it
2957 LabelStatement ls
= scx
.slabel
;
2958 if (ls
&& ls
.ident
== bs
.ident
)
2960 Statement s
= ls
.statement
;
2961 if (!s ||
!s
.hasBreak())
2962 error(bs
.loc
, "label `%s` has no `break`", bs
.ident
.toChars());
2963 else if (ls
.tf
!= sc
.tf
)
2964 error(bs
.loc
, "cannot break out of `finally` block");
2974 error(bs
.loc
, "enclosing label `%s` for `break` not found", bs
.ident
.toChars());
2977 else if (!sc
.sbreak
)
2979 if (sc
.os
&& sc
.os
.tok
!= TOK
.onScopeFailure
)
2981 error(bs
.loc
, "`break` is not allowed inside `%s` bodies", Token
.toChars(sc
.os
.tok
));
2985 // Replace break; with return 1;
2986 result
= new ReturnStatement(Loc
.initial
, IntegerExp
.literal
!1);
2990 error(bs
.loc
, "`break` is not inside a loop or `switch`");
2993 else if (sc
.sbreak
.isForwardingStatement())
2995 error(bs
.loc
, "must use labeled `break` within `static foreach`");
3000 void visitContinue(ContinueStatement cs
)
3002 /* https://dlang.org/spec/statement.html#continue-statement
3005 //printf("ContinueStatement::semantic() %p\n", cs);
3008 cs
.ident
= fixupLabelName(sc
, cs
.ident
);
3011 FuncDeclaration thisfunc
= sc
.func
;
3013 for (scx
= sc
; scx
; scx
= scx
.enclosing
)
3016 if (scx
.func
!= thisfunc
) // if in enclosing function
3018 if (sc
.fes
) // if this is the body of a foreach
3020 for (; scx
; scx
= scx
.enclosing
)
3023 if (ls
&& ls
.ident
== cs
.ident
&& ls
.statement
== sc
.fes
)
3025 // Replace continue ident; with return 0;
3026 result
= new ReturnStatement(Loc
.initial
, IntegerExp
.literal
!0);
3031 /* Post this statement to the fes, and replace
3032 * it with a return value that caller will put into
3033 * a switch. Caller will figure out where the break
3034 * label actually is.
3035 * Case numbers start with 2, not 0, as 0 is continue
3038 sc
.fes
.cases
.push(cs
);
3039 result
= new ReturnStatement(Loc
.initial
, new IntegerExp(sc
.fes
.cases
.length
+ 1));
3042 break; // can't continue to it
3046 if (ls
&& ls
.ident
== cs
.ident
)
3048 Statement s
= ls
.statement
;
3049 if (!s ||
!s
.hasContinue())
3050 error(cs
.loc
, "label `%s` has no `continue`", cs
.ident
.toChars());
3051 else if (ls
.tf
!= sc
.tf
)
3052 error(cs
.loc
, "cannot continue out of `finally` block");
3061 error(cs
.loc
, "enclosing label `%s` for `continue` not found", cs
.ident
.toChars());
3064 else if (!sc
.scontinue
)
3066 if (sc
.os
&& sc
.os
.tok
!= TOK
.onScopeFailure
)
3068 error(cs
.loc
, "`continue` is not allowed inside `%s` bodies", Token
.toChars(sc
.os
.tok
));
3072 // Replace continue; with return 0;
3073 result
= new ReturnStatement(Loc
.initial
, IntegerExp
.literal
!0);
3077 error(cs
.loc
, "`continue` is not inside a loop");
3080 else if (sc
.scontinue
.isForwardingStatement())
3082 error(cs
.loc
, "must use labeled `continue` within `static foreach`");
3087 void visitSynchronized(SynchronizedStatement ss
)
3089 /* https://dlang.org/spec/statement.html#synchronized-statement
3094 ss
.exp
= ss
.exp
.expressionSemantic(sc
);
3095 ss
.exp
= resolveProperties(sc
, ss
.exp
);
3096 ss
.exp
= ss
.exp
.optimize(WANTvalue
);
3097 ss
.exp
= checkGC(sc
, ss
.exp
);
3098 if (ss
.exp
.op
== EXP
.error
)
3101 ss
._body
= ss
._body
.statementSemantic(sc
);
3105 ClassDeclaration cd
= ss
.exp
.type
.isClassHandle();
3108 error(ss
.loc
, "can only `synchronize` on class objects, not `%s`", ss
.exp
.type
.toChars());
3111 else if (cd
.isInterfaceDeclaration())
3113 /* Cast the interface to an object, as the object has the monitor,
3114 * not the interface.
3116 if (!ClassDeclaration
.object
)
3118 error(ss
.loc
, "missing or corrupt object.d");
3122 Type t
= ClassDeclaration
.object
.type
;
3123 t
= t
.typeSemantic(Loc
.initial
, sc
).toBasetype();
3124 assert(t
.ty
== Tclass
);
3126 ss
.exp
= new CastExp(ss
.loc
, ss
.exp
, t
);
3127 ss
.exp
= ss
.exp
.expressionSemantic(sc
);
3133 * _d_monitorenter(tmp);
3134 * try { body } finally { _d_monitorexit(tmp); }
3136 auto tmp
= copyToTemp(0, "__sync", ss
.exp
);
3137 tmp
.dsymbolSemantic(sc
);
3139 auto cs
= new Statements();
3140 cs
.push(new ExpStatement(ss
.loc
, tmp
));
3142 auto args
= new Parameters();
3143 args
.push(new Parameter(Loc
.initial
, 0, ClassDeclaration
.object
.type
, null, null, null));
3145 FuncDeclaration fdenter
= FuncDeclaration
.genCfunc(args
, Type
.tvoid
, Id
.monitorenter
);
3146 Expression e
= new CallExp(ss
.loc
, fdenter
, new VarExp(ss
.loc
, tmp
));
3147 e
.type
= Type
.tvoid
; // do not run semantic on e
3149 cs
.push(new ExpStatement(ss
.loc
, e
));
3150 FuncDeclaration fdexit
= FuncDeclaration
.genCfunc(args
, Type
.tvoid
, Id
.monitorexit
);
3151 e
= new CallExp(ss
.loc
, fdexit
, new VarExp(ss
.loc
, tmp
));
3152 e
.type
= Type
.tvoid
; // do not run semantic on e
3153 Statement s
= new ExpStatement(ss
.loc
, e
);
3154 s
= new TryFinallyStatement(ss
.loc
, ss
._body
, s
);
3157 s
= new CompoundStatement(ss
.loc
, cs
);
3158 result
= s
.statementSemantic(sc
);
3163 /* Generate our own critical section, then rewrite as:
3164 * static shared void* __critsec;
3165 * _d_criticalenter2(&__critsec);
3166 * try { body } finally { _d_criticalexit(__critsec); }
3168 auto id
= Identifier
.generateId("__critsec");
3169 auto t
= Type
.tvoidptr
;
3170 auto tmp
= new VarDeclaration(ss
.loc
, t
, id
, null);
3171 tmp
.storage_class |
= STC
.temp | STC
.shared_ | STC
.static_
;
3172 Expression tmpExp
= new VarExp(ss
.loc
, tmp
);
3174 auto cs
= new Statements();
3175 cs
.push(new ExpStatement(ss
.loc
, tmp
));
3177 /* This is just a dummy variable for "goto skips declaration" error.
3178 * Backend optimizer could remove this unused variable.
3180 auto v
= new VarDeclaration(ss
.loc
, Type
.tvoidptr
, Identifier
.generateId("__sync"), null);
3181 v
.dsymbolSemantic(sc
);
3182 cs
.push(new ExpStatement(ss
.loc
, v
));
3184 auto enterArgs
= new Parameters();
3185 enterArgs
.push(new Parameter(Loc
.initial
, 0, t
.pointerTo(), null, null, null));
3187 FuncDeclaration fdenter
= FuncDeclaration
.genCfunc(enterArgs
, Type
.tvoid
, Id
.criticalenter
, STC
.nothrow_
);
3188 Expression e
= new AddrExp(ss
.loc
, tmpExp
);
3189 e
= e
.expressionSemantic(sc
);
3190 e
= new CallExp(ss
.loc
, fdenter
, e
);
3191 e
.type
= Type
.tvoid
; // do not run semantic on e
3192 cs
.push(new ExpStatement(ss
.loc
, e
));
3194 auto exitArgs
= new Parameters();
3195 exitArgs
.push(new Parameter(Loc
.initial
, 0, t
, null, null, null));
3197 FuncDeclaration fdexit
= FuncDeclaration
.genCfunc(exitArgs
, Type
.tvoid
, Id
.criticalexit
, STC
.nothrow_
);
3198 e
= new CallExp(ss
.loc
, fdexit
, tmpExp
);
3199 e
.type
= Type
.tvoid
; // do not run semantic on e
3200 Statement s
= new ExpStatement(ss
.loc
, e
);
3201 s
= new TryFinallyStatement(ss
.loc
, ss
._body
, s
);
3204 s
= new CompoundStatement(ss
.loc
, cs
);
3205 result
= s
.statementSemantic(sc
);
3209 void visitWith(WithStatement ws
)
3211 /* https://dlang.org/spec/statement.html#with-statement
3217 //printf("WithStatement::semantic()\n");
3218 ws
.exp
= ws
.exp
.expressionSemantic(sc
);
3219 ws
.exp
= resolveProperties(sc
, ws
.exp
);
3220 ws
.exp
= ws
.exp
.optimize(WANTvalue
);
3221 ws
.exp
= checkGC(sc
, ws
.exp
);
3222 if (ws
.exp
.op
== EXP
.error
)
3224 if (ws
.exp
.op
== EXP
.scope_
)
3226 sym
= new WithScopeSymbol(ws
);
3227 sym
.parent
= sc
.scopesym
;
3228 sym
.endlinnum
= ws
.endloc
.linnum
;
3230 else if (ws
.exp
.op
== EXP
.type
)
3232 Dsymbol s
= (cast(TypeExp
)ws
.exp
).type
.toDsymbol(sc
);
3233 if (!s ||
!s
.isScopeDsymbol())
3235 error(ws
.loc
, "`with` type `%s` has no members", ws
.exp
.toChars());
3238 sym
= new WithScopeSymbol(ws
);
3239 sym
.parent
= sc
.scopesym
;
3240 sym
.endlinnum
= ws
.endloc
.linnum
;
3244 Type texp
= ws
.exp
.type
;
3245 Type t
= texp
.toBasetype();
3247 Expression olde
= ws
.exp
;
3248 if (t
.ty
== Tpointer
)
3250 ws
.exp
= new PtrExp(ws
.loc
, ws
.exp
);
3251 ws
.exp
= ws
.exp
.expressionSemantic(sc
);
3253 t
= texp
.toBasetype();
3258 if (t
.isClassHandle())
3260 _init
= new ExpInitializer(ws
.loc
, ws
.exp
);
3261 ws
.wthis
= new VarDeclaration(ws
.loc
, ws
.exp
.type
, Id
.withSym
, _init
);
3262 ws
.wthis
.storage_class |
= STC
.temp
;
3263 ws
.wthis
.dsymbolSemantic(sc
);
3265 sym
= new WithScopeSymbol(ws
);
3266 sym
.parent
= sc
.scopesym
;
3267 sym
.endlinnum
= ws
.endloc
.linnum
;
3269 else if (t
.ty
== Tstruct
)
3271 if (!ws
.exp
.isLvalue())
3275 * auto __withtmp = exp
3282 auto tmp
= copyToTemp(0, "__withtmp", ws
.exp
);
3283 tmp
.dsymbolSemantic(sc
);
3284 auto es
= new ExpStatement(ws
.loc
, tmp
);
3285 ws
.exp
= new VarExp(ws
.loc
, tmp
);
3286 Statement ss
= new ScopeStatement(ws
.loc
, new CompoundStatement(ws
.loc
, es
, ws
), ws
.endloc
);
3287 result
= ss
.statementSemantic(sc
);
3290 Expression e
= ws
.exp
.addressOf();
3291 _init
= new ExpInitializer(ws
.loc
, e
);
3292 ws
.wthis
= new VarDeclaration(ws
.loc
, e
.type
, Id
.withSym
, _init
);
3293 ws
.wthis
.storage_class |
= STC
.temp
;
3294 ws
.wthis
.dsymbolSemantic(sc
);
3295 sym
= new WithScopeSymbol(ws
);
3296 // Need to set the scope to make use of resolveAliasThis
3298 sym
.parent
= sc
.scopesym
;
3299 sym
.endlinnum
= ws
.endloc
.linnum
;
3301 else if (auto tenum
= texp
.isTypeEnum())
3303 ws
.exp
= new TypeExp(ws
.exp
.loc
, tenum
);
3304 sym
= new WithScopeSymbol(ws
);
3305 sym
.parent
= sc
.scopesym
;
3306 sym
.endlinnum
= ws
.endloc
.linnum
;
3310 error(ws
.loc
, "`with` expression types must be enums or aggregates or pointers to them, not `%s`", olde
.type
.toChars());
3320 ws
._body
= ws
._body
.statementSemantic(sc
);
3322 if (ws
._body
&& ws
._body
.isErrorStatement())
3332 // https://dlang.org/spec/statement.html#TryStatement
3333 void visitTryCatch(TryCatchStatement tcs
)
3335 //printf("TryCatchStatement.semantic()\n");
3337 if (!global
.params
.useExceptions
)
3339 error(tcs
.loc
, "cannot use try-catch statements with %s", global
.params
.betterC ?
"-betterC".ptr
: "-nothrow".ptr
);
3343 if (!ClassDeclaration
.throwable
)
3345 error(tcs
.loc
, "cannot use try-catch statements because `object.Throwable` was not declared");
3353 tcs
.tryBody
= sc
.tryBody
; // chain on the in-flight tryBody
3354 tcs
._body
= tcs
._body
.semanticScope(sc
, null, null, tcs
);
3356 /* Even if body is empty, still do semantic analysis on catches
3358 bool catchErrors
= false;
3359 foreach (i
, c
; *tcs
.catches
)
3361 c
.catchSemantic(sc
);
3367 auto cd
= c
.type
.toBasetype().isClassHandle();
3368 flags |
= cd
.isCPPclass() ? FLAGcpp
: FLAGd
;
3370 // Determine if current catch 'hides' any previous catches
3373 Catch cj
= (*tcs
.catches
)[j
];
3374 const si
= c
.loc
.toChars();
3375 const sj
= cj
.loc
.toChars();
3376 if (c
.type
.toBasetype().implicitConvTo(cj
.type
.toBasetype()))
3378 error(tcs
.loc
, "`catch` at %s hides `catch` at %s", sj
, si
);
3386 sc
.func
.hasCatches
= true;
3387 if (flags
== (FLAGcpp | FLAGd
))
3389 error(tcs
.loc
, "cannot mix catching D and C++ exceptions in the same try-catch");
3397 // No actual code in the try (i.e. omitted any conditionally compiled code)
3398 // Could also be extended to check for hasCode
3402 if (tcs
._body
.isErrorStatement())
3408 /* If the try body never throws, we can eliminate any catches
3409 * of recoverable exceptions.
3411 if (!(tcs
._body
.blockExit(sc
.func
, null) & BE
.throw_
) && ClassDeclaration
.exception
)
3413 foreach_reverse (i
; 0 .. tcs
.catches
.length
)
3415 Catch c
= (*tcs
.catches
)[i
];
3417 /* If catch exception type is derived from Exception
3419 if (c
.type
.toBasetype().implicitConvTo(ClassDeclaration
.exception
.type
) &&
3420 (!c
.handler ||
!c
.handler
.comeFrom()) && !(sc
.flags
& SCOPE
.debug_
))
3422 // Remove c from the array of catches
3423 tcs
.catches
.remove(i
);
3428 if (tcs
.catches
.length
== 0)
3430 result
= tcs
._body
.hasCode() ? tcs
._body
: null;
3437 void visitTryFinally(TryFinallyStatement tfs
)
3439 //printf("TryFinallyStatement::semantic()\n");
3440 tfs
.tryBody
= sc
.tryBody
; // chain on in-flight tryBody
3441 tfs
._body
= tfs
._body
.semanticScope(sc
, null, null, tfs
);
3446 sc
.scontinue
= null; // no break or continue out of finally block
3447 tfs
.finalbody
= tfs
.finalbody
.semanticNoScope(sc
);
3452 result
= tfs
.finalbody
;
3461 auto blockexit
= tfs
._body
.blockExit(sc
.func
, null);
3463 // if not worrying about exceptions
3464 if (!(global
.params
.useExceptions
&& ClassDeclaration
.throwable
))
3465 blockexit
&= ~BE
.throw_
; // don't worry about paths that otherwise may throw
3467 // Don't care about paths that halt, either
3468 if ((blockexit
& ~BE
.halt
) == BE
.fallthru
)
3470 result
= new CompoundStatement(tfs
.loc
, tfs
._body
, tfs
.finalbody
);
3473 tfs
.bodyFallsThru
= (blockexit
& BE
.fallthru
) != 0;
3477 void visitScopeGuard(ScopeGuardStatement oss
)
3479 /* https://dlang.org/spec/statement.html#scope-guard-statement
3482 if (oss
.tok
!= TOK
.onScopeExit
)
3484 // https://issues.dlang.org/show_bug.cgi?id=23159
3485 if (!global
.params
.useExceptions
)
3488 error(oss
.loc
, "`%s` cannot be used with `-fno-exceptions`", Token
.toChars(oss
.tok
));
3490 error(oss
.loc
, "`%s` cannot be used with -betterC", Token
.toChars(oss
.tok
));
3494 // scope(success) and scope(failure) are rewritten to try-catch(-finally) statement,
3495 // so the generated catch block cannot be placed in finally block.
3496 // See also Catch::semantic.
3497 if (sc
.os
&& sc
.os
.tok
!= TOK
.onScopeFailure
)
3499 // If enclosing is scope(success) or scope(exit), this will be placed in finally block.
3500 error(oss
.loc
, "cannot put `%s` statement inside `%s`", Token
.toChars(oss
.tok
), Token
.toChars(sc
.os
.tok
));
3505 error(oss
.loc
, "cannot put `%s` statement inside `finally` block", Token
.toChars(oss
.tok
));
3513 if (oss
.tok
!= TOK
.onScopeFailure
)
3515 // Jump out from scope(failure) block is allowed.
3517 sc
.scontinue
= null;
3519 oss
.statement
= oss
.statement
.semanticNoScope(sc
);
3522 if (!oss
.statement || oss
.statement
.isErrorStatement())
3524 result
= oss
.statement
;
3530 void visitThrow(ThrowStatement ts
)
3532 /* https://dlang.org/spec/statement.html#throw-statement
3535 //printf("ThrowStatement::semantic()\n");
3536 if (throwSemantic(ts
.loc
, ts
.exp
, sc
))
3543 void visitDebug(DebugStatement
ds)
3548 sc
.flags |
= SCOPE
.debug_
;
3549 ds.statement
= ds.statement
.statementSemantic(sc
);
3552 result
= ds.statement
;
3555 void visitGoto(GotoStatement gs
)
3557 /* https://dlang.org/spec/statement.html#goto-statement
3560 //printf("GotoStatement::semantic()\n");
3561 FuncDeclaration fd
= sc
.func
;
3563 gs
.ident
= fixupLabelName(sc
, gs
.ident
);
3564 gs
.label
= fd
.searchLabel(gs
.ident
, gs
.loc
);
3565 gs
.tryBody
= sc
.tryBody
;
3568 gs
.lastVar
= sc
.lastVar
;
3569 gs
.inCtfeBlock
= (sc
.flags
& SCOPE
.ctfeBlock
) != 0;
3571 if (!gs
.label
.statement
&& sc
.fes
)
3573 /* Either the goto label is forward referenced or it
3574 * is in the function that the enclosing foreach is in.
3575 * Can't know yet, so wrap the goto in a scope statement
3576 * so we can patch it later, and add it to a 'look at this later'
3579 gs
.label
.deleted
= true;
3580 auto ss
= new ScopeStatement(gs
.loc
, gs
, gs
.loc
);
3581 sc
.fes
.gotos
.push(ss
); // 'look at this later' list
3586 // Add to fwdref list to check later
3587 if (!gs
.label
.statement
)
3590 fd
.gotos
= new GotoStatements();
3593 else if (!(sc
.flags
& SCOPE
.Cfile
) && gs
.checkLabel())
3599 void visitLabel(LabelStatement ls
)
3601 //printf("LabelStatement::semantic()\n");
3602 FuncDeclaration fd
= sc
.parent
.isFuncDeclaration();
3604 ls
.ident
= fixupLabelName(sc
, ls
.ident
);
3605 ls
.tryBody
= sc
.tryBody
;
3608 ls
.lastVar
= sc
.lastVar
;
3609 ls
.inCtfeBlock
= (sc
.flags
& SCOPE
.ctfeBlock
) != 0;
3611 LabelDsymbol ls2
= fd
.searchLabel(ls
.ident
, ls
.loc
);
3612 if (ls2
.statement
&& !ls2
.duplicated
)
3614 if (ls
.loc
== ls2
.loc
)
3616 ls2
.duplicated
= true;
3617 error(ls
.loc
, "label `%s` is duplicated", ls2
.toChars());
3618 .errorSupplemental(ls2
.loc
, "labels cannot be used in a static foreach with more than 1 iteration");
3622 error(ls
.loc
, "label `%s` is already defined", ls2
.toChars());
3623 .errorSupplemental(ls2
.loc
, "first definition is here");
3631 sc
.scopesym
= sc
.enclosing
.scopesym
;
3633 sc
.ctorflow
.orCSX(CSX
.label
);
3637 ls
.statement
= ls
.statement
.statementSemantic(sc
);
3643 void visitAsm(AsmStatement s
)
3645 /* https://dlang.org/spec/statement.html#asm
3648 //printf("AsmStatement()::semantic()\n");
3649 result
= asmSemantic(s
, sc
);
3652 void visitCompoundAsm(CompoundAsmStatement cas
)
3654 //printf("CompoundAsmStatement()::semantic()\n");
3655 // Apply postfix attributes of the asm block to each statement.
3659 /* Go through the statements twice, first to declare any labels,
3660 * second for anything else.
3663 foreach (ref s
; *cas
.statements
)
3667 if (auto ls
= s
.isLabelStatement())
3669 sc
.func
.searchLabel(ls
.ident
, ls
.loc
);
3674 foreach (ref s
; *cas
.statements
)
3676 s
= s ? s
.statementSemantic(sc
) : null;
3680 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"))
3681 error(cas
.loc
, "`asm` statement is assumed to be impure - mark it with `pure` if it is not");
3682 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"))
3683 error(cas
.loc
, "`asm` statement is assumed to use the GC - mark it with `@nogc` if it does not");
3684 // @@@DEPRECATED_2.114@@@
3685 // change deprecation() to error(), add `else` and remove `| STC.safe`
3686 // to turn deprecation into an error when deprecation cycle is over
3687 if (cas
.stc & STC
.safe
)
3688 deprecation(cas
.loc
, "`asm` statement cannot be marked `@safe`, use `@system` or `@trusted` instead");
3689 if (!(cas
.stc & (STC
.trusted | STC
.safe
)))
3691 sc
.setUnsafe(false, cas
.loc
, "`asm` statement is assumed to be `@system` - mark it with `@trusted` if it is not");
3698 void visitImport(ImportStatement imps
)
3700 /* https://dlang.org/spec/module.html#ImportDeclaration
3703 foreach (i
; 0 .. imps
.imports
.length
)
3705 Import s
= (*imps
.imports
)[i
].isImport();
3706 assert(!s
.aliasdecls
.length
);
3707 foreach (j
, name
; s
.names
)
3709 Identifier _alias
= s
.aliases
[j
];
3713 auto tname
= new TypeIdentifier(s
.loc
, name
);
3714 auto ad
= new AliasDeclaration(s
.loc
, _alias
, tname
);
3716 s
.aliasdecls
.push(ad
);
3719 s
.dsymbolSemantic(sc
);
3721 // https://issues.dlang.org/show_bug.cgi?id=19942
3722 // If the module that's being imported doesn't exist, don't add it to the symbol table
3723 // for the current scope.
3726 Module
.addDeferredSemantic2(s
); // https://issues.dlang.org/show_bug.cgi?id=14666
3729 foreach (aliasdecl
; s
.aliasdecls
)
3731 sc
.insert(aliasdecl
);
3738 mixin VisitStatement
!void visit
;
3739 visit
.VisitStatement(s
);
3744 * Run semantic on `throw <exp>`.
3747 * loc = location of the `throw`
3748 * exp = value to be thrown
3749 * sc = enclosing scope
3751 * Returns: true if the `throw` is valid, or false if an error was found
3753 public bool throwSemantic(const ref Loc loc
, ref Expression exp
, Scope
* sc
)
3755 if (!global
.params
.useExceptions
)
3757 loc
.error("cannot use `throw` statements with %s", global
.params
.betterC ?
"-betterC".ptr
: "-nothrow".ptr
);
3761 if (!ClassDeclaration
.throwable
)
3763 loc
.error("cannot use `throw` statements because `object.Throwable` was not declared");
3767 if (FuncDeclaration fd
= sc
.parent
.isFuncDeclaration())
3768 fd
.hasReturnExp |
= 2;
3770 if (exp
.op
== EXP
.new_
)
3772 NewExp ne
= cast(NewExp
) exp
;
3776 exp
= exp
.expressionSemantic(sc
);
3777 exp
= resolveProperties(sc
, exp
);
3778 exp
= checkGC(sc
, exp
);
3779 if (exp
.op
== EXP
.error
)
3781 if (!exp
.type
.isNaked())
3783 // @@@DEPRECATED_2.112@@@
3784 // Deprecated in 2.102, change into an error & return false in 2.112
3785 exp
.loc
.deprecation("cannot throw object of qualified type `%s`", exp
.type
.toChars());
3788 checkThrowEscape(sc
, exp
, false);
3790 ClassDeclaration cd
= exp
.type
.toBasetype().isClassHandle();
3791 if (!cd ||
((cd
!= ClassDeclaration
.throwable
) && !ClassDeclaration
.throwable
.isBaseOf(cd
, null)))
3793 loc
.error("can only throw class objects derived from `Throwable`, not type `%s`", exp
.type
.toChars());
3799 private extern(D
) Expression
applyOpApply(ForeachStatement fs
, Expression flde
,
3800 Type tab
, Scope
* sc2
, Dsymbol sapply
)
3804 if (sc2
.useDIP1000
== FeatureState
.enabled
)
3806 message(loc
, "To enforce `@safe`, the compiler allocates a closure unless `opApply()` uses `scope`");
3808 (cast(FuncExp
)flde
).fd
.tookAddressOf
= 1;
3812 if (sc2
.useDIP1000
== FeatureState
.enabled
)
3813 ++(cast(FuncExp
)flde
).fd
.tookAddressOf
; // allocate a closure unless the opApply() uses 'scope'
3815 assert(tab
.ty
== Tstruct || tab
.ty
== Tclass
);
3821 ec
= new DotIdExp(fs
.loc
, fs
.aggr
, sapply
.ident
);
3822 ec
= new CallExp(fs
.loc
, ec
, flde
);
3823 ec
= ec
.expressionSemantic(sc2
);
3824 if (ec
.op
== EXP
.error
)
3826 if (ec
.type
!= Type
.tint32
)
3828 error(fs
.loc
, "`opApply()` function for `%s` must return an `int`", tab
.toChars());
3834 private extern(D
) Expression
applyDelegate(ForeachStatement fs
, Expression flde
,
3835 Type tab
, Scope
* sc2
)
3841 if (fs
.aggr
.op
== EXP
.delegate_
&& (cast(DelegateExp
)fs
.aggr
).func
.isNested() &&
3842 !(cast(DelegateExp
)fs
.aggr
).func
.needThis())
3844 // https://issues.dlang.org/show_bug.cgi?id=3560
3845 fs
.aggr
= (cast(DelegateExp
)fs
.aggr
).e1
;
3847 ec
= new CallExp(fs
.loc
, fs
.aggr
, flde
);
3848 ec
= ec
.expressionSemantic(sc2
);
3849 if (ec
.op
== EXP
.error
)
3851 if (ec
.type
!= Type
.tint32
)
3853 error(fs
.loc
, "`opApply()` function for `%s` must return an `int`", tab
.toChars());
3859 private extern(D
) Expression
applyArray(ForeachStatement fs
, Expression flde
,
3860 Type tab
, Scope
* sc2
, Type tn
, Type tnv
)
3863 const dim
= fs
.parameters
.length
;
3866 * _aApply(aggr, flde)
3868 static immutable fntab
=
3875 const(size_t
) BUFFER_LEN
= 7 + 1 + 2 + dim
.sizeof
* 3 + 1;
3876 char[BUFFER_LEN
] fdname
;
3881 case Tchar
: flag
= 0; break;
3882 case Twchar
: flag
= 3; break;
3883 case Tdchar
: flag
= 6; break;
3889 case Tchar
: flag
+= 0; break;
3890 case Twchar
: flag
+= 1; break;
3891 case Tdchar
: flag
+= 2; break;
3895 const(char)* r
= (fs
.op
== TOK
.foreach_reverse_
) ?
"R" : "";
3896 int j
= snprintf(fdname
.ptr
, BUFFER_LEN
, "_aApply%s%.*s%llu", r
, 2, fntab
[flag
].ptr
, cast(ulong)dim
);
3897 assert(j
< BUFFER_LEN
);
3899 FuncDeclaration fdapply
;
3901 auto params
= new Parameters();
3902 params
.push(new Parameter(Loc
.initial
, STC
.in_
, tn
.arrayOf(), null, null, null));
3903 auto dgparams
= new Parameters();
3904 dgparams
.push(new Parameter(Loc
.initial
, 0, Type
.tvoidptr
, null, null, null));
3906 dgparams
.push(new Parameter(Loc
.initial
, 0, Type
.tvoidptr
, null, null, null));
3907 dgty
= new TypeDelegate(new TypeFunction(ParameterList(dgparams
), Type
.tint32
, LINK
.d
));
3908 params
.push(new Parameter(Loc
.initial
, 0, dgty
, null, null, null));
3909 fdapply
= FuncDeclaration
.genCfunc(params
, Type
.tint32
, fdname
.ptr
);
3911 if (tab
.isTypeSArray())
3912 fs
.aggr
= fs
.aggr
.castTo(sc2
, tn
.arrayOf());
3913 // paint delegate argument to the type runtime expects
3914 Expression fexp
= flde
;
3915 if (!dgty
.equals(flde
.type
))
3917 fexp
= new CastExp(loc
, flde
, flde
.type
);
3920 ec
= new VarExp(Loc
.initial
, fdapply
, false);
3921 ec
= new CallExp(loc
, ec
, fs
.aggr
, fexp
);
3922 ec
.type
= Type
.tint32
; // don't run semantic() on ec
3926 private extern(D
) Expression
applyAssocArray(ForeachStatement fs
, Expression flde
, Type tab
)
3928 auto taa
= tab
.isTypeAArray();
3930 const dim
= fs
.parameters
.length
;
3932 Parameter p
= (*fs
.parameters
)[0];
3933 bool isRef
= (p
.storageClass
& STC
.ref_
) != 0;
3937 Type ti
= (isRef ? taa
.index
.addMod(MODFlags
.const_
) : taa
.index
);
3938 if (isRef ?
!ti
.constConv(ta
) : !ti
.implicitConvTo(ta
))
3940 error(fs
.loc
, "`foreach`: index must be type `%s`, not `%s`",
3941 ti
.toChars(), ta
.toChars());
3944 p
= (*fs
.parameters
)[1];
3945 isRef
= (p
.storageClass
& STC
.ref_
) != 0;
3948 Type taav
= taa
.nextOf();
3949 if (isRef ?
!taav
.constConv(ta
) : !taav
.implicitConvTo(ta
))
3951 error(fs
.loc
, "`foreach`: value must be type `%s`, not `%s`",
3952 taav
.toChars(), ta
.toChars());
3957 * extern(C) int _aaApply(void*, in size_t, int delegate(void*))
3958 * _aaApply(aggr, keysize, flde)
3960 * extern(C) int _aaApply2(void*, in size_t, int delegate(void*, void*))
3961 * _aaApply2(aggr, keysize, flde)
3963 __gshared FuncDeclaration
* fdapply
= [null, null];
3964 __gshared TypeDelegate
* fldeTy
= [null, null];
3965 ubyte i
= (dim
== 2 ?
1 : 0);
3968 auto params
= new Parameters();
3969 params
.push(new Parameter(Loc
.initial
, 0, Type
.tvoid
.pointerTo(), null, null, null));
3970 params
.push(new Parameter(Loc
.initial
, STC
.const_
, Type
.tsize_t
, null, null, null));
3971 auto dgparams
= new Parameters();
3972 dgparams
.push(new Parameter(Loc
.initial
, 0, Type
.tvoidptr
, null, null, null));
3974 dgparams
.push(new Parameter(Loc
.initial
, 0, Type
.tvoidptr
, null, null, null));
3975 fldeTy
[i
] = new TypeDelegate(new TypeFunction(ParameterList(dgparams
), Type
.tint32
, LINK
.d
));
3976 params
.push(new Parameter(Loc
.initial
, 0, fldeTy
[i
], null, null, null));
3977 fdapply
[i
] = FuncDeclaration
.genCfunc(params
, Type
.tint32
, i ? Id
._aaApply2
: Id
._aaApply
);
3980 auto exps
= new Expressions();
3982 auto keysize
= taa
.index
.size();
3983 if (keysize
== SIZE_INVALID
)
3985 assert(keysize
< keysize
.max
- target
.ptrsize
);
3986 keysize
= (keysize
+ (target
.ptrsize
- 1)) & ~(target
.ptrsize
- 1);
3987 // paint delegate argument to the type runtime expects
3988 Expression fexp
= flde
;
3989 if (!fldeTy
[i
].equals(flde
.type
))
3991 fexp
= new CastExp(fs
.loc
, flde
, flde
.type
);
3992 fexp
.type
= fldeTy
[i
];
3994 exps
.push(new IntegerExp(Loc
.initial
, keysize
, Type
.tsize_t
));
3996 ec
= new VarExp(Loc
.initial
, fdapply
[i
], false);
3997 ec
= new CallExp(fs
.loc
, ec
, exps
);
3998 ec
.type
= Type
.tint32
; // don't run semantic() on ec
4002 private extern(D
) Statement
loopReturn(Expression e
, Statements
* cases
, const ref Loc loc
)
4006 // Easy case, a clean exit from the loop
4007 e
= new CastExp(loc
, e
, Type
.tvoid
); // https://issues.dlang.org/show_bug.cgi?id=13899
4008 return new ExpStatement(loc
, e
);
4010 // Construct a switch statement around the return value
4011 // of the apply function.
4013 auto a
= new Statements();
4015 // default: break; takes care of cases 0 and 1
4016 s
= new BreakStatement(Loc
.initial
, null);
4017 s
= new DefaultStatement(Loc
.initial
, s
);
4021 foreach (i
, c
; *cases
)
4023 s
= new CaseStatement(Loc
.initial
, new IntegerExp(i
+ 2), c
);
4027 s
= new CompoundStatement(loc
, a
);
4028 return new SwitchStatement(loc
, null, e
, s
, false, loc
);
4031 /*************************************
4032 * Turn foreach body into the function literal:
4033 * int delegate(ref T param) { body }
4036 * fs = ForeachStatement
4037 * tfld = type of function literal to be created (type of opApply() function if any), can be null
4039 * Function literal created, as an expression
4042 private FuncExp
foreachBodyToFunction(Scope
* sc
, ForeachStatement fs
, TypeFunction tfld
)
4044 auto params
= new Parameters();
4045 foreach (i
, p
; *fs
.parameters
)
4047 StorageClass
stc = STC
.ref_ |
(p
.storageClass
& STC
.scope_
);
4050 p
.type
= p
.type
.typeSemantic(fs
.loc
, sc
);
4051 p
.type
= p
.type
.addStorageClass(p
.storageClass
);
4054 Parameter prm
= tfld
.parameterList
[i
];
4055 //printf("\tprm = %s%s\n", (prm.storageClass&STC.ref_?"ref ":"").ptr, prm.ident.toChars());
4056 stc = (prm
.storageClass
& STC
.ref_
) |
(p
.storageClass
& STC
.scope_
);
4057 if ((p
.storageClass
& STC
.ref_
) != (prm
.storageClass
& STC
.ref_
))
4059 if (!(prm
.storageClass
& STC
.ref_
))
4061 error(fs
.loc
, "`foreach`: cannot make `%s` `ref`", p
.ident
.toChars());
4066 id
= p
.ident
; // argument copy is not need.
4068 else if (p
.storageClass
& STC
.ref_
)
4070 // default delegate parameters are marked as ref, then
4071 // argument copy is not need.
4076 // Make a copy of the ref argument so it isn't
4079 id
= Identifier
.generateId("__applyArg", cast(int)i
);
4081 Initializer ie
= new ExpInitializer(fs
.loc
, new IdentifierExp(fs
.loc
, id
));
4082 auto v
= new VarDeclaration(fs
.loc
, p
.type
, p
.ident
, ie
);
4083 v
.storage_class |
= STC
.temp |
(stc & STC
.scope_
);
4084 Statement s
= new ExpStatement(fs
.loc
, v
);
4085 fs
._body
= new CompoundStatement(fs
.loc
, s
, fs
._body
);
4087 params
.push(new Parameter(fs
.loc
, stc, p
.type
, id
, null, null));
4089 // https://issues.dlang.org/show_bug.cgi?id=13840
4090 // Throwable nested function inside nothrow function is acceptable.
4091 StorageClass
stc = mergeFuncAttrs(STC
.safe | STC
.pure_ | STC
.nogc
, fs
.func
);
4092 auto tf
= new TypeFunction(ParameterList(params
), Type
.tint32
, LINK
.d
, stc);
4093 fs
.cases
= new Statements();
4094 fs
.gotos
= new ScopeStatements();
4095 auto fld = new FuncLiteralDeclaration(fs
.loc
, fs
.endloc
, tf
, TOK
.delegate_
, fs
);
4096 fld.fbody
= fs
._body
;
4097 Expression flde
= new FuncExp(fs
.loc
, fld);
4098 flde
= flde
.expressionSemantic(sc
);
4099 fld.tookAddressOf
= 0;
4100 if (flde
.op
== EXP
.error
)
4102 return cast(FuncExp
)flde
;
4106 void catchSemantic(Catch c
, Scope
* sc
)
4108 //printf("Catch::semantic(%s)\n", ident.toChars());
4110 if (sc
.os
&& sc
.os
.tok
!= TOK
.onScopeFailure
)
4112 // If enclosing is scope(success) or scope(exit), this will be placed in finally block.
4113 error(c
.loc
, "cannot put `catch` statement inside `%s`", Token
.toChars(sc
.os
.tok
));
4118 /* This is because the _d_local_unwind() gets the stack munged
4119 * up on this. The workaround is to place any try-catches into
4120 * a separate function, and call that.
4121 * To fix, have the compiler automatically convert the finally
4122 * body into a nested function.
4124 error(c
.loc
, "cannot put `catch` statement inside `finally` block");
4128 auto sym
= new ScopeDsymbol();
4129 sym
.parent
= sc
.scopesym
;
4134 error(c
.loc
, "`catch` statement without an exception specification is deprecated");
4135 errorSupplemental(c
.loc
, "use `catch(Throwable)` for old behavior");
4138 // reference .object.Throwable
4139 c
.type
= getThrowable();
4141 else if (!c
.type
.isNaked() && !c
.type
.isConst())
4143 // @@@DEPRECATED_2.115@@@
4144 // Deprecated in 2.105, change into an error & uncomment assign in 2.115
4145 deprecation(c
.loc
, "can only catch mutable or const qualified types, not `%s`", c
.type
.toChars());
4148 c
.type
= c
.type
.typeSemantic(c
.loc
, sc
);
4149 if (c
.type
== Type
.terror
)
4157 auto cd
= c
.type
.toBasetype().isClassHandle();
4160 error(c
.loc
, "can only catch class objects, not `%s`", c
.type
.toChars());
4163 else if (cd
.isCPPclass())
4165 if (!target
.cpp
.exceptions
)
4167 error(c
.loc
, "catching C++ class objects not supported for this target");
4170 if (!c
.internalCatch
)
4172 if (sc
.setUnsafe(false, c
.loc
, "cannot catch C++ class objects in `@safe` code"))
4176 else if (cd
!= ClassDeclaration
.throwable
&& !ClassDeclaration
.throwable
.isBaseOf(cd
, null))
4178 error(c
.loc
, "can only catch class objects derived from `Throwable`, not `%s`", c
.type
.toChars());
4181 else if (!c
.internalCatch
&& ClassDeclaration
.exception
&&
4182 cd
!= ClassDeclaration
.exception
&& !ClassDeclaration
.exception
.isBaseOf(cd
, null) &&
4183 sc
.setUnsafe(false, c
.loc
,
4184 "can only catch class objects derived from `Exception` in `@safe` code, not `%s`", c
.type
))
4188 else if (global
.params
.ehnogc
)
4193 // DIP1008 requires destruction of the Throwable, even if the user didn't specify an identifier
4194 auto ident
= c
.ident
;
4195 if (!ident
&& global
.params
.ehnogc
)
4196 ident
= Identifier
.generateAnonymousId("var");
4200 c
.var
= new VarDeclaration(c
.loc
, c
.type
, ident
, null, stc);
4201 c
.var
.iscatchvar
= true;
4202 c
.var
.dsymbolSemantic(sc
);
4205 if (global
.params
.ehnogc
&& stc & STC
.scope_
)
4207 /* Add a destructor for c.var
4208 * try { handler } finally { if (!__ctfe) _d_delThrowable(var); }
4210 assert(!c
.var
.edtor
); // ensure we didn't create one in callScopeDtor()
4213 Expression e
= new VarExp(loc
, c
.var
);
4214 e
= new CallExp(loc
, new IdentifierExp(loc
, Id
._d_delThrowable
), e
);
4216 Expression ec
= new IdentifierExp(loc
, Id
.ctfe
);
4217 ec
= new NotExp(loc
, ec
);
4218 Statement s
= new IfStatement(loc
, null, ec
, new ExpStatement(loc
, e
), null, loc
);
4219 c
.handler
= new TryFinallyStatement(loc
, c
.handler
, s
);
4223 c
.handler
= c
.handler
.statementSemantic(sc
);
4224 if (c
.handler
&& c
.handler
.isErrorStatement())
4230 Statement
semanticNoScope(Statement s
, Scope
* sc
)
4232 //printf("Statement::semanticNoScope() %s\n", toChars());
4233 if (!s
.isCompoundStatement() && !s
.isScopeStatement())
4235 s
= new CompoundStatement(s
.loc
, s
); // so scopeCode() gets called
4237 s
= s
.statementSemantic(sc
);
4241 // Same as semanticNoScope(), but do create a new scope
4242 private Statement
semanticScope(Statement s
, Scope
* sc
, Statement sbreak
, Statement scontinue
, Statement tryBody
)
4244 auto sym
= new ScopeDsymbol();
4245 sym
.parent
= sc
.scopesym
;
4246 Scope
* scd
= sc
.push(sym
);
4248 scd
.sbreak
= sbreak
;
4250 scd
.scontinue
= scontinue
;
4252 scd
.tryBody
= tryBody
;
4253 s
= s
.semanticNoScope(scd
);
4259 /****************************************
4260 * If `statement` has code that needs to run in a finally clause
4261 * at the end of the current scope, return that code in the form of
4264 * statement = the statement
4266 * sentry = set to code executed upon entry to the scope
4267 * sexception = set to code executed upon exit from the scope via exception
4268 * sfinally = set to code executed in finally block
4270 * code to be run in the finally clause
4272 Statement
scopeCode(Statement statement
, Scope
* sc
, out Statement sentry
, out Statement sexception
, out Statement sfinally
)
4274 if (auto es
= statement
.isExpStatement())
4276 if (es
.exp
&& es
.exp
.op
== EXP
.declaration
)
4278 auto de = cast(DeclarationExp
)es
.exp
;
4279 auto v
= de.declaration
.isVarDeclaration();
4280 if (v
&& !v
.isDataseg())
4282 if (v
.needsScopeDtor())
4284 sfinally
= new DtorExpStatement(es
.loc
, v
.edtor
, v
);
4285 v
.storage_class |
= STC
.nodtor
; // don't add in dtor again
4292 else if (auto sgs
= statement
.isScopeGuardStatement())
4294 Statement s
= new PeelStatement(sgs
.statement
);
4298 case TOK
.onScopeExit
:
4302 case TOK
.onScopeFailure
:
4306 case TOK
.onScopeSuccess
:
4309 * sentry: bool x = false;
4310 * sexception: x = true;
4311 * sfinally: if (!x) statement;
4313 auto v
= copyToTemp(0, "__os", IntegerExp
.createBool(false));
4314 v
.dsymbolSemantic(sc
);
4315 sentry
= new ExpStatement(statement
.loc
, v
);
4317 Expression e
= IntegerExp
.createBool(true);
4318 e
= new AssignExp(Loc
.initial
, new VarExp(Loc
.initial
, v
), e
);
4319 sexception
= new ExpStatement(Loc
.initial
, e
);
4321 e
= new VarExp(Loc
.initial
, v
);
4322 e
= new NotExp(Loc
.initial
, e
);
4323 sfinally
= new IfStatement(Loc
.initial
, null, e
, s
, null, Loc
.initial
);
4332 else if (auto ls
= statement
.isLabelStatement())
4335 ls
.statement
= ls
.statement
.scopeCode(sc
, sentry
, sexception
, sfinally
);
4342 /*******************
4343 * Type check and unroll `foreach` over an expression tuple as well
4344 * as `static foreach` statements and `static foreach`
4345 * declarations. For `static foreach` statements and `static
4346 * foreach` declarations, the visitor interface is used (and the
4347 * result is written into the `result` field.) For `static
4348 * foreach` declarations, the resulting Dsymbols* are returned
4351 * The unrolled body is wrapped into a
4352 * - UnrolledLoopStatement, for `foreach` over an expression tuple.
4353 * - ForwardingStatement, for `static foreach` statements.
4354 * - ForwardingAttribDeclaration, for `static foreach` declarations.
4356 * `static foreach` variables are declared as `STC.local`, such
4357 * that they are inserted into the local symbol tables of the
4358 * forwarding constructs instead of forwarded. For `static
4359 * foreach` with multiple foreach loop variables whose aggregate
4360 * has been lowered into a sequence of tuples, this function
4361 * expands the tuples into multiple `STC.local` `static foreach`
4364 public auto makeTupleForeach(Scope
* sc
, bool isStatic
, bool isDecl
, ForeachStatement fs
, Dsymbols
* dbody
, bool needExpansion
)
4366 // Voldemort return type
4369 Statement statement
;
4380 result
.statement
= new ErrorStatement();
4385 size_t dim
= fs
.parameters
.length
;
4386 const bool skipCheck
= isStatic
&& needExpansion
;
4387 if (!skipCheck
&& (dim
< 1 || dim
> 2))
4389 error(fs
.loc
, "only one (value) or two (key,value) arguments allowed for sequence `foreach`");
4390 return returnEarly();
4393 Type paramtype
= (*fs
.parameters
)[dim
- 1].type
;
4396 paramtype
= paramtype
.typeSemantic(loc
, sc
);
4397 if (paramtype
.ty
== Terror
)
4399 return returnEarly();
4403 Type tab
= fs
.aggr
.type
.toBasetype();
4404 TypeTuple tuple
= cast(TypeTuple
)tab
;
4406 Statements
* statements
;
4407 Dsymbols
* declarations
;
4409 declarations
= new Dsymbols();
4411 statements
= new Statements();
4413 //printf("aggr: op = %d, %s\n", fs.aggr.op, fs.aggr.toChars());
4416 if (fs
.aggr
.op
== EXP
.tuple
) // expression tuple
4418 te
= cast(TupleExp
)fs
.aggr
;
4421 else if (fs
.aggr
.op
== EXP
.type
) // type tuple
4423 n
= Parameter
.dim(tuple
.arguments
);
4429 size_t k
= (fs
.op
== TOK
.foreach_
) ? j
: n
- 1 - j
;
4430 Expression e
= null;
4435 t
= Parameter
.getNth(tuple
.arguments
, k
).type
;
4436 Parameter p
= (*fs
.parameters
)[0];
4441 decls
= new Dsymbols();
4443 stmts
= new Statements();
4445 const bool skip
= isStatic
&& needExpansion
;
4446 if (!skip
&& dim
== 2)
4449 if (p
.isReference() || p
.isLazy())
4451 error(fs
.loc
, "no storage class for key `%s`", p
.ident
.toChars());
4452 return returnEarly();
4459 p
.type
= Type
.tsize_t
;
4462 p
.type
= p
.type
.typeSemantic(loc
, sc
);
4464 if (!p
.type
.isintegral())
4466 error(fs
.loc
, "foreach: key cannot be of non-integral type `%s`",
4468 return returnEarly();
4471 const length
= te ? te
.exps
.length
: tuple
.arguments
.length
;
4472 IntRange dimrange
= IntRange(SignExtendedNumber(length
))._cast(Type
.tsize_t
);
4473 // https://issues.dlang.org/show_bug.cgi?id=12504
4474 dimrange
.imax
= SignExtendedNumber(dimrange
.imax
.value
-1);
4475 if (!IntRange
.fromType(p
.type
).contains(dimrange
))
4477 error(fs
.loc
, "index type `%s` cannot cover index range 0..%llu",
4478 p
.type
.toChars(), cast(ulong)length
);
4479 return returnEarly();
4481 Initializer ie
= new ExpInitializer(Loc
.initial
, new IntegerExp(k
));
4482 auto var
= new VarDeclaration(loc
, p
.type
, p
.ident
, ie
);
4483 var
.storage_class |
= STC
.foreach_ | STC
.manifest
;
4485 var
.storage_class |
= STC
.local
;
4490 stmts
.push(new ExpStatement(loc
, var
));
4492 p
= (*fs
.parameters
)[1]; // value
4494 /***********************
4495 * Declares a unrolled `foreach` loop variable or a `static foreach` variable.
4498 * storageClass = The storage class of the variable.
4499 * type = The declared type of the variable.
4500 * ident = The name of the variable.
4501 * e = The initializer of the variable (i.e. the current element of the looped over aggregate).
4502 * t = The type of the initializer.
4504 * `true` iff the declaration was successful.
4506 bool declareVariable(StorageClass storageClass
, Type type
, Identifier ident
, Expression e
, Type t
)
4508 if (storageClass
& (STC
.out_ | STC
.lazy_
) ||
4509 storageClass
& STC
.ref_
&& !te
)
4511 error(fs
.loc
, "no storage class for value `%s`", ident
.toChars());
4517 Type tb
= e
.type
.toBasetype();
4519 if (!(storageClass
& STC
.manifest
))
4521 if (isStatic || tb
.ty
== Tfunction || storageClass
& STC
.alias_
)
4523 if (auto ve
= e
.isVarExp())
4525 else if (auto dve
= e
.isDotVarExp())
4528 if (auto te
= e
.isTemplateExp())
4530 else if (auto se
= e
.isScopeExp())
4532 else if (auto fe
= e
.isFuncExp())
4533 ds = fe
.td ? fe
.td
: fe
.fd
;
4534 else if (auto oe
= e
.isOverExp())
4537 else if (storageClass
& STC
.alias_
)
4539 error(fs
.loc
, "`foreach` loop variable cannot be both `enum` and `alias`");
4545 var
= new AliasDeclaration(loc
, ident
, ds);
4546 if (storageClass
& STC
.ref_
)
4548 error(fs
.loc
, "symbol `%s` cannot be `ref`", ds.toChars());
4553 error(fs
.loc
, "cannot specify element type for symbol `%s`", ds.toChars());
4557 else if (e
.op
== EXP
.type
)
4559 var
= new AliasDeclaration(loc
, ident
, e
.type
);
4562 error(fs
.loc
, "cannot specify element type for type `%s`", e
.type
.toChars());
4568 e
= resolveProperties(sc
, e
);
4569 Initializer ie
= new ExpInitializer(Loc
.initial
, e
);
4570 auto v
= new VarDeclaration(loc
, type
, ident
, ie
, storageClass
);
4571 v
.storage_class |
= STC
.foreach_
;
4572 if (storageClass
& STC
.ref_
)
4573 v
.storage_class |
= STC
.ref_
;
4574 if (isStatic || storageClass
&STC
.manifest || e
.isConst() ||
4575 e
.op
== EXP
.string_ ||
4576 e
.op
== EXP
.structLiteral ||
4577 e
.op
== EXP
.arrayLiteral
)
4579 if (v
.storage_class
& STC
.ref_
)
4583 error(fs
.loc
, "constant value `%s` cannot be `ref`", toChars(ie
));
4589 error(fs
.loc
, "constant value `%s` cannot be `ref`", toChars(ie
));
4593 error(fs
.loc
, "constant value `%s` cannot be `ref`", ident
.toChars());
4599 v
.storage_class |
= STC
.manifest
;
4606 var
= new AliasDeclaration(loc
, ident
, t
);
4609 error(fs
.loc
, "cannot specify element type for symbol `%s`", fs
.toChars());
4615 var
.storage_class |
= STC
.local
;
4621 stmts
.push(new ExpStatement(loc
, var
));
4628 if (!declareVariable(p
.storageClass
, p
.type
, p
.ident
, e
, t
))
4630 return returnEarly();
4638 if (!declareVariable(p
.storageClass
, p
.type
, p
.ident
, e
, t
))
4640 return returnEarly();
4644 { // expand tuples into multiple `static foreach` variables.
4646 auto ident
= Identifier
.generateId("__value");
4647 declareVariable(0, e
.type
, ident
, e
, null);
4648 import dmd
.cond
: StaticForeach
;
4649 auto field
= Identifier
.idPool(StaticForeach
.tupleFieldName
.ptr
,StaticForeach
.tupleFieldName
.length
);
4650 Expression access
= new DotIdExp(loc
, e
, field
);
4651 access
= expressionSemantic(access
, sc
);
4652 access
= access
.optimize(WANTvalue
);
4653 if (!tuple
) return returnEarly();
4654 //printf("%s\n",tuple.toChars());
4655 foreach (l
; 0 .. dim
)
4657 auto cp
= (*fs
.parameters
)[l
];
4658 Expression init_
= new IndexExp(loc
, access
, new IntegerExp(loc
, l
, Type
.tsize_t
));
4659 init_
= init_
.expressionSemantic(sc
);
4661 declareVariable(p
.storageClass
, init_
.type
, cp
.ident
, init_
, null);
4669 decls
.append(Dsymbol
.arraySyntaxCopy(dbody
));
4672 stmts
.push(fs
._body
.syntaxCopy());
4673 s
= new CompoundStatement(loc
, stmts
);
4678 s
= new ScopeStatement(loc
, s
, fs
.endloc
);
4682 import dmd
.attrib
: ForwardingAttribDeclaration
;
4683 d
= new ForwardingAttribDeclaration(decls
);
4687 s
= new ForwardingStatement(loc
, s
);
4691 declarations
.push(d
);
4698 Statement res
= new UnrolledLoopStatement(loc
, statements
);
4699 if (LabelStatement ls
= checkLabeledLoop(sc
, fs
))
4700 ls
.gotoTarget
= res
;
4702 res
= new CompoundStatement(loc
, new ExpStatement(te
.e0
.loc
, te
.e0
), res
);
4703 result
.statement
= res
;
4706 result
.decl
= declarations
;
4708 result
.statement
= new CompoundStatement(loc
, statements
);
4713 /*********************************
4714 * Flatten out the scope by presenting `statement`
4715 * as an array of statements.
4717 * statement = the statement to flatten
4720 * The array of `Statements`, or `null` if no flattening necessary
4722 private Statements
* flatten(Statement statement
, Scope
* sc
)
4724 static auto errorStatements()
4726 auto a
= new Statements();
4727 a
.push(new ErrorStatement());
4732 /*compound and expression statements have classes that inherit from them with the same
4733 *flattening behavior, so the isXXX methods won't work
4735 switch(statement
.stmt
)
4738 case STMT
.CompoundDeclaration
:
4739 return (cast(CompoundStatement
)statement
).statements
;
4743 auto es
= cast(ExpStatement
)statement
;
4744 /* https://issues.dlang.org/show_bug.cgi?id=14243
4745 * expand template mixin in statement scope
4746 * to handle variable destructors.
4748 if (!es
.exp ||
!es
.exp
.isDeclarationExp())
4751 Dsymbol d
= es
.exp
.isDeclarationExp().declaration
;
4752 auto tm
= d
.isTemplateMixin();
4756 Expression e
= es
.exp
.expressionSemantic(sc
);
4757 if (e
.op
== EXP
.error || tm
.errors
)
4758 return errorStatements();
4761 Statement s
= toStatement(tm
);
4768 toCBuffer(s
, &buf
, &hgs
);
4769 printf("tm ==> s = %s\n", buf
.peekChars());
4771 auto a
= new Statements();
4775 case STMT
.Forwarding
:
4776 /***********************
4777 * ForwardingStatements are distributed over the flattened
4778 * sequence of statements. This prevents flattening to be
4779 * "blocked" by a ForwardingStatement and is necessary, for
4780 * example, to support generating scope guards with `static
4783 * static foreach(i; 0 .. 10) scope(exit) writeln(i);
4784 * writeln("this is printed first");
4785 * // then, it prints 10, 9, 8, 7, ...
4787 auto fs
= statement
.isForwardingStatement();
4792 sc
= sc
.push(fs
.sym
);
4793 auto a
= fs
.statement
.flatten(sc
);
4799 auto b
= new Statements(a
.length
);
4802 (*b
)[i
] = s ?
new ForwardingStatement(s
.loc
, fs
.sym
, s
) : null;
4806 case STMT
.Conditional
:
4807 auto cs
= statement
.isConditionalStatement();
4810 //printf("ConditionalStatement::flatten()\n");
4811 if (cs
.condition
.include(sc
))
4813 DebugCondition dc
= cs
.condition
.isDebugCondition();
4816 s
= new DebugStatement(cs
.loc
, cs
.ifbody
);
4817 debugThrowWalker(cs
.ifbody
);
4825 auto a
= new Statements();
4829 case STMT
.StaticForeach
:
4830 auto sfs
= statement
.isStaticForeachStatement();
4831 sfs
.sfe
.prepare(sc
);
4832 if (sfs
.sfe
.ready())
4834 Statement s
= makeTupleForeach(sc
, true, false, sfs
.sfe
.aggrfe
, null, sfs
.sfe
.needExpansion
).statement
;
4835 auto result
= s
.flatten(sc
);
4840 result
= new Statements();
4845 return errorStatements();
4848 auto ds = statement
.isDebugStatement();
4849 Statements
* a
= ds.statement ?
ds.statement
.flatten(sc
) : null;
4855 s
= new DebugStatement(ds.loc
, s
);
4860 auto ls
= statement
.isLabelStatement();
4864 Statements
* a
= null;
4865 a
= ls
.statement
.flatten(sc
);
4871 a
.push(new ExpStatement(ls
.loc
, cast(Expression
)null));
4874 // reuse 'this' LabelStatement
4875 ls
.statement
= (*a
)[0];
4880 auto cs
= statement
.isMixinStatement();
4884 if (expressionsToString(buf
, sc
, cs
.exps
))
4885 return errorStatements();
4887 const errors
= global
.errors
;
4888 const len
= buf
.length
;
4890 const str = buf
.extractSlice()[0 .. len
];
4891 const bool doUnittests
= global
.params
.parsingUnittestsRequired();
4892 auto loc
= adjustLocForMixin(str, cs
.loc
, global
.params
.mixinOut
);
4893 scope p
= new Parser
!ASTCodegen(loc
, sc
._module
, str, false, global
.errorSink
, &global
.compileEnv
, doUnittests
);
4894 p
.transitionIn
= global
.params
.v
.vin
;
4897 auto a
= new Statements();
4898 while (p
.token
.value
!= TOK
.endOfFile
)
4900 Statement s
= p
.parseStatement(ParseStatementFlags
.curlyScope
);
4901 if (!s || global
.errors
!= errors
)
4902 return errorStatements();
4911 /***********************************************************
4912 * Convert TemplateMixin members (which are Dsymbols) to Statements.
4914 * s = the symbol to convert to a Statement
4916 * s redone as a Statement
4918 private Statement
toStatement(Dsymbol s
)
4922 if (auto tm
= s
.isTemplateMixin())
4924 auto a
= new Statements();
4925 foreach (m
; *tm
.members
)
4927 if (Statement sx
= toStatement(m
))
4930 result
= new CompoundStatement(tm
.loc
, a
);
4932 else if (s
.isVarDeclaration() ||
4933 s
.isAggregateDeclaration() ||
4934 s
.isFuncDeclaration() ||
4935 s
.isEnumDeclaration() ||
4936 s
.isAliasDeclaration() ||
4937 s
.isTemplateDeclaration())
4939 /* Perhaps replace the above with isScopeDsymbol() || isDeclaration()
4941 /* An actual declaration symbol will be converted to DeclarationExp
4942 * with ExpStatement.
4944 auto de = new DeclarationExp(s
.loc
, s
);
4945 de.type
= Type
.tvoid
; // avoid repeated semantic
4946 result
= new ExpStatement(s
.loc
, de);
4948 else if (auto d
= s
.isAttribDeclaration())
4950 /* All attributes have been already picked by the semantic analysis of
4951 * 'bottom' declarations (function, struct, class, etc).
4952 * So we don't have to copy them.
4954 if (Dsymbols
* a
= d
.include(null))
4956 auto statements
= new Statements();
4959 statements
.push(toStatement(sx
));
4961 result
= new CompoundStatement(d
.loc
, statements
);
4964 else if (s
.isStaticAssert() ||
4967 /* Ignore as they are not Statements
4972 .error(Loc
.initial
, "internal compiler error: cannot mixin %s `%s`\n", s
.kind(), s
.toChars());
4973 result
= new ErrorStatement();
4980 Marks all occurring ThrowStatements as internalThrows.
4981 This is intended to be called from a DebugStatement as it allows
4982 to mark all its nodes as nothrow.
4985 s = AST Node to traverse
4987 private void debugThrowWalker(Statement s
)
4990 extern(C
++) final class DebugWalker
: SemanticTimeTransitiveVisitor
4992 alias visit
= SemanticTimeTransitiveVisitor
.visit
;
4995 override void visit(ThrowStatement s
)
4997 s
.internalThrow
= true;
5000 override void visit(CallExp s
)
5002 s
.inDebugStatement
= true;
5006 scope walker
= new DebugWalker();
5010 /***********************************************************
5011 * Evaluate and print a `pragma(msg, args)`
5014 * loc = location for error messages
5015 * sc = scope for argument interpretation
5016 * args = expressions to print
5020 bool pragmaMsgSemantic(Loc loc
, Scope
* sc
, Expressions
* args
)
5024 foreach (arg
; *args
)
5026 sc
= sc
.startCTFE();
5027 auto e
= arg
.expressionSemantic(sc
);
5028 e
= resolveProperties(sc
, e
);
5031 // pragma(msg) is allowed to contain types as well as expressions
5032 e
= ctfeInterpretForPragmaMsg(e
);
5033 if (e
.op
== EXP
.error
)
5035 errorSupplemental(loc
, "while evaluating `pragma(msg, %s)`", arg
.toChars());
5038 if (auto se
= e
.toStringExp())
5040 const slice
= se
.toUTF8(sc
).peekString();
5041 fprintf(stderr
, "%.*s", cast(int)slice
.length
, slice
.ptr
);
5044 fprintf(stderr
, "%s", e
.toChars());
5046 fprintf(stderr
, "\n");
5050 /***********************************************************
5051 * Evaluate `pragma(startAddress, func)` and store the resolved symbol in `args`
5054 * loc = location for error messages
5055 * sc = scope for argument interpretation
5056 * args = pragma arguments
5060 bool pragmaStartAddressSemantic(Loc loc
, Scope
* sc
, Expressions
* args
)
5062 if (!args || args
.length
!= 1)
5064 .error(loc
, "function name expected for start address");
5069 /* https://issues.dlang.org/show_bug.cgi?id=11980
5070 * resolveProperties and ctfeInterpret call are not necessary.
5072 Expression e
= (*args
)[0];
5073 sc
= sc
.startCTFE();
5074 e
= e
.expressionSemantic(sc
);
5075 // e = resolveProperties(sc, e);
5078 // e = e.ctfeInterpret();
5080 Dsymbol sa
= getDsymbol(e
);
5081 if (!sa ||
!sa
.isFuncDeclaration())
5083 .error(loc
, "function name expected for start address, not `%s`", e
.toChars());
5090 /************************************
5091 * Check for skipped variable declarations.
5093 * ss = statement to check
5097 private bool checkLabel(SwitchStatement ss
)
5100 * Checks the scope of a label for existing variable declaration.
5102 * vd = last variable declared before this case/default label
5103 * Returns: `true` if the variables declared in this label would be skipped.
5105 bool checkVar(VarDeclaration vd
)
5107 for (auto v
= vd
; v
&& v
!= ss
.lastVar
; v
= v
.lastVar
)
5109 if (v
.isDataseg() ||
(v
.storage_class
& (STC
.manifest | STC
.temp
) && vd
.ident
!= Id
.withSym
) || v
._init
.isVoidInitializer())
5111 if (vd
.ident
== Id
.withSym
)
5112 error(ss
.loc
, "`switch` skips declaration of `with` temporary");
5114 error(ss
.loc
, "`switch` skips declaration of variable `%s`", v
.toPrettyChars());
5115 errorSupplemental(v
.loc
, "declared here");
5123 if (ss
.sdefault
&& checkVar(ss
.sdefault
.lastVar
))
5124 return !error
; // return error once fully deprecated
5126 foreach (scase
; *ss
.cases
)
5128 if (scase
&& checkVar(scase
.lastVar
))
5129 return !error
; // return error once fully deprecated
5136 * Check for skipped variable declarations.
5138 * gs = statement to check
5139 * Returns: true for error
5141 bool checkLabel(GotoStatement gs
)
5143 if (!gs
.label
.statement
)
5144 return true; // error should have been issued for this already
5146 if (gs
.label
.statement
.os
!= gs
.os
)
5148 if (gs
.os
&& gs
.os
.tok
== TOK
.onScopeFailure
&& !gs
.label
.statement
.os
)
5150 // Jump out from scope(failure) block is allowed.
5154 if (gs
.label
.statement
.os
)
5155 error(gs
.loc
, "cannot `goto` in to `%s` block", Token
.toChars(gs
.label
.statement
.os
.tok
));
5157 error(gs
.loc
, "cannot `goto` out of `%s` block", Token
.toChars(gs
.os
.tok
));
5162 if (gs
.label
.statement
.tf
!= gs
.tf
)
5164 error(gs
.loc
, "cannot `goto` in or out of `finally` block");
5168 if (gs
.label
.statement
.inCtfeBlock
&& !gs
.inCtfeBlock
)
5170 error(gs
.loc
, "cannot `goto` into `if (__ctfe)` block");
5175 for (auto stb
= gs
.tryBody
; stb
!= gs
.label
.statement
.tryBody
; stb
= stbnext
)
5179 error(gs
.loc
, "cannot `goto` into `try` block");
5182 if (auto stf
= stb
.isTryFinallyStatement())
5183 stbnext
= stf
.tryBody
;
5184 else if (auto stc = stb
.isTryCatchStatement())
5185 stbnext
= stc.tryBody
;
5190 VarDeclaration vd
= gs
.label
.statement
.lastVar
;
5191 if (!vd || vd
.isDataseg() ||
(vd
.storage_class
& STC
.manifest
))
5194 VarDeclaration last
= gs
.lastVar
;
5195 while (last
&& last
!= vd
)
5196 last
= last
.lastVar
;
5199 // All good, the label's scope has no variables
5201 else if (vd
.storage_class
& STC
.exptemp
)
5203 // Lifetime ends at end of expression, so no issue with skipping the statement
5207 if (vd
.ident
== Id
.withSym
)
5208 error(gs
.loc
, "`goto` skips declaration of `with` temporary");
5210 error(gs
.loc
, "`goto` skips declaration of variable `%s`", vd
.toPrettyChars());
5211 errorSupplemental(vd
.loc
, "declared here");