Internally these would treat the cast same as a normal conversion from int[7] to int[], which allows code at CTFE to erroneously succeed where it would raise a SEGV at run-time.
1 |
/**
|
|
2 |
* Performs the semantic3 stage, which deals with function bodies.
|
|
3 |
*
|
|
4 |
* Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
|
|
5 |
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
|
|
6 |
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
|
|
7 |
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/semantic3.d, _semantic3.d)
|
|
8 |
* Documentation: https://dlang.org/phobos/dmd_semantic3.html
|
|
9 |
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/semantic3.d
|
|
10 |
*/
|
|
11 |
|
|
12 |
module dmd.semantic3; |
|
13 |
|
|
14 |
import core.stdc.stdio; |
|
15 |
import core.stdc.string; |
|
16 |
|
|
17 |
import dmd.aggregate; |
|
18 |
import dmd.aliasthis; |
|
19 |
import dmd.arraytypes; |
|
20 |
import dmd.astcodegen; |
|
21 |
import dmd.attrib; |
|
22 |
import dmd.blockexit; |
|
23 |
import dmd.clone; |
|
24 |
import dmd.ctorflow; |
|
25 |
import dmd.dcast; |
|
26 |
import dmd.dclass; |
|
27 |
import dmd.declaration; |
|
28 |
import dmd.denum; |
|
29 |
import dmd.dimport; |
|
30 |
import dmd.dinterpret; |
|
31 |
import dmd.dmodule; |
|
32 |
import dmd.dscope; |
|
33 |
import dmd.dstruct; |
|
34 |
import dmd.dsymbol; |
|
35 |
import dmd.dsymbolsem; |
|
36 |
import dmd.dtemplate; |
|
37 |
import dmd.dversion; |
|
38 |
import dmd.errors; |
|
39 |
import dmd.escape; |
|
40 |
import dmd.expression; |
|
41 |
import dmd.expressionsem; |
|
42 |
import dmd.func; |
|
43 |
import dmd.globals; |
|
44 |
import dmd.id; |
|
45 |
import dmd.identifier; |
|
46 |
import dmd.init; |
|
47 |
import dmd.initsem; |
|
48 |
import dmd.hdrgen; |
|
49 |
import dmd.mtype; |
|
50 |
import dmd.nogc; |
|
51 |
import dmd.nspace; |
|
52 |
import dmd.ob; |
|
53 |
import dmd.objc; |
|
54 |
import dmd.opover; |
|
55 |
import dmd.parse; |
|
56 |
import dmd.root.filename; |
|
57 |
import dmd.root.outbuffer; |
|
58 |
import dmd.root.rmem; |
|
59 |
import dmd.root.rootobject; |
|
60 |
import dmd.sideeffect; |
|
61 |
import dmd.statementsem; |
|
62 |
import dmd.staticassert; |
|
63 |
import dmd.tokens; |
|
64 |
import dmd.utf; |
|
65 |
import dmd.semantic2; |
|
66 |
import dmd.statement; |
|
67 |
import dmd.target; |
|
68 |
import dmd.templateparamsem; |
|
69 |
import dmd.typesem; |
|
70 |
import dmd.visitor; |
|
71 |
|
|
72 |
enum LOG = false; |
|
73 |
|
|
74 |
|
|
75 |
/*************************************
|
|
76 |
* Does semantic analysis on function bodies.
|
|
77 |
*/
|
|
78 |
extern(C++) void semantic3(Dsymbol dsym, Scope* sc) |
|
79 |
{
|
|
80 | 1 |
scope v = new Semantic3Visitor(sc); |
81 | 1 |
dsym.accept(v); |
82 |
}
|
|
83 |
|
|
84 |
private extern(C++) final class Semantic3Visitor : Visitor |
|
85 |
{
|
|
86 |
alias visit = Visitor.visit; |
|
87 |
|
|
88 |
Scope* sc; |
|
89 | 1 |
this(Scope* sc) |
90 |
{
|
|
91 | 1 |
this.sc = sc; |
92 |
}
|
|
93 |
|
|
94 |
override void visit(Dsymbol) {} |
|
95 |
|
|
96 |
override void visit(TemplateInstance tempinst) |
|
97 |
{
|
|
98 |
static if (LOG) |
|
99 |
{
|
|
100 |
printf("TemplateInstance.semantic3('%s'), semanticRun = %d\n", tempinst.toChars(), tempinst.semanticRun); |
|
101 |
}
|
|
102 |
//if (toChars()[0] == 'D') *(char*)0=0;
|
|
103 | 1 |
if (tempinst.semanticRun >= PASS.semantic3) |
104 | 1 |
return; |
105 | 1 |
tempinst.semanticRun = PASS.semantic3; |
106 | 1 |
if (!tempinst.errors && tempinst.members) |
107 |
{
|
|
108 | 1 |
TemplateDeclaration tempdecl = tempinst.tempdecl.isTemplateDeclaration(); |
109 | 1 |
assert(tempdecl); |
110 |
|
|
111 | 1 |
sc = tempdecl._scope; |
112 | 1 |
sc = sc.push(tempinst.argsym); |
113 | 1 |
sc = sc.push(tempinst); |
114 | 1 |
sc.tinst = tempinst; |
115 | 1 |
sc.minst = tempinst.minst; |
116 |
|
|
117 | 1 |
int needGagging = (tempinst.gagged && !global.gag); |
118 | 1 |
uint olderrors = global.errors; |
119 | 1 |
int oldGaggedErrors = -1; // dead-store to prevent spurious warning |
120 |
/* If this is a gagged instantiation, gag errors.
|
|
121 |
* Future optimisation: If the results are actually needed, errors
|
|
122 |
* would already be gagged, so we don't really need to run semantic
|
|
123 |
* on the members.
|
|
124 |
*/
|
|
125 | 1 |
if (needGagging) |
126 | 1 |
oldGaggedErrors = global.startGagging(); |
127 |
|
|
128 | 1 |
for (size_t i = 0; i < tempinst.members.dim; i++) |
129 |
{
|
|
130 | 1 |
Dsymbol s = (*tempinst.members)[i]; |
131 | 1 |
s.semantic3(sc); |
132 | 1 |
if (tempinst.gagged && global.errors != olderrors) |
133 | 1 |
break; |
134 |
}
|
|
135 |
|
|
136 | 1 |
if (global.errors != olderrors) |
137 |
{
|
|
138 | 1 |
if (!tempinst.errors) |
139 |
{
|
|
140 | 1 |
if (!tempdecl.literal) |
141 | 1 |
tempinst.error(tempinst.loc, "error instantiating"); |
142 | 1 |
if (tempinst.tinst) |
143 | 1 |
tempinst.tinst.printInstantiationTrace(); |
144 |
}
|
|
145 | 1 |
tempinst.errors = true; |
146 |
}
|
|
147 | 1 |
if (needGagging) |
148 | 1 |
global.endGagging(oldGaggedErrors); |
149 |
|
|
150 | 1 |
sc = sc.pop(); |
151 | 1 |
sc.pop(); |
152 |
}
|
|
153 |
}
|
|
154 |
|
|
155 |
override void visit(TemplateMixin tmix) |
|
156 |
{
|
|
157 | 1 |
if (tmix.semanticRun >= PASS.semantic3) |
158 | 1 |
return; |
159 | 1 |
tmix.semanticRun = PASS.semantic3; |
160 |
static if (LOG) |
|
161 |
{
|
|
162 |
printf("TemplateMixin.semantic3('%s')\n", tmix.toChars()); |
|
163 |
}
|
|
164 | 1 |
if (tmix.members) |
165 |
{
|
|
166 | 1 |
sc = sc.push(tmix.argsym); |
167 | 1 |
sc = sc.push(tmix); |
168 | 1 |
for (size_t i = 0; i < tmix.members.dim; i++) |
169 |
{
|
|
170 | 1 |
Dsymbol s = (*tmix.members)[i]; |
171 | 1 |
s.semantic3(sc); |
172 |
}
|
|
173 | 1 |
sc = sc.pop(); |
174 | 1 |
sc.pop(); |
175 |
}
|
|
176 |
}
|
|
177 |
|
|
178 |
override void visit(Module mod) |
|
179 |
{
|
|
180 |
//printf("Module::semantic3('%s'): parent = %p\n", toChars(), parent);
|
|
181 | 1 |
if (mod.semanticRun != PASS.semantic2done) |
182 |
return; |
|
183 | 1 |
mod.semanticRun = PASS.semantic3; |
184 |
// Note that modules get their own scope, from scratch.
|
|
185 |
// This is so regardless of where in the syntax a module
|
|
186 |
// gets imported, it is unaffected by context.
|
|
187 | 1 |
Scope* sc = Scope.createGlobal(mod); // create root scope |
188 |
//printf("Module = %p\n", sc.scopesym);
|
|
189 |
// Pass 3 semantic routines: do initializers and function bodies
|
|
190 | 1 |
for (size_t i = 0; i < mod.members.dim; i++) |
191 |
{
|
|
192 | 1 |
Dsymbol s = (*mod.members)[i]; |
193 |
//printf("Module %s: %s.semantic3()\n", toChars(), s.toChars());
|
|
194 | 1 |
s.semantic3(sc); |
195 |
|
|
196 | 1 |
mod.runDeferredSemantic2(); |
197 |
}
|
|
198 | 1 |
if (mod.userAttribDecl) |
199 |
{
|
|
200 | 1 |
mod.userAttribDecl.semantic3(sc); |
201 |
}
|
|
202 | 1 |
sc = sc.pop(); |
203 | 1 |
sc.pop(); |
204 | 1 |
mod.semanticRun = PASS.semantic3done; |
205 |
}
|
|
206 |
|
|
207 |
override void visit(FuncDeclaration funcdecl) |
|
208 |
{
|
|
209 |
/* Determine if function should add `return 0;`
|
|
210 |
*/
|
|
211 |
bool addReturn0() |
|
212 |
{
|
|
213 | 1 |
TypeFunction f = cast(TypeFunction)funcdecl.type; |
214 |
|
|
215 | 1 |
return f.next.ty == Tvoid && |
216 | 1 |
(funcdecl.isMain() || global.params.betterC && funcdecl.isCMain()); |
217 |
}
|
|
218 |
|
|
219 | 1 |
VarDeclaration _arguments = null; |
220 |
|
|
221 | 1 |
if (!funcdecl.parent) |
222 |
{
|
|
223 |
if (global.errors) |
|
224 |
return; |
|
225 |
//printf("FuncDeclaration::semantic3(%s '%s', sc = %p)\n", kind(), toChars(), sc);
|
|
226 |
assert(0); |
|
227 |
}
|
|
228 | 1 |
if (funcdecl.errors || isError(funcdecl.parent)) |
229 |
{
|
|
230 | 1 |
funcdecl.errors = true; |
231 | 1 |
return; |
232 |
}
|
|
233 |
//printf("FuncDeclaration::semantic3('%s.%s', %p, sc = %p, loc = %s)\n", funcdecl.parent.toChars(), funcdecl.toChars(), funcdecl, sc, funcdecl.loc.toChars());
|
|
234 |
//fflush(stdout);
|
|
235 |
//printf("storage class = x%x %x\n", sc.stc, storage_class);
|
|
236 |
//{ static int x; if (++x == 2) *(char*)0=0; }
|
|
237 |
//printf("\tlinkage = %d\n", sc.linkage);
|
|
238 |
|
|
239 | 1 |
if (funcdecl.ident == Id.assign && !funcdecl.inuse) |
240 |
{
|
|
241 | 1 |
if (funcdecl.storage_class & STC.inference) |
242 |
{
|
|
243 |
/* https://issues.dlang.org/show_bug.cgi?id=15044
|
|
244 |
* For generated opAssign function, any errors
|
|
245 |
* from its body need to be gagged.
|
|
246 |
*/
|
|
247 | 1 |
uint oldErrors = global.startGagging(); |
248 | 1 |
++funcdecl.inuse; |
249 | 1 |
funcdecl.semantic3(sc); |
250 | 1 |
--funcdecl.inuse; |
251 | 1 |
if (global.endGagging(oldErrors)) // if errors happened |
252 |
{
|
|
253 |
// Disable generated opAssign, because some members forbid identity assignment.
|
|
254 | 1 |
funcdecl.storage_class |= STC.disable; |
255 | 1 |
funcdecl.fbody = null; // remove fbody which contains the error |
256 | 1 |
funcdecl.semantic3Errors = false; |
257 |
}
|
|
258 | 1 |
return; |
259 |
}
|
|
260 |
}
|
|
261 |
|
|
262 |
//printf(" sc.incontract = %d\n", (sc.flags & SCOPE.contract));
|
|
263 | 1 |
if (funcdecl.semanticRun >= PASS.semantic3) |
264 | 1 |
return; |
265 | 1 |
funcdecl.semanticRun = PASS.semantic3; |
266 | 1 |
funcdecl.semantic3Errors = false; |
267 |
|
|
268 | 1 |
if (!funcdecl.type || funcdecl.type.ty != Tfunction) |
269 | 1 |
return; |
270 | 1 |
TypeFunction f = cast(TypeFunction)funcdecl.type; |
271 | 1 |
if (!funcdecl.inferRetType && f.next.ty == Terror) |
272 |
return; |
|
273 |
|
|
274 | 1 |
if (!funcdecl.fbody && funcdecl.inferRetType && !f.next) |
275 |
{
|
|
276 | 1 |
funcdecl.error("has no function body with return type inference"); |
277 | 1 |
return; |
278 |
}
|
|
279 |
|
|
280 | 1 |
uint oldErrors = global.errors; |
281 | 1 |
auto fds = FuncDeclSem3(funcdecl,sc); |
282 |
|
|
283 | 1 |
fds.checkInContractOverrides(); |
284 |
|
|
285 |
// Remember whether we need to generate an 'out' contract.
|
|
286 | 1 |
immutable bool needEnsure = FuncDeclaration.needsFensure(funcdecl); |
287 |
|
|
288 | 1 |
if (funcdecl.fbody || funcdecl.frequires || needEnsure) |
289 |
{
|
|
290 |
/* Symbol table into which we place parameters and nested functions,
|
|
291 |
* solely to diagnose name collisions.
|
|
292 |
*/
|
|
293 | 1 |
funcdecl.localsymtab = new DsymbolTable(); |
294 |
|
|
295 |
// Establish function scope
|
|
296 | 1 |
auto ss = new ScopeDsymbol(funcdecl.loc, null); |
297 |
// find enclosing scope symbol, might skip symbol-less CTFE and/or FuncExp scopes
|
|
298 | 1 |
for (auto scx = sc; ; scx = scx.enclosing) |
299 |
{
|
|
300 | 1 |
if (scx.scopesym) |
301 |
{
|
|
302 | 1 |
ss.parent = scx.scopesym; |
303 | 1 |
break; |
304 |
}
|
|
305 |
}
|
|
306 | 1 |
ss.endlinnum = funcdecl.endloc.linnum; |
307 | 1 |
Scope* sc2 = sc.push(ss); |
308 | 1 |
sc2.func = funcdecl; |
309 | 1 |
sc2.parent = funcdecl; |
310 | 1 |
sc2.ctorflow.callSuper = CSX.none; |
311 | 1 |
sc2.sbreak = null; |
312 | 1 |
sc2.scontinue = null; |
313 | 1 |
sc2.sw = null; |
314 | 1 |
sc2.fes = funcdecl.fes; |
315 | 1 |
sc2.linkage = LINK.d; |
316 | 1 |
sc2.stc &= STCFlowThruFunction; |
317 | 1 |
sc2.protection = Prot(Prot.Kind.public_); |
318 | 1 |
sc2.explicitProtection = 0; |
319 | 1 |
sc2.aligndecl = null; |
320 | 1 |
if (funcdecl.ident != Id.require && funcdecl.ident != Id.ensure) |
321 | 1 |
sc2.flags = sc.flags & ~SCOPE.contract; |
322 | 1 |
sc2.flags &= ~SCOPE.compile; |
323 | 1 |
sc2.tf = null; |
324 | 1 |
sc2.os = null; |
325 | 1 |
sc2.inLoop = false; |
326 | 1 |
sc2.userAttribDecl = null; |
327 | 1 |
if (sc2.intypeof == 1) |
328 | 1 |
sc2.intypeof = 2; |
329 | 1 |
sc2.ctorflow.fieldinit = null; |
330 |
|
|
331 |
/* Note: When a lambda is defined immediately under aggregate member
|
|
332 |
* scope, it should be contextless due to prevent interior pointers.
|
|
333 |
* e.g.
|
|
334 |
* // dg points 'this' - it's interior pointer
|
|
335 |
* class C { int x; void delegate() dg = (){ this.x = 1; }; }
|
|
336 |
*
|
|
337 |
* However, lambdas could be used inside typeof, in order to check
|
|
338 |
* some expressions validity at compile time. For such case the lambda
|
|
339 |
* body can access aggregate instance members.
|
|
340 |
* e.g.
|
|
341 |
* class C { int x; static assert(is(typeof({ this.x = 1; }))); }
|
|
342 |
*
|
|
343 |
* To properly accept it, mark these lambdas as member functions.
|
|
344 |
*/
|
|
345 | 1 |
if (auto fld = funcdecl.isFuncLiteralDeclaration()) |
346 |
{
|
|
347 | 1 |
if (auto ad = funcdecl.isMember2()) |
348 |
{
|
|
349 | 1 |
if (!sc.intypeof) |
350 |
{
|
|
351 | 1 |
if (fld.tok == TOK.delegate_) |
352 | 1 |
funcdecl.error("cannot be %s members", ad.kind()); |
353 |
else
|
|
354 | 1 |
fld.tok = TOK.function_; |
355 |
}
|
|
356 |
else
|
|
357 |
{
|
|
358 | 1 |
if (fld.tok != TOK.function_) |
359 | 1 |
fld.tok = TOK.delegate_; |
360 |
}
|
|
361 |
}
|
|
362 |
}
|
|
363 |
|
|
364 | 1 |
funcdecl.declareThis(sc2); |
365 |
|
|
366 |
//printf("[%s] ad = %p vthis = %p\n", loc.toChars(), ad, vthis);
|
|
367 |
//if (vthis) printf("\tvthis.type = %s\n", vthis.type.toChars());
|
|
368 |
|
|
369 |
// Declare hidden variable _arguments[] and _argptr
|
|
370 | 1 |
if (f.parameterList.varargs == VarArg.variadic) |
371 |
{
|
|
372 | 1 |
if (f.linkage == LINK.d) |
373 |
{
|
|
374 |
// Variadic arguments depend on Typeinfo being defined.
|
|
375 | 1 |
if (!global.params.useTypeInfo || !Type.dtypeinfo || !Type.typeinfotypelist) |
376 |
{
|
|
377 | 1 |
if (!global.params.useTypeInfo) |
378 | 1 |
funcdecl.error("D-style variadic functions cannot be used with -betterC"); |
379 | 1 |
else if (!Type.typeinfotypelist) |
380 | 1 |
funcdecl.error("`object.TypeInfo_Tuple` could not be found, but is implicitly used in D-style variadic functions"); |
381 |
else
|
|
382 | 1 |
funcdecl.error("`object.TypeInfo` could not be found, but is implicitly used in D-style variadic functions"); |
383 | 1 |
fatal(); |
384 |
}
|
|
385 |
|
|
386 |
// Declare _arguments[]
|
|
387 | 1 |
funcdecl.v_arguments = new VarDeclaration(funcdecl.loc, Type.typeinfotypelist.type, Id._arguments_typeinfo, null); |
388 | 1 |
funcdecl.v_arguments.storage_class |= STC.temp | STC.parameter; |
389 | 1 |
funcdecl.v_arguments.dsymbolSemantic(sc2); |
390 | 1 |
sc2.insert(funcdecl.v_arguments); |
391 | 1 |
funcdecl.v_arguments.parent = funcdecl; |
392 |
|
|
393 |
//Type t = Type.dtypeinfo.type.constOf().arrayOf();
|
|
394 | 1 |
Type t = Type.dtypeinfo.type.arrayOf(); |
395 | 1 |
_arguments = new VarDeclaration(funcdecl.loc, t, Id._arguments, null); |
396 | 1 |
_arguments.storage_class |= STC.temp; |
397 | 1 |
_arguments.dsymbolSemantic(sc2); |
398 | 1 |
sc2.insert(_arguments); |
399 | 1 |
_arguments.parent = funcdecl; |
400 |
}
|
|
401 | 1 |
if (f.linkage == LINK.d || f.parameterList.length) |
402 |
{
|
|
403 |
// Declare _argptr
|
|
404 | 1 |
Type t = target.va_listType(funcdecl.loc, sc); |
405 |
// Init is handled in FuncDeclaration_toObjFile
|
|
406 | 1 |
funcdecl.v_argptr = new VarDeclaration(funcdecl.loc, t, Id._argptr, new VoidInitializer(funcdecl.loc)); |
407 | 1 |
funcdecl.v_argptr.storage_class |= STC.temp; |
408 | 1 |
funcdecl.v_argptr.dsymbolSemantic(sc2); |
409 | 1 |
sc2.insert(funcdecl.v_argptr); |
410 | 1 |
funcdecl.v_argptr.parent = funcdecl; |
411 |
}
|
|
412 |
}
|
|
413 |
|
|
414 |
/* Declare all the function parameters as variables
|
|
415 |
* and install them in parameters[]
|
|
416 |
*/
|
|
417 | 1 |
if (const nparams = f.parameterList.length) |
418 |
{
|
|
419 |
/* parameters[] has all the tuples removed, as the back end
|
|
420 |
* doesn't know about tuples
|
|
421 |
*/
|
|
422 | 1 |
funcdecl.parameters = new VarDeclarations(); |
423 | 1 |
funcdecl.parameters.reserve(nparams); |
424 | 1 |
foreach (i, fparam; f.parameterList) |
425 |
{
|
|
426 | 1 |
Identifier id = fparam.ident; |
427 | 1 |
StorageClass stc = 0; |
428 | 1 |
if (!id) |
429 |
{
|
|
430 |
/* Generate identifier for un-named parameter,
|
|
431 |
* because we need it later on.
|
|
432 |
*/
|
|
433 | 1 |
fparam.ident = id = Identifier.generateId("_param_", i); |
434 | 1 |
stc |= STC.temp; |
435 |
}
|
|
436 | 1 |
Type vtype = fparam.type; |
437 | 1 |
auto v = new VarDeclaration(funcdecl.loc, vtype, id, null); |
438 |
//printf("declaring parameter %s of type %s\n", v.toChars(), v.type.toChars());
|
|
439 | 1 |
stc |= STC.parameter; |
440 | 1 |
if (f.parameterList.varargs == VarArg.typesafe && i + 1 == nparams) |
441 |
{
|
|
442 | 1 |
stc |= STC.variadic; |
443 | 1 |
auto vtypeb = vtype.toBasetype(); |
444 | 1 |
if (vtypeb.ty == Tarray) |
445 |
{
|
|
446 |
/* Since it'll be pointing into the stack for the array
|
|
447 |
* contents, it needs to be `scope`
|
|
448 |
*/
|
|
449 | 1 |
stc |= STC.scope_; |
450 |
}
|
|
451 |
}
|
|
452 |
|
|
453 | 1 |
if ((funcdecl.flags & FUNCFLAG.inferScope) && !(fparam.storageClass & STC.scope_)) |
454 | 1 |
stc |= STC.maybescope; |
455 |
|
|
456 | 1 |
stc |= fparam.storageClass & (STC.IOR | STC.return_ | STC.scope_ | STC.lazy_ | STC.final_ | STC.TYPECTOR | STC.nodtor); |
457 | 1 |
v.storage_class = stc; |
458 | 1 |
v.dsymbolSemantic(sc2); |
459 | 1 |
if (!sc2.insert(v)) |
460 |
{
|
|
461 | 1 |
funcdecl.error("parameter `%s.%s` is already defined", funcdecl.toChars(), v.toChars()); |
462 | 1 |
funcdecl.errors = true; |
463 |
}
|
|
464 |
else
|
|
465 | 1 |
funcdecl.parameters.push(v); |
466 | 1 |
funcdecl.localsymtab.insert(v); |
467 | 1 |
v.parent = funcdecl; |
468 | 1 |
if (fparam.userAttribDecl) |
469 | 1 |
v.userAttribDecl = fparam.userAttribDecl; |
470 |
}
|
|
471 |
}
|
|
472 |
|
|
473 |
// Declare the tuple symbols and put them in the symbol table,
|
|
474 |
// but not in parameters[].
|
|
475 | 1 |
if (f.parameterList.parameters) |
476 | 1 |
foreach (fparam; *f.parameterList.parameters) |
477 |
{
|
|
478 | 1 |
if (!fparam.ident) |
479 | 1 |
continue; // never used, so ignore |
480 |
// expand any tuples
|
|
481 | 1 |
if (fparam.type.ty != Ttuple) |
482 | 1 |
continue; |
483 |
|
|
484 | 1 |
TypeTuple t = cast(TypeTuple)fparam.type; |
485 | 1 |
size_t dim = Parameter.dim(t.arguments); |
486 | 1 |
auto exps = new Objects(dim); |
487 | 1 |
foreach (j; 0 .. dim) |
488 |
{
|
|
489 | 1 |
Parameter narg = Parameter.getNth(t.arguments, j); |
490 | 1 |
assert(narg.ident); |
491 | 1 |
VarDeclaration v = sc2.search(Loc.initial, narg.ident, null).isVarDeclaration(); |
492 | 1 |
assert(v); |
493 | 1 |
(*exps)[j] = new VarExp(v.loc, v); |
494 |
}
|
|
495 | 1 |
assert(fparam.ident); |
496 | 1 |
auto v = new TupleDeclaration(funcdecl.loc, fparam.ident, exps); |
497 |
//printf("declaring tuple %s\n", v.toChars());
|
|
498 | 1 |
v.isexp = true; |
499 | 1 |
if (!sc2.insert(v)) |
500 |
funcdecl.error("parameter `%s.%s` is already defined", funcdecl.toChars(), v.toChars()); |
|
501 | 1 |
funcdecl.localsymtab.insert(v); |
502 | 1 |
v.parent = funcdecl; |
503 |
}
|
|
504 |
|
|
505 |
// Precondition invariant
|
|
506 | 1 |
Statement fpreinv = null; |
507 | 1 |
if (funcdecl.addPreInvariant()) |
508 |
{
|
|
509 | 1 |
Expression e = addInvariant(funcdecl.isThis(), funcdecl.vthis); |
510 | 1 |
if (e) |
511 | 1 |
fpreinv = new ExpStatement(Loc.initial, e); |
512 |
}
|
|
513 |
|
|
514 |
// Postcondition invariant
|
|
515 | 1 |
Statement fpostinv = null; |
516 | 1 |
if (funcdecl.addPostInvariant()) |
517 |
{
|
|
518 | 1 |
Expression e = addInvariant(funcdecl.isThis(), funcdecl.vthis); |
519 | 1 |
if (e) |
520 | 1 |
fpostinv = new ExpStatement(Loc.initial, e); |
521 |
}
|
|
522 |
|
|
523 |
// Pre/Postcondition contract
|
|
524 | 1 |
if (!funcdecl.fbody) |
525 | 1 |
funcdecl.buildEnsureRequire(); |
526 |
|
|
527 | 1 |
Scope* scout = null; |
528 | 1 |
if (needEnsure || funcdecl.addPostInvariant()) |
529 |
{
|
|
530 |
/* https://issues.dlang.org/show_bug.cgi?id=3657
|
|
531 |
* Set the correct end line number for fensure scope.
|
|
532 |
*/
|
|
533 | 1 |
uint fensure_endlin = funcdecl.endloc.linnum; |
534 | 1 |
if (funcdecl.fensure) |
535 | 1 |
if (auto s = funcdecl.fensure.isScopeStatement()) |
536 |
fensure_endlin = s.endloc.linnum; |
|
537 |
|
|
538 | 1 |
if ((needEnsure && global.params.useOut == CHECKENABLE.on) || fpostinv) |
539 |
{
|
|
540 | 1 |
funcdecl.returnLabel = funcdecl.searchLabel(Id.returnLabel); |
541 |
}
|
|
542 |
|
|
543 |
// scope of out contract (need for vresult.semantic)
|
|
544 | 1 |
auto sym = new ScopeDsymbol(funcdecl.loc, null); |
545 | 1 |
sym.parent = sc2.scopesym; |
546 | 1 |
sym.endlinnum = fensure_endlin; |
547 | 1 |
scout = sc2.push(sym); |
548 |
}
|
|
549 |
|
|
550 | 1 |
if (funcdecl.fbody) |
551 |
{
|
|
552 | 1 |
auto sym = new ScopeDsymbol(funcdecl.loc, null); |
553 | 1 |
sym.parent = sc2.scopesym; |
554 | 1 |
sym.endlinnum = funcdecl.endloc.linnum; |
555 | 1 |
sc2 = sc2.push(sym); |
556 |
|
|
557 | 1 |
auto ad2 = funcdecl.isMemberLocal(); |
558 |
|
|
559 |
/* If this is a class constructor
|
|
560 |
*/
|
|
561 | 1 |
if (ad2 && funcdecl.isCtorDeclaration()) |
562 |
{
|
|
563 | 1 |
sc2.ctorflow.allocFieldinit(ad2.fields.dim); |
564 | 1 |
foreach (v; ad2.fields) |
565 |
{
|
|
566 | 1 |
v.ctorinit = 0; |
567 |
}
|
|
568 |
}
|
|
569 |
|
|
570 | 1 |
if (!funcdecl.inferRetType && !target.isReturnOnStack(f, funcdecl.needThis())) |
571 | 1 |
funcdecl.nrvo_can = 0; |
572 |
|
|
573 | 1 |
bool inferRef = (f.isref && (funcdecl.storage_class & STC.auto_)); |
574 |
|
|
575 | 1 |
funcdecl.fbody = funcdecl.fbody.statementSemantic(sc2); |
576 | 1 |
if (!funcdecl.fbody) |
577 | 1 |
funcdecl.fbody = new CompoundStatement(Loc.initial, new Statements()); |
578 |
|
|
579 | 1 |
if (funcdecl.naked) |
580 |
{
|
|
581 | 1 |
fpreinv = null; // can't accommodate with no stack frame |
582 | 1 |
fpostinv = null; |
583 |
}
|
|
584 |
|
|
585 | 1 |
assert(funcdecl.type == f || (funcdecl.type.ty == Tfunction && f.purity == PURE.impure && (cast(TypeFunction)funcdecl.type).purity >= PURE.fwdref)); |
586 | 1 |
f = cast(TypeFunction)funcdecl.type; |
587 |
|
|
588 | 1 |
if (funcdecl.inferRetType) |
589 |
{
|
|
590 |
// If no return type inferred yet, then infer a void
|
|
591 | 1 |
if (!f.next) |
592 | 1 |
f.next = Type.tvoid; |
593 | 1 |
if (f.checkRetType(funcdecl.loc)) |
594 | 1 |
funcdecl.fbody = new ErrorStatement(); |
595 |
}
|
|
596 | 1 |
if (global.params.vcomplex && f.next !is null) |
597 | 1 |
f.next.checkComplexTransition(funcdecl.loc, sc); |
598 |
|
|
599 | 1 |
if (funcdecl.returns && !funcdecl.fbody.isErrorStatement()) |
600 |
{
|
|
601 | 1 |
for (size_t i = 0; i < funcdecl.returns.dim;) |
602 |
{
|
|
603 | 1 |
Expression exp = (*funcdecl.returns)[i].exp; |
604 | 1 |
if (exp.op == TOK.variable && (cast(VarExp)exp).var == funcdecl.vresult) |
605 |
{
|
|
606 | 1 |
if (addReturn0()) |
607 | 1 |
exp.type = Type.tint32; |
608 |
else
|
|
609 | 1 |
exp.type = f.next; |
610 |
// Remove `return vresult;` from returns
|
|
611 | 1 |
funcdecl.returns.remove(i); |
612 | 1 |
continue; |
613 |
}
|
|
614 | 1 |
if (inferRef && f.isref && !exp.type.constConv(f.next)) // https://issues.dlang.org/show_bug.cgi?id=13336 |
615 | 1 |
f.isref = false; |
616 | 1 |
i++; |
617 |
}
|
|
618 |
}
|
|
619 | 1 |
if (f.isref) // Function returns a reference |
620 |
{
|
|
621 | 1 |
if (funcdecl.storage_class & STC.auto_) |
622 | 1 |
funcdecl.storage_class &= ~STC.auto_; |
623 |
}
|
|
624 |
|
|
625 |
// handle NRVO
|
|
626 | 1 |
if (!target.isReturnOnStack(f, funcdecl.needThis()) || funcdecl.checkNrvo()) |
627 | 1 |
funcdecl.nrvo_can = 0; |
628 |
|
|
629 | 1 |
if (funcdecl.fbody.isErrorStatement()) |
630 |
{
|
|
631 |
}
|
|
632 | 1 |
else if (funcdecl.isStaticCtorDeclaration()) |
633 |
{
|
|
634 |
/* It's a static constructor. Ensure that all
|
|
635 |
* ctor consts were initialized.
|
|
636 |
*/
|
|
637 | 1 |
ScopeDsymbol pd = funcdecl.toParent().isScopeDsymbol(); |
638 | 1 |
for (size_t i = 0; i < pd.members.dim; i++) |
639 |
{
|
|
640 | 1 |
Dsymbol s = (*pd.members)[i]; |
641 | 1 |
s.checkCtorConstInit(); |
642 |
}
|
|
643 |
}
|
|
644 | 1 |
else if (ad2 && funcdecl.isCtorDeclaration()) |
645 |
{
|
|
646 | 1 |
ClassDeclaration cd = ad2.isClassDeclaration(); |
647 |
|
|
648 |
// Verify that all the ctorinit fields got initialized
|
|
649 | 1 |
if (!(sc2.ctorflow.callSuper & CSX.this_ctor)) |
650 |
{
|
|
651 | 1 |
foreach (i, v; ad2.fields) |
652 |
{
|
|
653 | 1 |
if (v.isThisDeclaration()) |
654 | 1 |
continue; |
655 | 1 |
if (v.ctorinit == 0) |
656 |
{
|
|
657 |
/* Current bugs in the flow analysis:
|
|
658 |
* 1. union members should not produce error messages even if
|
|
659 |
* not assigned to
|
|
660 |
* 2. structs should recognize delegating opAssign calls as well
|
|
661 |
* as delegating calls to other constructors
|
|
662 |
*/
|
|
663 | 1 |
if (v.isCtorinit() && !v.type.isMutable() && cd) |
664 | 1 |
funcdecl.error("missing initializer for %s field `%s`", MODtoChars(v.type.mod), v.toChars()); |
665 | 1 |
else if (v.storage_class & STC.nodefaultctor) |
666 | 1 |
error(funcdecl.loc, "field `%s` must be initialized in constructor", v.toChars()); |
667 | 1 |
else if (v.type.needsNested()) |
668 | 1 |
error(funcdecl.loc, "field `%s` must be initialized in constructor, because it is nested struct", v.toChars()); |
669 |
}
|
|
670 |
else
|
|
671 |
{
|
|
672 | 1 |
bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested()); |
673 | 1 |
if (mustInit && !(sc2.ctorflow.fieldinit[i].csx & CSX.this_ctor)) |
674 |
{
|
|
675 |
funcdecl.error("field `%s` must be initialized but skipped", v.toChars()); |
|
676 |
}
|
|
677 |
}
|
|
678 |
}
|
|
679 |
}
|
|
680 | 1 |
sc2.ctorflow.freeFieldinit(); |
681 |
|
|
682 | 1 |
if (cd && !(sc2.ctorflow.callSuper & CSX.any_ctor) && cd.baseClass && cd.baseClass.ctor) |
683 |
{
|
|
684 | 1 |
sc2.ctorflow.callSuper = CSX.none; |
685 |
|
|
686 |
// Insert implicit super() at start of fbody
|
|
687 | 1 |
Type tthis = ad2.type.addMod(funcdecl.vthis.type.mod); |
688 | 1 |
FuncDeclaration fd = resolveFuncCall(Loc.initial, sc2, cd.baseClass.ctor, null, tthis, null, FuncResolveFlag.quiet); |
689 | 1 |
if (!fd) |
690 |
{
|
|
691 | 1 |
funcdecl.error("no match for implicit `super()` call in constructor"); |
692 |
}
|
|
693 | 1 |
else if (fd.storage_class & STC.disable) |
694 |
{
|
|
695 | 1 |
funcdecl.error("cannot call `super()` implicitly because it is annotated with `@disable`"); |
696 |
}
|
|
697 |
else
|
|
698 |
{
|
|
699 | 1 |
Expression e1 = new SuperExp(Loc.initial); |
700 | 1 |
Expression e = new CallExp(Loc.initial, e1); |
701 | 1 |
e = e.expressionSemantic(sc2); |
702 | 1 |
Statement s = new ExpStatement(Loc.initial, e); |
703 | 1 |
funcdecl.fbody = new CompoundStatement(Loc.initial, s, funcdecl.fbody); |
704 |
}
|
|
705 |
}
|
|
706 |
//printf("ctorflow.callSuper = x%x\n", sc2.ctorflow.callSuper);
|
|
707 |
}
|
|
708 |
|
|
709 |
/* https://issues.dlang.org/show_bug.cgi?id=17502
|
|
710 |
* Wait until after the return type has been inferred before
|
|
711 |
* generating the contracts for this function, and merging contracts
|
|
712 |
* from overrides.
|
|
713 |
*
|
|
714 |
* https://issues.dlang.org/show_bug.cgi?id=17893
|
|
715 |
* However should take care to generate this before inferered
|
|
716 |
* function attributes are applied, such as 'nothrow'.
|
|
717 |
*
|
|
718 |
* This was originally at the end of the first semantic pass, but
|
|
719 |
* required a fix-up to be done here for the '__result' variable
|
|
720 |
* type of __ensure() inside auto functions, but this didn't work
|
|
721 |
* if the out parameter was implicit.
|
|
722 |
*/
|
|
723 | 1 |
funcdecl.buildEnsureRequire(); |
724 |
|
|
725 |
// Check for errors related to 'nothrow'.
|
|
726 | 1 |
const blockexit = funcdecl.fbody.blockExit(funcdecl, f.isnothrow); |
727 | 1 |
if (f.isnothrow && blockexit & BE.throw_) |
728 | 1 |
error(funcdecl.loc, "`nothrow` %s `%s` may throw", funcdecl.kind(), funcdecl.toPrettyChars()); |
729 |
|
|
730 | 1 |
if (!(blockexit & (BE.throw_ | BE.halt) || funcdecl.flags & FUNCFLAG.hasCatches)) |
731 |
{
|
|
732 |
/* Disable optimization on Win32 due to
|
|
733 |
* https://issues.dlang.org/show_bug.cgi?id=17997
|
|
734 |
*/
|
|
735 |
// if (!global.params.isWindows || global.params.is64bit)
|
|
736 | 1 |
funcdecl.eh_none = true; // don't generate unwind tables for this function |
737 |
}
|
|
738 |
|
|
739 | 1 |
if (funcdecl.flags & FUNCFLAG.nothrowInprocess) |
740 |
{
|
|
741 | 1 |
if (funcdecl.type == f) |
742 | 1 |
f = cast(TypeFunction)f.copy(); |
743 | 1 |
f.isnothrow = !(blockexit & BE.throw_); |
744 |
}
|
|
745 |
|
|
746 | 1 |
if (funcdecl.fbody.isErrorStatement()) |
747 |
{
|
|
748 |
}
|
|
749 | 1 |
else if (ad2 && funcdecl.isCtorDeclaration()) |
750 |
{
|
|
751 |
/* Append:
|
|
752 |
* return this;
|
|
753 |
* to function body
|
|
754 |
*/
|
|
755 | 1 |
if (blockexit & BE.fallthru) |
756 |
{
|
|
757 | 1 |
Statement s = new ReturnStatement(funcdecl.loc, null); |
758 | 1 |
s = s.statementSemantic(sc2); |
759 | 1 |
funcdecl.fbody = new CompoundStatement(funcdecl.loc, funcdecl.fbody, s); |
760 | 1 |
funcdecl.hasReturnExp |= (funcdecl.hasReturnExp & 1 ? 16 : 1); |
761 |
}
|
|
762 |
}
|
|
763 | 1 |
else if (funcdecl.fes) |
764 |
{
|
|
765 |
// For foreach(){} body, append a return 0;
|
|
766 | 1 |
if (blockexit & BE.fallthru) |
767 |
{
|
|
768 | 1 |
Expression e = IntegerExp.literal!0; |
769 | 1 |
Statement s = new ReturnStatement(Loc.initial, e); |
770 | 1 |
funcdecl.fbody = new CompoundStatement(Loc.initial, funcdecl.fbody, s); |
771 | 1 |
funcdecl.hasReturnExp |= (funcdecl.hasReturnExp & 1 ? 16 : 1); |
772 |
}
|
|
773 | 1 |
assert(!funcdecl.returnLabel); |
774 |
}
|
|
775 |
else
|
|
776 |
{
|
|
777 | 1 |
const(bool) inlineAsm = (funcdecl.hasReturnExp & 8) != 0; |
778 | 1 |
if ((blockexit & BE.fallthru) && f.next.ty != Tvoid && !inlineAsm) |
779 |
{
|
|
780 | 1 |
Expression e; |
781 | 1 |
if (!funcdecl.hasReturnExp) |
782 | 1 |
funcdecl.error("has no `return` statement, but is expected to return a value of type `%s`", f.next.toChars()); |
783 |
else
|
|
784 | 1 |
funcdecl.error("no `return exp;` or `assert(0);` at end of function"); |
785 | 1 |
if (global.params.useAssert == CHECKENABLE.on && !global.params.useInline) |
786 |
{
|
|
787 |
/* Add an assert(0, msg); where the missing return
|
|
788 |
* should be.
|
|
789 |
*/
|
|
790 | 1 |
e = new AssertExp(funcdecl.endloc, IntegerExp.literal!0, new StringExp(funcdecl.loc, "missing return expression")); |
791 |
}
|
|
792 |
else
|
|
793 |
e = new HaltExp(funcdecl.endloc); |
|
794 | 1 |
e = new CommaExp(Loc.initial, e, f.next.defaultInit(Loc.initial)); |
795 | 1 |
e = e.expressionSemantic(sc2); |
796 | 1 |
Statement s = new ExpStatement(Loc.initial, e); |
797 | 1 |
funcdecl.fbody = new CompoundStatement(Loc.initial, funcdecl.fbody, s); |
798 |
}
|
|
799 |
}
|
|
800 |
|
|
801 | 1 |
if (funcdecl.returns) |
802 |
{
|
|
803 | 1 |
bool implicit0 = addReturn0(); |
804 | 1 |
Type tret = implicit0 ? Type.tint32 : f.next; |
805 | 1 |
assert(tret.ty != Tvoid); |
806 | 1 |
if (funcdecl.vresult || funcdecl.returnLabel) |
807 | 1 |
funcdecl.buildResultVar(scout ? scout : sc2, tret); |
808 |
|
|
809 |
/* Cannot move this loop into NrvoWalker, because
|
|
810 |
* returns[i] may be in the nested delegate for foreach-body.
|
|
811 |
*/
|
|
812 | 1 |
for (size_t i = 0; i < funcdecl.returns.dim; i++) |
813 |
{
|
|
814 | 1 |
ReturnStatement rs = (*funcdecl.returns)[i]; |
815 | 1 |
Expression exp = rs.exp; |
816 | 1 |
if (exp.op == TOK.error) |
817 | 1 |
continue; |
818 | 1 |
if (tret.ty == Terror) |
819 |
{
|
|
820 |
// https://issues.dlang.org/show_bug.cgi?id=13702
|
|
821 | 1 |
exp = checkGC(sc2, exp); |
822 | 1 |
continue; |
823 |
}
|
|
824 |
|
|
825 |
/* If the expression in the return statement (exp) cannot be implicitly
|
|
826 |
* converted to the return type (tret) of the function and if the
|
|
827 |
* type of the expression is type isolated, then it may be possible
|
|
828 |
* that a promotion to `immutable` or `inout` (through a cast) will
|
|
829 |
* match the return type.
|
|
830 |
*/
|
|
831 | 1 |
if (!exp.implicitConvTo(tret) && funcdecl.isTypeIsolated(exp.type)) |
832 |
{
|
|
833 |
/* https://issues.dlang.org/show_bug.cgi?id=20073
|
|
834 |
*
|
|
835 |
* The problem is that if the type of the returned expression (exp.type)
|
|
836 |
* is an aggregated declaration with an alias this, the alias this may be
|
|
837 |
* used for the conversion testing without it being an isolated type.
|
|
838 |
*
|
|
839 |
* To make sure this does not happen, we can test here the implicit conversion
|
|
840 |
* only for the aggregated declaration type by using `implicitConvToWithoutAliasThis`.
|
|
841 |
* The implicit conversion with alias this is taken care of later.
|
|
842 |
*/
|
|
843 | 1 |
AggregateDeclaration aggDecl = isAggregate(exp.type); |
844 | 1 |
TypeStruct tstruct; |
845 | 1 |
TypeClass tclass; |
846 | 1 |
bool hasAliasThis; |
847 | 1 |
if (aggDecl && aggDecl.aliasthis) |
848 |
{
|
|
849 | 1 |
hasAliasThis = true; |
850 | 1 |
tclass = exp.type.isTypeClass(); |
851 | 1 |
if (!tclass) |
852 | 1 |
tstruct = exp.type.isTypeStruct(); |
853 | 1 |
assert(tclass || tstruct); |
854 |
}
|
|
855 | 1 |
if (hasAliasThis) |
856 |
{
|
|
857 | 1 |
if (tclass) |
858 |
{
|
|
859 |
if ((cast(TypeClass)(exp.type.immutableOf())).implicitConvToWithoutAliasThis(tret)) |
|
860 |
exp = exp.castTo(sc2, exp.type.immutableOf()); |
|
861 |
else if ((cast(TypeClass)(exp.type.wildOf())).implicitConvToWithoutAliasThis(tret)) |
|
862 |
exp = exp.castTo(sc2, exp.type.wildOf()); |
|
863 |
}
|
|
864 |
else
|
|
865 |
{
|
|
866 | 1 |
if ((cast(TypeStruct)exp.type.immutableOf()).implicitConvToWithoutAliasThis(tret)) |
867 |
exp = exp.castTo(sc2, exp.type.immutableOf()); |
|
868 | 1 |
else if ((cast(TypeStruct)exp.type.immutableOf()).implicitConvToWithoutAliasThis(tret)) |
869 |
exp = exp.castTo(sc2, exp.type.wildOf()); |
|
870 |
}
|
|
871 |
}
|
|
872 |
else
|
|
873 |
{
|
|
874 | 1 |
if (exp.type.immutableOf().implicitConvTo(tret)) |
875 | 1 |
exp = exp.castTo(sc2, exp.type.immutableOf()); |
876 | 1 |
else if (exp.type.wildOf().implicitConvTo(tret)) |
877 | 1 |
exp = exp.castTo(sc2, exp.type.wildOf()); |
878 |
}
|
|
879 |
}
|
|
880 |
|
|
881 | 1 |
const hasCopyCtor = exp.type.ty == Tstruct && (cast(TypeStruct)exp.type).sym.hasCopyCtor; |
882 |
// if a copy constructor is present, the return type conversion will be handled by it
|
|
883 | 1 |
if (!(hasCopyCtor && exp.isLvalue())) |
884 | 1 |
exp = exp.implicitCastTo(sc2, tret); |
885 |
|
|
886 | 1 |
if (f.isref) |
887 |
{
|
|
888 |
// Function returns a reference
|
|
889 | 1 |
exp = exp.toLvalue(sc2, exp); |
890 | 1 |
checkReturnEscapeRef(sc2, exp, false); |
891 |
}
|
|
892 |
else
|
|
893 |
{
|
|
894 | 1 |
exp = exp.optimize(WANTvalue); |
895 |
|
|
896 |
/* https://issues.dlang.org/show_bug.cgi?id=10789
|
|
897 |
* If NRVO is not possible, all returned lvalues should call their postblits.
|
|
898 |
*/
|
|
899 | 1 |
if (!funcdecl.nrvo_can) |
900 | 1 |
exp = doCopyOrMove(sc2, exp, f.next); |
901 |
|
|
902 | 1 |
if (tret.hasPointers()) |
903 | 1 |
checkReturnEscape(sc2, exp, false); |
904 |
}
|
|
905 |
|
|
906 | 1 |
exp = checkGC(sc2, exp); |
907 |
|
|
908 | 1 |
if (funcdecl.vresult) |
909 |
{
|
|
910 |
// Create: return vresult = exp;
|
|
911 | 1 |
exp = new BlitExp(rs.loc, funcdecl.vresult, exp); |
912 | 1 |
exp.type = funcdecl.vresult.type; |
913 |
|
|
914 | 1 |
if (rs.caseDim) |
915 | 1 |
exp = Expression.combine(exp, new IntegerExp(rs.caseDim)); |
916 |
}
|
|
917 | 1 |
else if (funcdecl.tintro && !tret.equals(funcdecl.tintro.nextOf())) |
918 |
{
|
|
919 | 1 |
exp = exp.implicitCastTo(sc2, funcdecl.tintro.nextOf()); |
920 |
}
|
|
921 | 1 |
rs.exp = exp; |
922 |
}
|
|
923 |
}
|
|
924 | 1 |
if (funcdecl.nrvo_var || funcdecl.returnLabel) |
925 |
{
|
|
926 | 1 |
scope NrvoWalker nw = new NrvoWalker(); |
927 | 1 |
nw.fd = funcdecl; |
928 | 1 |
nw.sc = sc2; |
929 | 1 |
nw.visitStmt(funcdecl.fbody); |
930 |
}
|
|
931 |
|
|
932 | 1 |
sc2 = sc2.pop(); |
933 |
}
|
|
934 |
|
|
935 | 1 |
funcdecl.frequire = funcdecl.mergeFrequire(funcdecl.frequire, funcdecl.fdrequireParams); |
936 | 1 |
funcdecl.fensure = funcdecl.mergeFensure(funcdecl.fensure, Id.result, funcdecl.fdensureParams); |
937 |
|
|
938 | 1 |
Statement freq = funcdecl.frequire; |
939 | 1 |
Statement fens = funcdecl.fensure; |
940 |
|
|
941 |
/* Do the semantic analysis on the [in] preconditions and
|
|
942 |
* [out] postconditions.
|
|
943 |
*/
|
|
944 | 1 |
if (freq) |
945 |
{
|
|
946 |
/* frequire is composed of the [in] contracts
|
|
947 |
*/
|
|
948 | 1 |
auto sym = new ScopeDsymbol(funcdecl.loc, null); |
949 | 1 |
sym.parent = sc2.scopesym; |
950 | 1 |
sym.endlinnum = funcdecl.endloc.linnum; |
951 | 1 |
sc2 = sc2.push(sym); |
952 | 1 |
sc2.flags = (sc2.flags & ~SCOPE.contract) | SCOPE.require; |
953 |
|
|
954 |
// BUG: need to error if accessing out parameters
|
|
955 |
// BUG: need to disallow returns and throws
|
|
956 |
// BUG: verify that all in and ref parameters are read
|
|
957 | 1 |
freq = freq.statementSemantic(sc2); |
958 | 1 |
freq.blockExit(funcdecl, false); |
959 |
|
|
960 | 1 |
funcdecl.eh_none = false; |
961 |
|
|
962 | 1 |
sc2 = sc2.pop(); |
963 |
|
|
964 | 1 |
if (global.params.useIn == CHECKENABLE.off) |
965 | 1 |
freq = null; |
966 |
}
|
|
967 | 1 |
if (fens) |
968 |
{
|
|
969 |
/* fensure is composed of the [out] contracts
|
|
970 |
*/
|
|
971 | 1 |
if (f.next.ty == Tvoid && funcdecl.fensures) |
972 |
{
|
|
973 | 1 |
foreach (e; *funcdecl.fensures) |
974 |
{
|
|
975 | 1 |
if (e.id) |
976 |
{
|
|
977 | 1 |
funcdecl.error(e.ensure.loc, "`void` functions have no result"); |
978 |
//fens = null;
|
|
979 |
}
|
|
980 |
}
|
|
981 |
}
|
|
982 |
|
|
983 | 1 |
sc2 = scout; //push |
984 | 1 |
sc2.flags = (sc2.flags & ~SCOPE.contract) | SCOPE.ensure; |
985 |
|
|
986 |
// BUG: need to disallow returns and throws
|
|
987 |
|
|
988 | 1 |
if (funcdecl.fensure && f.next.ty != Tvoid) |
989 | 1 |
funcdecl.buildResultVar(scout, f.next); |
990 |
|
|
991 | 1 |
fens = fens.statementSemantic(sc2); |
992 | 1 |
fens.blockExit(funcdecl, false); |
993 |
|
|
994 | 1 |
funcdecl.eh_none = false; |
995 |
|
|
996 | 1 |
sc2 = sc2.pop(); |
997 |
|
|
998 | 1 |
if (global.params.useOut == CHECKENABLE.off) |
999 | 1 |
fens = null; |
1000 |
}
|
|
1001 | 1 |
if (funcdecl.fbody && funcdecl.fbody.isErrorStatement()) |
1002 |
{
|
|
1003 |
}
|
|
1004 |
else
|
|
1005 |
{
|
|
1006 | 1 |
auto a = new Statements(); |
1007 |
// Merge in initialization of 'out' parameters
|
|
1008 | 1 |
if (funcdecl.parameters) |
1009 |
{
|
|
1010 | 1 |
for (size_t i = 0; i < funcdecl.parameters.dim; i++) |
1011 |
{
|
|
1012 | 1 |
VarDeclaration v = (*funcdecl.parameters)[i]; |
1013 | 1 |
if (v.storage_class & STC.out_) |
1014 |
{
|
|
1015 | 1 |
if (!v._init) |
1016 |
{
|
|
1017 | 1 |
v.error("Zero-length `out` parameters are not allowed."); |
1018 | 1 |
return; |
1019 |
}
|
|
1020 | 1 |
ExpInitializer ie = v._init.isExpInitializer(); |
1021 | 1 |
assert(ie); |
1022 | 1 |
if (auto iec = ie.exp.isConstructExp()) |
1023 |
{
|
|
1024 |
// construction occurred in parameter processing
|
|
1025 |
auto ec = new AssignExp(iec.loc, iec.e1, iec.e2); |
|
1026 |
ec.type = iec.type; |
|
1027 |
ie.exp = ec; |
|
1028 |
}
|
|
1029 | 1 |
a.push(new ExpStatement(Loc.initial, ie.exp)); |
1030 |
}
|
|
1031 |
}
|
|
1032 |
}
|
|
1033 |
|
|
1034 | 1 |
if (_arguments) |
1035 |
{
|
|
1036 |
/* Advance to elements[] member of TypeInfo_Tuple with:
|
|
1037 |
* _arguments = v_arguments.elements;
|
|
1038 |
*/
|
|
1039 | 1 |
Expression e = new VarExp(Loc.initial, funcdecl.v_arguments); |
1040 | 1 |
e = new DotIdExp(Loc.initial, e, Id.elements); |
1041 | 1 |
e = new ConstructExp(Loc.initial, _arguments, e); |
1042 | 1 |
e = e.expressionSemantic(sc2); |
1043 |
|
|
1044 | 1 |
_arguments._init = new ExpInitializer(Loc.initial, e); |
1045 | 1 |
auto de = new DeclarationExp(Loc.initial, _arguments); |
1046 | 1 |
a.push(new ExpStatement(Loc.initial, de)); |
1047 |
}
|
|
1048 |
|
|
1049 |
// Merge contracts together with body into one compound statement
|
|
1050 |
|
|
1051 | 1 |
if (freq || fpreinv) |
1052 |
{
|
|
1053 | 1 |
if (!freq) |
1054 | 1 |
freq = fpreinv; |
1055 | 1 |
else if (fpreinv) |
1056 | 1 |
freq = new CompoundStatement(Loc.initial, freq, fpreinv); |
1057 |
|
|
1058 | 1 |
a.push(freq); |
1059 |
}
|
|
1060 |
|
|
1061 | 1 |
if (funcdecl.fbody) |
1062 | 1 |
a.push(funcdecl.fbody); |
1063 |
|
|
1064 | 1 |
if (fens || fpostinv) |
1065 |
{
|
|
1066 | 1 |
if (!fens) |
1067 | 1 |
fens = fpostinv; |
1068 | 1 |
else if (fpostinv) |
1069 | 1 |
fens = new CompoundStatement(Loc.initial, fpostinv, fens); |
1070 |
|
|
1071 | 1 |
auto ls = new LabelStatement(Loc.initial, Id.returnLabel, fens); |
1072 | 1 |
funcdecl.returnLabel.statement = ls; |
1073 | 1 |
a.push(funcdecl.returnLabel.statement); |
1074 |
|
|
1075 | 1 |
if (f.next.ty != Tvoid && funcdecl.vresult) |
1076 |
{
|
|
1077 |
// Create: return vresult;
|
|
1078 | 1 |
Expression e = new VarExp(Loc.initial, funcdecl.vresult); |
1079 | 1 |
if (funcdecl.tintro) |
1080 |
{
|
|
1081 | 1 |
e = e.implicitCastTo(sc, funcdecl.tintro.nextOf()); |
1082 | 1 |
e = e.expressionSemantic(sc); |
1083 |
}
|
|
1084 | 1 |
auto s = new ReturnStatement(Loc.initial, e); |
1085 | 1 |
a.push(s); |
1086 |
}
|
|
1087 |
}
|
|
1088 | 1 |
if (addReturn0()) |
1089 |
{
|
|
1090 |
// Add a return 0; statement
|
|
1091 | 1 |
Statement s = new ReturnStatement(Loc.initial, IntegerExp.literal!0); |
1092 | 1 |
a.push(s); |
1093 |
}
|
|
1094 |
|
|
1095 | 1 |
Statement sbody = new CompoundStatement(Loc.initial, a); |
1096 |
|
|
1097 |
/* Append destructor calls for parameters as finally blocks.
|
|
1098 |
*/
|
|
1099 | 1 |
if (funcdecl.parameters) |
1100 |
{
|
|
1101 | 1 |
foreach (v; *funcdecl.parameters) |
1102 |
{
|
|
1103 | 1 |
if (v.storage_class & (STC.ref_ | STC.out_ | STC.lazy_)) |
1104 | 1 |
continue; |
1105 | 1 |
if (v.needsScopeDtor()) |
1106 |
{
|
|
1107 |
// same with ExpStatement.scopeCode()
|
|
1108 | 1 |
Statement s = new DtorExpStatement(Loc.initial, v.edtor, v); |
1109 | 1 |
v.storage_class |= STC.nodtor; |
1110 |
|
|
1111 | 1 |
s = s.statementSemantic(sc2); |
1112 |
|
|
1113 | 1 |
bool isnothrow = f.isnothrow & !(funcdecl.flags & FUNCFLAG.nothrowInprocess); |
1114 | 1 |
const blockexit = s.blockExit(funcdecl, isnothrow); |
1115 | 1 |
if (blockexit & BE.throw_) |
1116 | 1 |
funcdecl.eh_none = false; |
1117 | 1 |
if (f.isnothrow && isnothrow && blockexit & BE.throw_) |
1118 | 1 |
error(funcdecl.loc, "`nothrow` %s `%s` may throw", funcdecl.kind(), funcdecl.toPrettyChars()); |
1119 | 1 |
if (funcdecl.flags & FUNCFLAG.nothrowInprocess && blockexit & BE.throw_) |
1120 | 1 |
f.isnothrow = false; |
1121 |
|
|
1122 | 1 |
if (sbody.blockExit(funcdecl, f.isnothrow) == BE.fallthru) |
1123 | 1 |
sbody = new CompoundStatement(Loc.initial, sbody, s); |
1124 |
else
|
|
1125 | 1 |
sbody = new TryFinallyStatement(Loc.initial, sbody, s); |
1126 |
}
|
|
1127 |
}
|
|
1128 |
}
|
|
1129 |
// from this point on all possible 'throwers' are checked
|
|
1130 | 1 |
funcdecl.flags &= ~FUNCFLAG.nothrowInprocess; |
1131 |
|
|
1132 | 1 |
if (funcdecl.isSynchronized()) |
1133 |
{
|
|
1134 |
/* Wrap the entire function body in a synchronized statement
|
|
1135 |
*/
|
|
1136 | 1 |
ClassDeclaration cd = funcdecl.toParentDecl().isClassDeclaration(); |
1137 | 1 |
if (cd) |
1138 |
{
|
|
1139 | 1 |
if (!global.params.is64bit && global.params.isWindows && !funcdecl.isStatic() && !sbody.usesEH() && !global.params.trace) |
1140 |
{
|
|
1141 |
/* The back end uses the "jmonitor" hack for syncing;
|
|
1142 |
* no need to do the sync at this level.
|
|
1143 |
*/
|
|
1144 |
}
|
|
1145 |
else
|
|
1146 |
{
|
|
1147 | 1 |
Expression vsync; |
1148 | 1 |
if (funcdecl.isStatic()) |
1149 |
{
|
|
1150 |
// The monitor is in the ClassInfo
|
|
1151 | 1 |
vsync = new DotIdExp(funcdecl.loc, symbolToExp(cd, funcdecl.loc, sc2, false), Id.classinfo); |
1152 |
}
|
|
1153 |
else
|
|
1154 |
{
|
|
1155 |
// 'this' is the monitor
|
|
1156 | 1 |
vsync = new VarExp(funcdecl.loc, funcdecl.vthis); |
1157 | 1 |
if (funcdecl.isThis2) |
1158 |
{
|
|
1159 |
vsync = new PtrExp(funcdecl.loc, vsync); |
|
1160 |
vsync = new IndexExp(funcdecl.loc, vsync, IntegerExp.literal!0); |
|
1161 |
}
|
|
1162 |
}
|
|
1163 | 1 |
sbody = new PeelStatement(sbody); // don't redo semantic() |
1164 | 1 |
sbody = new SynchronizedStatement(funcdecl.loc, vsync, sbody); |
1165 | 1 |
sbody = sbody.statementSemantic(sc2); |
1166 |
}
|
|
1167 |
}
|
|
1168 |
else
|
|
1169 |
{
|
|
1170 |
funcdecl.error("synchronized function `%s` must be a member of a class", funcdecl.toChars()); |
|
1171 |
}
|
|
1172 |
}
|
|
1173 |
|
|
1174 |
// If declaration has no body, don't set sbody to prevent incorrect codegen.
|
|
1175 | 1 |
if (funcdecl.fbody || funcdecl.allowsContractWithoutBody()) |
1176 | 1 |
funcdecl.fbody = sbody; |
1177 |
}
|
|
1178 |
|
|
1179 |
// Check for undefined labels
|
|
1180 | 1 |
if (funcdecl.labtab) |
1181 | 1 |
foreach (keyValue; funcdecl.labtab.tab.asRange) |
1182 |
{
|
|
1183 |
//printf(" KV: %s = %s\n", keyValue.key.toChars(), keyValue.value.toChars());
|
|
1184 | 1 |
LabelDsymbol label = cast(LabelDsymbol)keyValue.value; |
1185 | 1 |
if (!label.statement && (!label.deleted || label.iasm)) |
1186 |
{
|
|
1187 | 1 |
funcdecl.error("label `%s` is undefined", label.toChars()); |
1188 |
}
|
|
1189 |
}
|
|
1190 |
|
|
1191 |
// Fix up forward-referenced gotos
|
|
1192 | 1 |
if (funcdecl.gotos) |
1193 |
{
|
|
1194 | 1 |
for (size_t i = 0; i < funcdecl.gotos.dim; ++i) |
1195 |
{
|
|
1196 | 1 |
(*funcdecl.gotos)[i].checkLabel(); |
1197 |
}
|
|
1198 |
}
|
|
1199 |
|
|
1200 | 1 |
if (funcdecl.naked && (funcdecl.fensures || funcdecl.frequires)) |
1201 | 1 |
funcdecl.error("naked assembly functions with contracts are not supported"); |
1202 |
|
|
1203 | 1 |
sc2.ctorflow.callSuper = CSX.none; |
1204 | 1 |
sc2.pop(); |
1205 |
}
|
|
1206 |
|
|
1207 | 1 |
if (funcdecl.checkClosure()) |
1208 |
{
|
|
1209 |
// We should be setting errors here instead of relying on the global error count.
|
|
1210 |
//errors = true;
|
|
1211 |
}
|
|
1212 |
|
|
1213 |
/* If function survived being marked as impure, then it is pure
|
|
1214 |
*/
|
|
1215 | 1 |
if (funcdecl.flags & FUNCFLAG.purityInprocess) |
1216 |
{
|
|
1217 | 1 |
funcdecl.flags &= ~FUNCFLAG.purityInprocess; |
1218 | 1 |
if (funcdecl.type == f) |
1219 | 1 |
f = cast(TypeFunction)f.copy(); |
1220 | 1 |
f.purity = PURE.fwdref; |
1221 |
}
|
|
1222 |
|
|
1223 | 1 |
if (funcdecl.flags & FUNCFLAG.safetyInprocess) |
1224 |
{
|
|
1225 | 1 |
funcdecl.flags &= ~FUNCFLAG.safetyInprocess; |
1226 | 1 |
if (funcdecl.type == f) |
1227 | 1 |
f = cast(TypeFunction)f.copy(); |
1228 | 1 |
f.trust = TRUST.safe; |
1229 |
}
|
|
1230 |
|
|
1231 | 1 |
if (funcdecl.flags & FUNCFLAG.nogcInprocess) |
1232 |
{
|
|
1233 | 1 |
funcdecl.flags &= ~FUNCFLAG.nogcInprocess; |
1234 | 1 |
if (funcdecl.type == f) |
1235 | 1 |
f = cast(TypeFunction)f.copy(); |
1236 | 1 |
f.isnogc = true; |
1237 |
}
|
|
1238 |
|
|
1239 | 1 |
if (funcdecl.flags & FUNCFLAG.returnInprocess) |
1240 |
{
|
|
1241 | 1 |
funcdecl.flags &= ~FUNCFLAG.returnInprocess; |
1242 | 1 |
if (funcdecl.storage_class & STC.return_) |
1243 |
{
|
|
1244 | 1 |
if (funcdecl.type == f) |
1245 |
f = cast(TypeFunction)f.copy(); |
|
1246 | 1 |
f.isreturn = true; |
1247 | 1 |
if (funcdecl.storage_class & STC.returninferred) |
1248 | 1 |
f.isreturninferred = true; |
1249 |
}
|
|
1250 |
}
|
|
1251 |
|
|
1252 | 1 |
funcdecl.flags &= ~FUNCFLAG.inferScope; |
1253 |
|
|
1254 |
// Eliminate maybescope's
|
|
1255 |
{
|
|
1256 |
// Create and fill array[] with maybe candidates from the `this` and the parameters
|
|
1257 | 1 |
VarDeclaration[] array = void; |
1258 |
|
|
1259 | 1 |
VarDeclaration[10] tmp = void; |
1260 | 1 |
size_t dim = (funcdecl.vthis !is null) + (funcdecl.parameters ? funcdecl.parameters.dim : 0); |
1261 | 1 |
if (dim <= tmp.length) |
1262 | 1 |
array = tmp[0 .. dim]; |
1263 |
else
|
|
1264 |
{
|
|
1265 | 1 |
auto ptr = cast(VarDeclaration*)mem.xmalloc(dim * VarDeclaration.sizeof); |
1266 | 1 |
array = ptr[0 .. dim]; |
1267 |
}
|
|
1268 | 1 |
size_t n = 0; |
1269 | 1 |
if (funcdecl.vthis) |
1270 | 1 |
array[n++] = funcdecl.vthis; |
1271 | 1 |
if (funcdecl.parameters) |
1272 |
{
|
|
1273 | 1 |
foreach (v; *funcdecl.parameters) |
1274 |
{
|
|
1275 | 1 |
array[n++] = v; |
1276 |
}
|
|
1277 |
}
|
|
1278 |
|
|
1279 | 1 |
eliminateMaybeScopes(array[0 .. n]); |
1280 |
|
|
1281 | 1 |
if (dim > tmp.length) |
1282 | 1 |
mem.xfree(array.ptr); |
1283 |
}
|
|
1284 |
|
|
1285 |
// Infer STC.scope_
|
|
1286 | 1 |
if (funcdecl.parameters && !funcdecl.errors) |
1287 |
{
|
|
1288 | 1 |
assert(f.parameterList.length == funcdecl.parameters.dim); |
1289 | 1 |
foreach (u, p; f.parameterList) |
1290 |
{
|
|
1291 | 1 |
auto v = (*funcdecl.parameters)[u]; |
1292 | 1 |
if (v.storage_class & STC.maybescope) |
1293 |
{
|
|
1294 |
//printf("Inferring scope for %s\n", v.toChars());
|
|
1295 | 1 |
notMaybeScope(v); |
1296 | 1 |
v.storage_class |= STC.scope_ | STC.scopeinferred; |
1297 | 1 |
p.storageClass |= STC.scope_ | STC.scopeinferred; |
1298 | 1 |
assert(!(p.storageClass & STC.maybescope)); |
1299 |
}
|
|
1300 |
}
|
|
1301 |
}
|
|
1302 |
|
|
1303 | 1 |
if (funcdecl.vthis && funcdecl.vthis.storage_class & STC.maybescope) |
1304 |
{
|
|
1305 | 1 |
notMaybeScope(funcdecl.vthis); |
1306 | 1 |
funcdecl.vthis.storage_class |= STC.scope_ | STC.scopeinferred; |
1307 | 1 |
f.isScopeQual = true; |
1308 | 1 |
f.isscopeinferred = true; |
1309 |
}
|
|
1310 |
|
|
1311 |
// reset deco to apply inference result to mangled name
|
|
1312 | 1 |
if (f != funcdecl.type) |
1313 | 1 |
f.deco = null; |
1314 |
|
|
1315 |
// Do semantic type AFTER pure/nothrow inference.
|
|
1316 | 1 |
if (!f.deco && funcdecl.ident != Id.xopEquals && funcdecl.ident != Id.xopCmp) |
1317 |
{
|
|
1318 | 1 |
sc = sc.push(); |
1319 | 1 |
if (funcdecl.isCtorDeclaration()) // https://issues.dlang.org/show_bug.cgi?id=#15665 |
1320 | 1 |
sc.flags |= SCOPE.ctor; |
1321 | 1 |
sc.stc = 0; |
1322 | 1 |
sc.linkage = funcdecl.linkage; // https://issues.dlang.org/show_bug.cgi?id=8496 |
1323 | 1 |
funcdecl.type = f.typeSemantic(funcdecl.loc, sc); |
1324 | 1 |
sc = sc.pop(); |
1325 |
}
|
|
1326 |
|
|
1327 |
// Do live analysis
|
|
1328 | 1 |
if (global.params.useDIP1021 && funcdecl.fbody && funcdecl.type.ty != Terror && |
1329 | 1 |
funcdecl.type.isTypeFunction().islive) |
1330 |
{
|
|
1331 | 1 |
oblive(funcdecl); |
1332 |
}
|
|
1333 |
|
|
1334 |
/* If this function had instantiated with gagging, error reproduction will be
|
|
1335 |
* done by TemplateInstance::semantic.
|
|
1336 |
* Otherwise, error gagging should be temporarily ungagged by functionSemantic3.
|
|
1337 |
*/
|
|
1338 | 1 |
funcdecl.semanticRun = PASS.semantic3done; |
1339 | 1 |
funcdecl.semantic3Errors = (global.errors != oldErrors) || (funcdecl.fbody && funcdecl.fbody.isErrorStatement()); |
1340 | 1 |
if (funcdecl.type.ty == Terror) |
1341 | 1 |
funcdecl.errors = true; |
1342 |
//printf("-FuncDeclaration::semantic3('%s.%s', sc = %p, loc = %s)\n", parent.toChars(), toChars(), sc, loc.toChars());
|
|
1343 |
//fflush(stdout);
|
|
1344 |
}
|
|
1345 |
|
|
1346 |
override void visit(CtorDeclaration ctor) |
|
1347 |
{
|
|
1348 |
//printf("CtorDeclaration()\n%s\n", ctor.fbody.toChars());
|
|
1349 | 1 |
if (ctor.semanticRun >= PASS.semantic3) |
1350 | 1 |
return; |
1351 |
|
|
1352 |
/* If any of the fields of the aggregate have a destructor, add
|
|
1353 |
* scope (failure) { this.fieldDtor(); }
|
|
1354 |
* as the first statement. It is not necessary to add it after
|
|
1355 |
* each initialization of a field, because destruction of .init constructed
|
|
1356 |
* structs should be benign.
|
|
1357 |
* https://issues.dlang.org/show_bug.cgi?id=14246
|
|
1358 |
*/
|
|
1359 | 1 |
AggregateDeclaration ad = ctor.isMemberDecl(); |
1360 | 1 |
if (ad && ad.fieldDtor && global.params.dtorFields) |
1361 |
{
|
|
1362 |
/* Generate:
|
|
1363 |
* this.fieldDtor()
|
|
1364 |
*/
|
|
1365 | 1 |
Expression e = new ThisExp(ctor.loc); |
1366 | 1 |
e.type = ad.type.mutableOf(); |
1367 | 1 |
e = new DotVarExp(ctor.loc, e, ad.fieldDtor, false); |
1368 | 1 |
e = new CallExp(ctor.loc, e); |
1369 | 1 |
auto sexp = new ExpStatement(ctor.loc, e); |
1370 | 1 |
auto ss = new ScopeStatement(ctor.loc, sexp, ctor.loc); |
1371 |
|
|
1372 |
version (all) |
|
1373 |
{
|
|
1374 |
/* Generate:
|
|
1375 |
* try { ctor.fbody; }
|
|
1376 |
* catch (Exception __o)
|
|
1377 |
* { this.fieldDtor(); throw __o; }
|
|
1378 |
* This differs from the alternate scope(failure) version in that an Exception
|
|
1379 |
* is caught rather than a Throwable. This enables the optimization whereby
|
|
1380 |
* the try-catch can be removed if ctor.fbody is nothrow. (nothrow only
|
|
1381 |
* applies to Exception.)
|
|
1382 |
*/
|
|
1383 | 1 |
Identifier id = Identifier.generateId("__o"); |
1384 | 1 |
auto ts = new ThrowStatement(ctor.loc, new IdentifierExp(ctor.loc, id)); |
1385 | 1 |
auto handler = new CompoundStatement(ctor.loc, ss, ts); |
1386 |
|
|
1387 | 1 |
auto catches = new Catches(); |
1388 | 1 |
auto ctch = new Catch(ctor.loc, getException(), id, handler); |
1389 | 1 |
catches.push(ctch); |
1390 |
|
|
1391 | 1 |
ctor.fbody = new TryCatchStatement(ctor.loc, ctor.fbody, catches); |
1392 |
}
|
|
1393 |
else
|
|
1394 |
{
|
|
1395 |
/* Generate:
|
|
1396 |
* scope (failure) { this.fieldDtor(); }
|
|
1397 |
* Hopefully we can use this version someday when scope(failure) catches
|
|
1398 |
* Exception instead of Throwable.
|
|
1399 |
*/
|
|
1400 |
auto s = new ScopeGuardStatement(ctor.loc, TOK.onScopeFailure, ss); |
|
1401 |
ctor.fbody = new CompoundStatement(ctor.loc, s, ctor.fbody); |
|
1402 |
}
|
|
1403 |
}
|
|
1404 | 1 |
visit(cast(FuncDeclaration)ctor); |
1405 |
}
|
|
1406 |
|
|
1407 |
|
|
1408 |
override void visit(Nspace ns) |
|
1409 |
{
|
|
1410 | 1 |
if (ns.semanticRun >= PASS.semantic3) |
1411 |
return; |
|
1412 | 1 |
ns.semanticRun = PASS.semantic3; |
1413 |
static if (LOG) |
|
1414 |
{
|
|
1415 |
printf("Nspace::semantic3('%s')\n", ns.toChars()); |
|
1416 |
}
|
|
1417 | 1 |
if (ns.members) |
1418 |
{
|
|
1419 | 1 |
sc = sc.push(ns); |
1420 | 1 |
sc.linkage = LINK.cpp; |
1421 | 1 |
foreach (s; *ns.members) |
1422 |
{
|
|
1423 | 1 |
s.semantic3(sc); |
1424 |
}
|
|
1425 | 1 |
sc.pop(); |
1426 |
}
|
|
1427 |
}
|
|
1428 |
|
|
1429 |
override void visit(AttribDeclaration ad) |
|
1430 |
{
|
|
1431 | 1 |
Dsymbols* d = ad.include(sc); |
1432 | 1 |
if (d) |
1433 |
{
|
|
1434 | 1 |
Scope* sc2 = ad.newScope(sc); |
1435 | 1 |
for (size_t i = 0; i < d.dim; i++) |
1436 |
{
|
|
1437 | 1 |
Dsymbol s = (*d)[i]; |
1438 | 1 |
s.semantic3(sc2); |
1439 |
}
|
|
1440 | 1 |
if (sc2 != sc) |
1441 | 1 |
sc2.pop(); |
1442 |
}
|
|
1443 |
}
|
|
1444 |
|
|
1445 |
override void visit(AggregateDeclaration ad) |
|
1446 |
{
|
|
1447 |
//printf("AggregateDeclaration::semantic3(sc=%p, %s) type = %s, errors = %d\n", sc, toChars(), type.toChars(), errors);
|
|
1448 | 1 |
if (!ad.members) |
1449 | 1 |
return; |
1450 |
|
|
1451 | 1 |
StructDeclaration sd = ad.isStructDeclaration(); |
1452 | 1 |
if (!sc) // from runDeferredSemantic3 for TypeInfo generation |
1453 |
{
|
|
1454 | 1 |
assert(sd); |
1455 | 1 |
sd.semanticTypeInfoMembers(); |
1456 | 1 |
return; |
1457 |
}
|
|
1458 |
|
|
1459 | 1 |
auto sc2 = ad.newScope(sc); |
1460 |
|
|
1461 | 1 |
for (size_t i = 0; i < ad.members.dim; i++) |
1462 |
{
|
|
1463 | 1 |
Dsymbol s = (*ad.members)[i]; |
1464 | 1 |
s.semantic3(sc2); |
1465 |
}
|
|
1466 |
|
|
1467 | 1 |
sc2.pop(); |
1468 |
|
|
1469 |
// don't do it for unused deprecated types
|
|
1470 |
// or error ypes
|
|
1471 | 1 |
if (!ad.getRTInfo && Type.rtinfo && (!ad.isDeprecated() || global.params.useDeprecated != DiagnosticReporting.error) && (ad.type && ad.type.ty != Terror)) |
1472 |
{
|
|
1473 |
// Evaluate: RTinfo!type
|
|
1474 | 1 |
auto tiargs = new Objects(); |
1475 | 1 |
tiargs.push(ad.type); |
1476 | 1 |
auto ti = Pool!TemplateInstance.make(ad.loc, Type.rtinfo, tiargs); |
1477 |
|
|
1478 | 1 |
Scope* sc3 = ti.tempdecl._scope.startCTFE(); |
1479 | 1 |
sc3.tinst = sc.tinst; |
1480 | 1 |
sc3.minst = sc.minst; |
1481 | 1 |
if (ad.isDeprecated()) |
1482 | 1 |
sc3.stc |= STC.deprecated_; |
1483 |
|
|
1484 | 1 |
ti.dsymbolSemantic(sc3); |
1485 | 1 |
ti.semantic2(sc3); |
1486 | 1 |
ti.semantic3(sc3); |
1487 | 1 |
auto e = symbolToExp(ti.toAlias(), Loc.initial, sc3, false); |
1488 |
|
|
1489 | 1 |
sc3.endCTFE(); |
1490 |
|
|
1491 | 1 |
e = e.ctfeInterpret(); |
1492 | 1 |
ad.getRTInfo = e; |
1493 |
}
|
|
1494 | 1 |
if (sd) |
1495 | 1 |
sd.semanticTypeInfoMembers(); |
1496 | 1 |
ad.semanticRun = PASS.semantic3done; |
1497 |
}
|
|
1498 |
}
|
|
1499 |
|
|
1500 |
private struct FuncDeclSem3 |
|
1501 |
{
|
|
1502 |
// The FuncDeclaration subject to Semantic analysis
|
|
1503 |
FuncDeclaration funcdecl; |
|
1504 |
|
|
1505 |
// Scope of analysis
|
|
1506 |
Scope* sc; |
|
1507 | 1 |
this(FuncDeclaration fd,Scope* s) |
1508 |
{
|
|
1509 | 1 |
funcdecl = fd; |
1510 | 1 |
sc = s; |
1511 |
}
|
|
1512 |
|
|
1513 |
/* Checks that the overriden functions (if any) have in contracts if
|
|
1514 |
* funcdecl has an in contract.
|
|
1515 |
*/
|
|
1516 |
void checkInContractOverrides() |
|
1517 |
{
|
|
1518 | 1 |
if (funcdecl.frequires) |
1519 |
{
|
|
1520 | 1 |
for (size_t i = 0; i < funcdecl.foverrides.dim; i++) |
1521 |
{
|
|
1522 | 1 |
FuncDeclaration fdv = funcdecl.foverrides[i]; |
1523 | 1 |
if (fdv.fbody && !fdv.frequires) |
1524 |
{
|
|
1525 | 1 |
funcdecl.error("cannot have an in contract when overridden function `%s` does not have an in contract", fdv.toPrettyChars()); |
1526 | 1 |
break; |
1527 |
}
|
|
1528 |
}
|
|
1529 |
}
|
|
1530 |
}
|
|
1531 |
}
|
Read our documentation on viewing source code .