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 |
* Defines a function declaration.
|
|
3 |
*
|
|
4 |
* Includes:
|
|
5 |
* - function/delegate literals
|
|
6 |
* - function aliases
|
|
7 |
* - (static/shared) constructors/destructors/post-blits
|
|
8 |
* - `invariant`
|
|
9 |
* - `unittest`
|
|
10 |
*
|
|
11 |
* Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
|
|
12 |
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
|
|
13 |
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
|
|
14 |
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/func.d, _func.d)
|
|
15 |
* Documentation: https://dlang.org/phobos/dmd_func.html
|
|
16 |
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/func.d
|
|
17 |
*/
|
|
18 |
|
|
19 |
module dmd.func; |
|
20 |
|
|
21 |
import core.stdc.stdio; |
|
22 |
import core.stdc.string; |
|
23 |
import dmd.aggregate; |
|
24 |
import dmd.arraytypes; |
|
25 |
import dmd.blockexit; |
|
26 |
import dmd.gluelayer; |
|
27 |
import dmd.dclass; |
|
28 |
import dmd.declaration; |
|
29 |
import dmd.delegatize; |
|
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.errors; |
|
38 |
import dmd.escape; |
|
39 |
import dmd.expression; |
|
40 |
import dmd.globals; |
|
41 |
import dmd.hdrgen; |
|
42 |
import dmd.id; |
|
43 |
import dmd.identifier; |
|
44 |
import dmd.init; |
|
45 |
import dmd.mtype; |
|
46 |
import dmd.objc; |
|
47 |
import dmd.root.outbuffer; |
|
48 |
import dmd.root.rootobject; |
|
49 |
import dmd.root.string; |
|
50 |
import dmd.root.stringtable; |
|
51 |
import dmd.semantic2; |
|
52 |
import dmd.semantic3; |
|
53 |
import dmd.statement_rewrite_walker; |
|
54 |
import dmd.statement; |
|
55 |
import dmd.statementsem; |
|
56 |
import dmd.tokens; |
|
57 |
import dmd.visitor; |
|
58 |
|
|
59 |
/// Inline Status
|
|
60 |
enum ILS : int |
|
61 |
{
|
|
62 |
uninitialized, /// not computed yet |
|
63 |
no, /// cannot inline |
|
64 |
yes, /// can inline |
|
65 |
}
|
|
66 |
|
|
67 |
enum BUILTIN : byte |
|
68 |
{
|
|
69 |
unknown = -1, /// not known if this is a builtin |
|
70 |
unimp, /// this is not a builtin |
|
71 |
gcc, /// this is a GCC builtin |
|
72 |
llvm, /// this is an LLVM builtin |
|
73 |
sin, |
|
74 |
cos, |
|
75 |
tan, |
|
76 |
sqrt, |
|
77 |
fabs, |
|
78 |
ldexp, |
|
79 |
log, |
|
80 |
log2, |
|
81 |
log10, |
|
82 |
exp, |
|
83 |
expm1, |
|
84 |
exp2, |
|
85 |
round, |
|
86 |
floor, |
|
87 |
ceil, |
|
88 |
trunc, |
|
89 |
copysign, |
|
90 |
pow, |
|
91 |
fmin, |
|
92 |
fmax, |
|
93 |
fma, |
|
94 |
isnan, |
|
95 |
isinfinity, |
|
96 |
isfinite, |
|
97 |
bsf, |
|
98 |
bsr, |
|
99 |
bswap, |
|
100 |
popcnt, |
|
101 |
yl2x, |
|
102 |
yl2xp1, |
|
103 |
toPrecFloat, |
|
104 |
toPrecDouble, |
|
105 |
toPrecReal
|
|
106 |
}
|
|
107 |
|
|
108 |
/* Tweak all return statements and dtor call for nrvo_var, for correct NRVO.
|
|
109 |
*/
|
|
110 |
extern (C++) final class NrvoWalker : StatementRewriteWalker |
|
111 |
{
|
|
112 |
alias visit = typeof(super).visit; |
|
113 |
public: |
|
114 |
FuncDeclaration fd; |
|
115 |
Scope* sc; |
|
116 |
|
|
117 |
override void visit(ReturnStatement s) |
|
118 |
{
|
|
119 |
// See if all returns are instead to be replaced with a goto returnLabel;
|
|
120 | 1 |
if (fd.returnLabel) |
121 |
{
|
|
122 |
/* Rewrite:
|
|
123 |
* return exp;
|
|
124 |
* as:
|
|
125 |
* vresult = exp; goto Lresult;
|
|
126 |
*/
|
|
127 | 1 |
auto gs = new GotoStatement(s.loc, Id.returnLabel); |
128 | 1 |
gs.label = fd.returnLabel; |
129 |
|
|
130 | 1 |
Statement s1 = gs; |
131 | 1 |
if (s.exp) |
132 | 1 |
s1 = new CompoundStatement(s.loc, new ExpStatement(s.loc, s.exp), gs); |
133 |
|
|
134 | 1 |
replaceCurrent(s1); |
135 |
}
|
|
136 |
}
|
|
137 |
|
|
138 |
override void visit(TryFinallyStatement s) |
|
139 |
{
|
|
140 | 1 |
DtorExpStatement des; |
141 | 1 |
if (fd.nrvo_can && s.finalbody && (des = s.finalbody.isDtorExpStatement()) !is null && |
142 | 1 |
fd.nrvo_var == des.var) |
143 |
{
|
|
144 | 1 |
if (!(global.params.useExceptions && ClassDeclaration.throwable)) |
145 |
{
|
|
146 |
/* Don't need to call destructor at all, since it is nrvo
|
|
147 |
*/
|
|
148 | 1 |
replaceCurrent(s._body); |
149 | 1 |
s._body.accept(this); |
150 | 1 |
return; |
151 |
}
|
|
152 |
|
|
153 |
/* Normally local variable dtors are called regardless exceptions.
|
|
154 |
* But for nrvo_var, its dtor should be called only when exception is thrown.
|
|
155 |
*
|
|
156 |
* Rewrite:
|
|
157 |
* try { s.body; } finally { nrvo_var.edtor; }
|
|
158 |
* // equivalent with:
|
|
159 |
* // s.body; scope(exit) nrvo_var.edtor;
|
|
160 |
* as:
|
|
161 |
* try { s.body; } catch(Throwable __o) { nrvo_var.edtor; throw __o; }
|
|
162 |
* // equivalent with:
|
|
163 |
* // s.body; scope(failure) nrvo_var.edtor;
|
|
164 |
*/
|
|
165 | 1 |
Statement sexception = new DtorExpStatement(Loc.initial, fd.nrvo_var.edtor, fd.nrvo_var); |
166 | 1 |
Identifier id = Identifier.generateId("__o"); |
167 |
|
|
168 | 1 |
Statement handler = new PeelStatement(sexception); |
169 | 1 |
if (sexception.blockExit(fd, false) & BE.fallthru) |
170 |
{
|
|
171 | 1 |
auto ts = new ThrowStatement(Loc.initial, new IdentifierExp(Loc.initial, id)); |
172 | 1 |
ts.internalThrow = true; |
173 | 1 |
handler = new CompoundStatement(Loc.initial, handler, ts); |
174 |
}
|
|
175 |
|
|
176 | 1 |
auto catches = new Catches(); |
177 | 1 |
auto ctch = new Catch(Loc.initial, getThrowable(), id, handler); |
178 | 1 |
ctch.internalCatch = true; |
179 | 1 |
ctch.catchSemantic(sc); // Run semantic to resolve identifier '__o' |
180 | 1 |
catches.push(ctch); |
181 |
|
|
182 | 1 |
Statement s2 = new TryCatchStatement(Loc.initial, s._body, catches); |
183 | 1 |
fd.eh_none = false; |
184 | 1 |
replaceCurrent(s2); |
185 | 1 |
s2.accept(this); |
186 |
}
|
|
187 |
else
|
|
188 | 1 |
StatementRewriteWalker.visit(s); |
189 |
}
|
|
190 |
}
|
|
191 |
|
|
192 |
enum FUNCFLAG : uint |
|
193 |
{
|
|
194 |
purityInprocess = 1, /// working on determining purity |
|
195 |
safetyInprocess = 2, /// working on determining safety |
|
196 |
nothrowInprocess = 4, /// working on determining nothrow |
|
197 |
nogcInprocess = 8, /// working on determining @nogc |
|
198 |
returnInprocess = 0x10, /// working on inferring 'return' for parameters |
|
199 |
inlineScanned = 0x20, /// function has been scanned for inline possibilities |
|
200 |
inferScope = 0x40, /// infer 'scope' for parameters |
|
201 |
hasCatches = 0x80, /// function has try-catch statements |
|
202 |
compileTimeOnly = 0x100, /// is a compile time only function; no code will be generated for it |
|
203 |
printf = 0x200, /// is a printf-like function |
|
204 |
scanf = 0x400, /// is a scanf-like function |
|
205 |
}
|
|
206 |
|
|
207 |
/***********************************************************
|
|
208 |
* Tuple of result identifier (possibly null) and statement.
|
|
209 |
* This is used to store out contracts: out(id){ ensure }
|
|
210 |
*/
|
|
211 |
extern (C++) struct Ensure |
|
212 |
{
|
|
213 |
Identifier id; |
|
214 |
Statement ensure; |
|
215 |
|
|
216 |
Ensure syntaxCopy() |
|
217 |
{
|
|
218 | 1 |
return Ensure(id, ensure.syntaxCopy()); |
219 |
}
|
|
220 |
|
|
221 |
/*****************************************
|
|
222 |
* Do syntax copy of an array of Ensure's.
|
|
223 |
*/
|
|
224 |
static Ensures* arraySyntaxCopy(Ensures* a) |
|
225 |
{
|
|
226 | 1 |
Ensures* b = null; |
227 | 1 |
if (a) |
228 |
{
|
|
229 | 1 |
b = a.copy(); |
230 | 1 |
foreach (i, e; *a) |
231 |
{
|
|
232 | 1 |
(*b)[i] = e.syntaxCopy(); |
233 |
}
|
|
234 |
}
|
|
235 | 1 |
return b; |
236 |
}
|
|
237 |
|
|
238 |
}
|
|
239 |
|
|
240 |
/***********************************************************
|
|
241 |
*/
|
|
242 |
extern (C++) class FuncDeclaration : Declaration |
|
243 |
{
|
|
244 |
Statements* frequires; /// in contracts |
|
245 |
Ensures* fensures; /// out contracts |
|
246 |
Statement frequire; /// lowered in contract |
|
247 |
Statement fensure; /// lowered out contract |
|
248 |
Statement fbody; /// function body |
|
249 |
|
|
250 |
FuncDeclarations foverrides; /// functions this function overrides |
|
251 |
FuncDeclaration fdrequire; /// function that does the in contract |
|
252 |
FuncDeclaration fdensure; /// function that does the out contract |
|
253 |
|
|
254 |
Expressions* fdrequireParams; /// argument list for __require |
|
255 |
Expressions* fdensureParams; /// argument list for __ensure |
|
256 |
|
|
257 |
const(char)* mangleString; /// mangled symbol created from mangleExact() |
|
258 |
|
|
259 |
VarDeclaration vresult; /// result variable for out contracts |
|
260 |
LabelDsymbol returnLabel; /// where the return goes |
|
261 |
|
|
262 |
// used to prevent symbols in different
|
|
263 |
// scopes from having the same name
|
|
264 |
DsymbolTable localsymtab; |
|
265 |
VarDeclaration vthis; /// 'this' parameter (member and nested) |
|
266 |
bool isThis2; /// has a dual-context 'this' parameter |
|
267 |
VarDeclaration v_arguments; /// '_arguments' parameter |
|
268 |
ObjcSelector* selector; /// Objective-C method selector (member function only) |
|
269 |
VarDeclaration selectorParameter; /// Objective-C implicit selector parameter |
|
270 |
|
|
271 |
VarDeclaration v_argptr; /// '_argptr' variable |
|
272 |
VarDeclarations* parameters; /// Array of VarDeclaration's for parameters |
|
273 |
DsymbolTable labtab; /// statement label symbol table |
|
274 |
Dsymbol overnext; /// next in overload list |
|
275 |
FuncDeclaration overnext0; /// next in overload list (only used during IFTI) |
|
276 |
Loc endloc; /// location of closing curly bracket |
|
277 |
int vtblIndex = -1; /// for member functions, index into vtbl[] |
|
278 |
bool naked; /// true if naked |
|
279 |
bool generated; /// true if function was generated by the compiler rather than |
|
280 |
/// supplied by the user
|
|
281 |
bool hasAlwaysInlines; /// contains references to functions that must be inlined |
|
282 |
ubyte isCrtCtorDtor; /// has attribute pragma(crt_constructor(1)/crt_destructor(2)) |
|
283 |
/// not set before the glue layer
|
|
284 |
|
|
285 |
ILS inlineStatusStmt = ILS.uninitialized; |
|
286 |
ILS inlineStatusExp = ILS.uninitialized; |
|
287 |
PINLINE inlining = PINLINE.default_; |
|
288 |
|
|
289 |
int inlineNest; /// !=0 if nested inline |
|
290 |
bool eh_none; /// true if no exception unwinding is needed |
|
291 |
|
|
292 |
bool semantic3Errors; /// true if errors in semantic3 this function's frame ptr |
|
293 |
ForeachStatement fes; /// if foreach body, this is the foreach |
|
294 |
BaseClass* interfaceVirtual; /// if virtual, but only appears in base interface vtbl[] |
|
295 |
bool introducing; /// true if 'introducing' function |
|
296 |
/** if !=NULL, then this is the type
|
|
297 |
of the 'introducing' function
|
|
298 |
this one is overriding
|
|
299 |
*/
|
|
300 |
Type tintro; |
|
301 |
|
|
302 |
bool inferRetType; /// true if return type is to be inferred |
|
303 |
StorageClass storage_class2; /// storage class for template onemember's |
|
304 |
|
|
305 |
// Things that should really go into Scope
|
|
306 |
|
|
307 |
/// 1 if there's a return exp; statement
|
|
308 |
/// 2 if there's a throw statement
|
|
309 |
/// 4 if there's an assert(0)
|
|
310 |
/// 8 if there's inline asm
|
|
311 |
/// 16 if there are multiple return statements
|
|
312 |
int hasReturnExp; |
|
313 |
|
|
314 |
// Support for NRVO (named return value optimization)
|
|
315 |
bool nrvo_can = true; /// true means we can do NRVO |
|
316 |
VarDeclaration nrvo_var; /// variable to replace with shidden |
|
317 |
Symbol* shidden; /// hidden pointer passed to function |
|
318 |
|
|
319 |
ReturnStatements* returns; |
|
320 |
|
|
321 |
GotoStatements* gotos; /// Gotos with forward references |
|
322 |
|
|
323 |
/// set if this is a known, builtin function we can evaluate at compile time
|
|
324 |
BUILTIN builtin = BUILTIN.unknown; |
|
325 |
|
|
326 |
/// set if someone took the address of this function
|
|
327 |
int tookAddressOf; |
|
328 |
|
|
329 |
bool requiresClosure; // this function needs a closure |
|
330 |
|
|
331 |
/** local variables in this function which are referenced by nested functions
|
|
332 |
* (They'll get put into the "closure" for this function.)
|
|
333 |
*/
|
|
334 |
VarDeclarations closureVars; |
|
335 |
|
|
336 |
/** Outer variables which are referenced by this nested function
|
|
337 |
* (the inverse of closureVars)
|
|
338 |
*/
|
|
339 |
VarDeclarations outerVars; |
|
340 |
|
|
341 |
/// Sibling nested functions which called this one
|
|
342 |
FuncDeclarations siblingCallers; |
|
343 |
|
|
344 |
FuncDeclarations *inlinedNestedCallees; |
|
345 |
|
|
346 |
uint flags; /// FUNCFLAG.xxxxx |
|
347 |
|
|
348 | 1 |
extern (D) this(const ref Loc loc, const ref Loc endloc, Identifier ident, StorageClass storage_class, Type type) |
349 |
{
|
|
350 | 1 |
super(loc, ident); |
351 |
//printf("FuncDeclaration(id = '%s', type = %p)\n", id.toChars(), type);
|
|
352 |
//printf("storage_class = x%x\n", storage_class);
|
|
353 | 1 |
this.storage_class = storage_class; |
354 | 1 |
this.type = type; |
355 | 1 |
if (type) |
356 |
{
|
|
357 |
// Normalize storage_class, because function-type related attributes
|
|
358 |
// are already set in the 'type' in parsing phase.
|
|
359 | 1 |
this.storage_class &= ~(STC.TYPECTOR | STC.FUNCATTR); |
360 |
}
|
|
361 | 1 |
this.endloc = endloc; |
362 |
/* The type given for "infer the return type" is a TypeFunction with
|
|
363 |
* NULL for the return type.
|
|
364 |
*/
|
|
365 | 1 |
inferRetType = (type && type.nextOf() is null); |
366 |
}
|
|
367 |
|
|
368 |
static FuncDeclaration create(const ref Loc loc, const ref Loc endloc, Identifier id, StorageClass storage_class, Type type) |
|
369 |
{
|
|
370 |
return new FuncDeclaration(loc, endloc, id, storage_class, type); |
|
371 |
}
|
|
372 |
|
|
373 |
override Dsymbol syntaxCopy(Dsymbol s) |
|
374 |
{
|
|
375 |
//printf("FuncDeclaration::syntaxCopy('%s')\n", toChars());
|
|
376 | 1 |
FuncDeclaration f = s ? cast(FuncDeclaration)s : new FuncDeclaration(loc, endloc, ident, storage_class, type.syntaxCopy()); |
377 | 1 |
f.frequires = frequires ? Statement.arraySyntaxCopy(frequires) : null; |
378 | 1 |
f.fensures = fensures ? Ensure.arraySyntaxCopy(fensures) : null; |
379 | 1 |
f.fbody = fbody ? fbody.syntaxCopy() : null; |
380 | 1 |
return f; |
381 |
}
|
|
382 |
|
|
383 |
/****************************************************
|
|
384 |
* Resolve forward reference of function signature -
|
|
385 |
* parameter types, return type, and attributes.
|
|
386 |
* Returns false if any errors exist in the signature.
|
|
387 |
*/
|
|
388 |
final bool functionSemantic() |
|
389 |
{
|
|
390 | 1 |
if (!_scope) |
391 | 1 |
return !errors; |
392 |
|
|
393 | 1 |
this.cppnamespace = _scope.namespace; |
394 |
|
|
395 | 1 |
if (!originalType) // semantic not yet run |
396 |
{
|
|
397 | 1 |
TemplateInstance spec = isSpeculative(); |
398 | 1 |
uint olderrs = global.errors; |
399 | 1 |
uint oldgag = global.gag; |
400 | 1 |
if (global.gag && !spec) |
401 | 1 |
global.gag = 0; |
402 | 1 |
dsymbolSemantic(this, _scope); |
403 | 1 |
global.gag = oldgag; |
404 | 1 |
if (spec && global.errors != olderrs) |
405 |
spec.errors = (global.errors - olderrs != 0); |
|
406 | 1 |
if (olderrs != global.errors) // if errors compiling this function |
407 | 1 |
return false; |
408 |
}
|
|
409 |
|
|
410 |
// if inferring return type, sematic3 needs to be run
|
|
411 |
// - When the function body contains any errors, we cannot assume
|
|
412 |
// the inferred return type is valid.
|
|
413 |
// So, the body errors should become the function signature error.
|
|
414 | 1 |
if (inferRetType && type && !type.nextOf()) |
415 | 1 |
return functionSemantic3(); |
416 |
|
|
417 | 1 |
TemplateInstance ti; |
418 | 1 |
if (isInstantiated() && !isVirtualMethod() && |
419 | 1 |
((ti = parent.isTemplateInstance()) is null || ti.isTemplateMixin() || ti.tempdecl.ident == ident)) |
420 |
{
|
|
421 | 1 |
AggregateDeclaration ad = isMemberLocal(); |
422 | 1 |
if (ad && ad.sizeok != Sizeok.done) |
423 |
{
|
|
424 |
/* Currently dmd cannot resolve forward references per methods,
|
|
425 |
* then setting SIZOKfwd is too conservative and would break existing code.
|
|
426 |
* So, just stop method attributes inference until ad.dsymbolSemantic() done.
|
|
427 |
*/
|
|
428 |
//ad.sizeok = Sizeok.fwd;
|
|
429 |
}
|
|
430 |
else
|
|
431 | 1 |
return functionSemantic3() || !errors; |
432 |
}
|
|
433 |
|
|
434 | 1 |
if (storage_class & STC.inference) |
435 | 1 |
return functionSemantic3() || !errors; |
436 |
|
|
437 | 1 |
return !errors; |
438 |
}
|
|
439 |
|
|
440 |
/****************************************************
|
|
441 |
* Resolve forward reference of function body.
|
|
442 |
* Returns false if any errors exist in the body.
|
|
443 |
*/
|
|
444 |
final bool functionSemantic3() |
|
445 |
{
|
|
446 | 1 |
if (semanticRun < PASS.semantic3 && _scope) |
447 |
{
|
|
448 |
/* Forward reference - we need to run semantic3 on this function.
|
|
449 |
* If errors are gagged, and it's not part of a template instance,
|
|
450 |
* we need to temporarily ungag errors.
|
|
451 |
*/
|
|
452 | 1 |
TemplateInstance spec = isSpeculative(); |
453 | 1 |
uint olderrs = global.errors; |
454 | 1 |
uint oldgag = global.gag; |
455 | 1 |
if (global.gag && !spec) |
456 | 1 |
global.gag = 0; |
457 | 1 |
semantic3(this, _scope); |
458 | 1 |
global.gag = oldgag; |
459 |
|
|
460 |
// If it is a speculatively-instantiated template, and errors occur,
|
|
461 |
// we need to mark the template as having errors.
|
|
462 | 1 |
if (spec && global.errors != olderrs) |
463 | 1 |
spec.errors = (global.errors - olderrs != 0); |
464 | 1 |
if (olderrs != global.errors) // if errors compiling this function |
465 | 1 |
return false; |
466 |
}
|
|
467 |
|
|
468 | 1 |
return !errors && !semantic3Errors; |
469 |
}
|
|
470 |
|
|
471 |
/****************************************************
|
|
472 |
* Check that this function type is properly resolved.
|
|
473 |
* If not, report "forward reference error" and return true.
|
|
474 |
*/
|
|
475 |
extern (D) final bool checkForwardRef(const ref Loc loc) |
|
476 |
{
|
|
477 | 1 |
if (!functionSemantic()) |
478 |
return true; |
|
479 |
|
|
480 |
/* No deco means the functionSemantic() call could not resolve
|
|
481 |
* forward referenes in the type of this function.
|
|
482 |
*/
|
|
483 | 1 |
if (!type.deco) |
484 |
{
|
|
485 | 1 |
bool inSemantic3 = (inferRetType && semanticRun >= PASS.semantic3); |
486 | 1 |
.error(loc, "forward reference to %s`%s`", |
487 | 1 |
(inSemantic3 ? "inferred return type of function " : "").ptr, |
488 |
toChars()); |
|
489 | 1 |
return true; |
490 |
}
|
|
491 | 1 |
return false; |
492 |
}
|
|
493 |
|
|
494 |
// called from semantic3
|
|
495 |
/**
|
|
496 |
* Creates and returns the hidden parameters for this function declaration.
|
|
497 |
*
|
|
498 |
* Hidden parameters include the `this` parameter of a class, struct or
|
|
499 |
* nested function and the selector parameter for Objective-C methods.
|
|
500 |
*/
|
|
501 |
extern (D) final void declareThis(Scope* sc) |
|
502 |
{
|
|
503 | 1 |
isThis2 = toParent2() != toParentLocal(); |
504 | 1 |
auto ad = isThis(); |
505 | 1 |
if (!isThis2 && !ad && !isNested()) |
506 |
{
|
|
507 | 1 |
vthis = null; |
508 | 1 |
selectorParameter = null; |
509 | 1 |
return; |
510 |
}
|
|
511 |
|
|
512 |
Type addModStc(Type t) |
|
513 |
{
|
|
514 | 1 |
return t.addMod(type.mod).addStorageClass(storage_class); |
515 |
}
|
|
516 |
|
|
517 | 1 |
if (isThis2 || isNested()) |
518 |
{
|
|
519 |
/* The 'this' for a nested function is the link to the
|
|
520 |
* enclosing function's stack frame.
|
|
521 |
* Note that nested functions and member functions are disjoint.
|
|
522 |
*/
|
|
523 | 1 |
Type tthis = addModStc(isThis2 ? |
524 | 1 |
Type.tvoidptr.sarrayOf(2).pointerTo() : |
525 | 1 |
Type.tvoid.pointerTo()); |
526 | 1 |
vthis = new VarDeclaration(loc, tthis, isThis2 ? Id.this2 : Id.capture, null); |
527 | 1 |
vthis.storage_class |= STC.parameter | STC.nodtor; |
528 |
}
|
|
529 | 1 |
else if (ad) |
530 |
{
|
|
531 | 1 |
Type thandle = addModStc(ad.handleType()); |
532 | 1 |
vthis = new ThisDeclaration(loc, thandle); |
533 | 1 |
vthis.storage_class |= STC.parameter; |
534 | 1 |
if (thandle.ty == Tstruct) |
535 |
{
|
|
536 | 1 |
vthis.storage_class |= STC.ref_; |
537 |
// if member function is marked 'inout', then 'this' is 'return ref'
|
|
538 | 1 |
if (type.ty == Tfunction && (cast(TypeFunction)type).isInOutQual()) |
539 | 1 |
vthis.storage_class |= STC.return_; |
540 |
}
|
|
541 |
}
|
|
542 |
|
|
543 | 1 |
if (type.ty == Tfunction) |
544 |
{
|
|
545 | 1 |
TypeFunction tf = cast(TypeFunction)type; |
546 | 1 |
if (tf.isreturn) |
547 | 1 |
vthis.storage_class |= STC.return_; |
548 | 1 |
if (tf.isScopeQual) |
549 | 1 |
vthis.storage_class |= STC.scope_; |
550 |
}
|
|
551 | 1 |
if (flags & FUNCFLAG.inferScope && !(vthis.storage_class & STC.scope_)) |
552 | 1 |
vthis.storage_class |= STC.maybescope; |
553 |
|
|
554 | 1 |
vthis.dsymbolSemantic(sc); |
555 | 1 |
if (!sc.insert(vthis)) |
556 |
assert(0); |
|
557 | 1 |
vthis.parent = this; |
558 | 1 |
if (ad) |
559 | 1 |
selectorParameter = objc.createSelectorParameter(this, sc); |
560 |
}
|
|
561 |
|
|
562 |
override final bool equals(const RootObject o) const |
|
563 |
{
|
|
564 | 1 |
if (this == o) |
565 | 1 |
return true; |
566 |
|
|
567 | 1 |
if (auto s = isDsymbol(o)) |
568 |
{
|
|
569 | 1 |
auto fd1 = this; |
570 | 1 |
auto fd2 = s.isFuncDeclaration(); |
571 | 1 |
if (!fd2) |
572 |
return false; |
|
573 |
|
|
574 | 1 |
auto fa1 = fd1.isFuncAliasDeclaration(); |
575 | 1 |
auto faf1 = fa1 ? fa1.toAliasFunc() : fd1; |
576 |
|
|
577 | 1 |
auto fa2 = fd2.isFuncAliasDeclaration(); |
578 | 1 |
auto faf2 = fa2 ? fa2.toAliasFunc() : fd2; |
579 |
|
|
580 | 1 |
if (fa1 && fa2) |
581 |
{
|
|
582 | 1 |
return faf1.equals(faf2) && fa1.hasOverloads == fa2.hasOverloads; |
583 |
}
|
|
584 |
|
|
585 | 1 |
bool b1 = fa1 !is null; |
586 | 1 |
if (b1 && faf1.isUnique() && !fa1.hasOverloads) |
587 |
b1 = false; |
|
588 |
|
|
589 | 1 |
bool b2 = fa2 !is null; |
590 | 1 |
if (b2 && faf2.isUnique() && !fa2.hasOverloads) |
591 |
b2 = false; |
|
592 |
|
|
593 | 1 |
if (b1 != b2) |
594 | 1 |
return false; |
595 |
|
|
596 | 1 |
return faf1.toParent().equals(faf2.toParent()) && |
597 | 1 |
faf1.ident.equals(faf2.ident) && |
598 | 1 |
faf1.type.equals(faf2.type); |
599 |
}
|
|
600 |
return false; |
|
601 |
}
|
|
602 |
|
|
603 |
/****************************************************
|
|
604 |
* Determine if 'this' overrides fd.
|
|
605 |
* Return !=0 if it does.
|
|
606 |
*/
|
|
607 |
final int overrides(FuncDeclaration fd) |
|
608 |
{
|
|
609 | 1 |
int result = 0; |
610 | 1 |
if (fd.ident == ident) |
611 |
{
|
|
612 | 1 |
int cov = type.covariant(fd.type); |
613 | 1 |
if (cov) |
614 |
{
|
|
615 | 1 |
ClassDeclaration cd1 = toParent().isClassDeclaration(); |
616 | 1 |
ClassDeclaration cd2 = fd.toParent().isClassDeclaration(); |
617 | 1 |
if (cd1 && cd2 && cd2.isBaseOf(cd1, null)) |
618 | 1 |
result = 1; |
619 |
}
|
|
620 |
}
|
|
621 | 1 |
return result; |
622 |
}
|
|
623 |
|
|
624 |
/*************************************************
|
|
625 |
* Find index of function in vtbl[0..dim] that
|
|
626 |
* this function overrides.
|
|
627 |
* Prefer an exact match to a covariant one.
|
|
628 |
* Params:
|
|
629 |
* vtbl = vtable to use
|
|
630 |
* dim = maximal vtable dimension
|
|
631 |
* Returns:
|
|
632 |
* -1 didn't find one
|
|
633 |
* -2 can't determine because of forward references
|
|
634 |
*/
|
|
635 |
final int findVtblIndex(Dsymbols* vtbl, int dim) |
|
636 |
{
|
|
637 |
//printf("findVtblIndex() %s\n", toChars());
|
|
638 | 1 |
FuncDeclaration mismatch = null; |
639 | 1 |
StorageClass mismatchstc = 0; |
640 | 1 |
int mismatchvi = -1; |
641 | 1 |
int exactvi = -1; |
642 | 1 |
int bestvi = -1; |
643 | 1 |
for (int vi = 0; vi < dim; vi++) |
644 |
{
|
|
645 | 1 |
FuncDeclaration fdv = (*vtbl)[vi].isFuncDeclaration(); |
646 | 1 |
if (fdv && fdv.ident == ident) |
647 |
{
|
|
648 | 1 |
if (type.equals(fdv.type)) // if exact match |
649 |
{
|
|
650 | 1 |
if (fdv.parent.isClassDeclaration()) |
651 |
{
|
|
652 | 1 |
if (fdv.isFuture()) |
653 |
{
|
|
654 | 1 |
bestvi = vi; |
655 | 1 |
continue; // keep looking |
656 |
}
|
|
657 | 1 |
return vi; // no need to look further |
658 |
}
|
|
659 |
|
|
660 | 1 |
if (exactvi >= 0) |
661 |
{
|
|
662 | 1 |
error("cannot determine overridden function"); |
663 | 1 |
return exactvi; |
664 |
}
|
|
665 | 1 |
exactvi = vi; |
666 | 1 |
bestvi = vi; |
667 | 1 |
continue; |
668 |
}
|
|
669 |
|
|
670 | 1 |
StorageClass stc = 0; |
671 | 1 |
int cov = type.covariant(fdv.type, &stc); |
672 |
//printf("\tbaseclass cov = %d\n", cov);
|
|
673 | 1 |
switch (cov) |
674 |
{
|
|
675 | 1 |
case 0: |
676 |
// types are distinct
|
|
677 | 1 |
break; |
678 |
|
|
679 | 1 |
case 1: |
680 | 1 |
bestvi = vi; // covariant, but not identical |
681 | 1 |
break; |
682 |
// keep looking for an exact match
|
|
683 |
|
|
684 | 1 |
case 2: |
685 | 1 |
mismatchvi = vi; |
686 | 1 |
mismatchstc = stc; |
687 | 1 |
mismatch = fdv; // overrides, but is not covariant |
688 | 1 |
break; |
689 |
// keep looking for an exact match
|
|
690 |
|
|
691 |
case 3: |
|
692 |
return -2; // forward references |
|
693 |
|
|
694 |
default: |
|
695 |
assert(0); |
|
696 |
}
|
|
697 |
}
|
|
698 |
}
|
|
699 | 1 |
if (bestvi == -1 && mismatch) |
700 |
{
|
|
701 |
//type.print();
|
|
702 |
//mismatch.type.print();
|
|
703 |
//printf("%s %s\n", type.deco, mismatch.type.deco);
|
|
704 |
//printf("stc = %llx\n", mismatchstc);
|
|
705 | 1 |
if (mismatchstc) |
706 |
{
|
|
707 |
// Fix it by modifying the type to add the storage classes
|
|
708 | 1 |
type = type.addStorageClass(mismatchstc); |
709 | 1 |
bestvi = mismatchvi; |
710 |
}
|
|
711 |
}
|
|
712 | 1 |
return bestvi; |
713 |
}
|
|
714 |
|
|
715 |
/*********************************
|
|
716 |
* If function a function in a base class,
|
|
717 |
* return that base class.
|
|
718 |
* Returns:
|
|
719 |
* base class if overriding, null if not
|
|
720 |
*/
|
|
721 |
final BaseClass* overrideInterface() |
|
722 |
{
|
|
723 |
if (ClassDeclaration cd = toParent2().isClassDeclaration()) |
|
724 |
{
|
|
725 |
foreach (b; cd.interfaces) |
|
726 |
{
|
|
727 |
auto v = findVtblIndex(&b.sym.vtbl, cast(int)b.sym.vtbl.dim); |
|
728 |
if (v >= 0) |
|
729 |
return b; |
|
730 |
}
|
|
731 |
}
|
|
732 |
return null; |
|
733 |
}
|
|
734 |
|
|
735 |
/****************************************************
|
|
736 |
* Overload this FuncDeclaration with the new one f.
|
|
737 |
* Return true if successful; i.e. no conflict.
|
|
738 |
*/
|
|
739 |
override bool overloadInsert(Dsymbol s) |
|
740 |
{
|
|
741 |
//printf("FuncDeclaration::overloadInsert(s = %s) this = %s\n", s.toChars(), toChars());
|
|
742 | 1 |
assert(s != this); |
743 | 1 |
AliasDeclaration ad = s.isAliasDeclaration(); |
744 | 1 |
if (ad) |
745 |
{
|
|
746 | 1 |
if (overnext) |
747 |
return overnext.overloadInsert(ad); |
|
748 | 1 |
if (!ad.aliassym && ad.type.ty != Tident && ad.type.ty != Tinstance && ad.type.ty != Ttypeof) |
749 |
{
|
|
750 |
//printf("\tad = '%s'\n", ad.type.toChars());
|
|
751 | 1 |
return false; |
752 |
}
|
|
753 | 1 |
overnext = ad; |
754 |
//printf("\ttrue: no conflict\n");
|
|
755 | 1 |
return true; |
756 |
}
|
|
757 | 1 |
TemplateDeclaration td = s.isTemplateDeclaration(); |
758 | 1 |
if (td) |
759 |
{
|
|
760 | 1 |
if (!td.funcroot) |
761 | 1 |
td.funcroot = this; |
762 | 1 |
if (overnext) |
763 | 1 |
return overnext.overloadInsert(td); |
764 | 1 |
overnext = td; |
765 | 1 |
return true; |
766 |
}
|
|
767 | 1 |
FuncDeclaration fd = s.isFuncDeclaration(); |
768 | 1 |
if (!fd) |
769 |
return false; |
|
770 |
|
|
771 |
version (none) |
|
772 |
{
|
|
773 |
/* Disable this check because:
|
|
774 |
* const void foo();
|
|
775 |
* semantic() isn't run yet on foo(), so the const hasn't been
|
|
776 |
* applied yet.
|
|
777 |
*/
|
|
778 |
if (type) |
|
779 |
{
|
|
780 |
printf("type = %s\n", type.toChars()); |
|
781 |
printf("fd.type = %s\n", fd.type.toChars()); |
|
782 |
}
|
|
783 |
// fd.type can be NULL for overloaded constructors
|
|
784 |
if (type && fd.type && fd.type.covariant(type) && fd.type.mod == type.mod && !isFuncAliasDeclaration()) |
|
785 |
{
|
|
786 |
//printf("\tfalse: conflict %s\n", kind());
|
|
787 |
return false; |
|
788 |
}
|
|
789 |
}
|
|
790 |
|
|
791 | 1 |
if (overnext) |
792 |
{
|
|
793 | 1 |
td = overnext.isTemplateDeclaration(); |
794 | 1 |
if (td) |
795 | 1 |
fd.overloadInsert(td); |
796 |
else
|
|
797 | 1 |
return overnext.overloadInsert(fd); |
798 |
}
|
|
799 | 1 |
overnext = fd; |
800 |
//printf("\ttrue: no conflict\n");
|
|
801 | 1 |
return true; |
802 |
}
|
|
803 |
|
|
804 |
/********************************************
|
|
805 |
* Find function in overload list that exactly matches t.
|
|
806 |
*/
|
|
807 |
extern (D) final FuncDeclaration overloadExactMatch(Type t) |
|
808 |
{
|
|
809 | 1 |
FuncDeclaration fd; |
810 | 1 |
overloadApply(this, (Dsymbol s) |
811 |
{
|
|
812 | 1 |
auto f = s.isFuncDeclaration(); |
813 | 1 |
if (!f) |
814 | 1 |
return 0; |
815 | 1 |
if (t.equals(f.type)) |
816 |
{
|
|
817 | 1 |
fd = f; |
818 | 1 |
return 1; |
819 |
}
|
|
820 |
|
|
821 |
/* Allow covariant matches, as long as the return type
|
|
822 |
* is just a const conversion.
|
|
823 |
* This allows things like pure functions to match with an impure function type.
|
|
824 |
*/
|
|
825 | 1 |
if (t.ty == Tfunction) |
826 |
{
|
|
827 | 1 |
auto tf = cast(TypeFunction)f.type; |
828 | 1 |
if (tf.covariant(t) == 1 && |
829 | 1 |
tf.nextOf().implicitConvTo(t.nextOf()) >= MATCH.constant) |
830 |
{
|
|
831 | 1 |
fd = f; |
832 | 1 |
return 1; |
833 |
}
|
|
834 |
}
|
|
835 | 1 |
return 0; |
836 |
});
|
|
837 | 1 |
return fd; |
838 |
}
|
|
839 |
|
|
840 |
/********************************************
|
|
841 |
* Find function in overload list that matches to the 'this' modifier.
|
|
842 |
* There's four result types.
|
|
843 |
*
|
|
844 |
* 1. If the 'tthis' matches only one candidate, it's an "exact match".
|
|
845 |
* Returns the function and 'hasOverloads' is set to false.
|
|
846 |
* eg. If 'tthis" is mutable and there's only one mutable method.
|
|
847 |
* 2. If there's two or more match candidates, but a candidate function will be
|
|
848 |
* a "better match".
|
|
849 |
* Returns the better match function but 'hasOverloads' is set to true.
|
|
850 |
* eg. If 'tthis' is mutable, and there's both mutable and const methods,
|
|
851 |
* the mutable method will be a better match.
|
|
852 |
* 3. If there's two or more match candidates, but there's no better match,
|
|
853 |
* Returns null and 'hasOverloads' is set to true to represent "ambiguous match".
|
|
854 |
* eg. If 'tthis' is mutable, and there's two or more mutable methods.
|
|
855 |
* 4. If there's no candidates, it's "no match" and returns null with error report.
|
|
856 |
* e.g. If 'tthis' is const but there's no const methods.
|
|
857 |
*/
|
|
858 |
extern (D) final FuncDeclaration overloadModMatch(const ref Loc loc, Type tthis, ref bool hasOverloads) |
|
859 |
{
|
|
860 |
//printf("FuncDeclaration::overloadModMatch('%s')\n", toChars());
|
|
861 | 1 |
MatchAccumulator m; |
862 | 1 |
overloadApply(this, (Dsymbol s) |
863 |
{
|
|
864 | 1 |
auto f = s.isFuncDeclaration(); |
865 | 1 |
if (!f || f == m.lastf) // skip duplicates |
866 |
return 0; |
|
867 |
|
|
868 | 1 |
auto tf = f.type.toTypeFunction(); |
869 |
//printf("tf = %s\n", tf.toChars());
|
|
870 |
|
|
871 | 1 |
MATCH match; |
872 | 1 |
if (tthis) // non-static functions are preferred than static ones |
873 |
{
|
|
874 | 1 |
if (f.needThis()) |
875 | 1 |
match = f.isCtorDeclaration() ? MATCH.exact : MODmethodConv(tthis.mod, tf.mod); |
876 |
else
|
|
877 |
match = MATCH.constant; // keep static function in overload candidates |
|
878 |
}
|
|
879 |
else // static functions are preferred than non-static ones |
|
880 |
{
|
|
881 |
if (f.needThis()) |
|
882 |
match = MATCH.convert; |
|
883 |
else
|
|
884 |
match = MATCH.exact; |
|
885 |
}
|
|
886 | 1 |
if (match == MATCH.nomatch) |
887 | 1 |
return 0; |
888 |
|
|
889 | 1 |
if (match > m.last) goto LcurrIsBetter; |
890 | 1 |
if (match < m.last) goto LlastIsBetter; |
891 |
|
|
892 |
// See if one of the matches overrides the other.
|
|
893 |
if (m.lastf.overrides(f)) goto LlastIsBetter; |
|
894 |
if (f.overrides(m.lastf)) goto LcurrIsBetter; |
|
895 |
|
|
896 |
//printf("\tambiguous\n");
|
|
897 |
m.nextf = f; |
|
898 |
m.count++; |
|
899 |
return 0; |
|
900 |
|
|
901 |
LlastIsBetter: |
|
902 |
//printf("\tlastbetter\n");
|
|
903 | 1 |
m.count++; // count up |
904 | 1 |
return 0; |
905 |
|
|
906 |
LcurrIsBetter: |
|
907 |
//printf("\tisbetter\n");
|
|
908 | 1 |
if (m.last <= MATCH.convert) |
909 |
{
|
|
910 |
// clear last secondary matching
|
|
911 | 1 |
m.nextf = null; |
912 | 1 |
m.count = 0; |
913 |
}
|
|
914 | 1 |
m.last = match; |
915 | 1 |
m.lastf = f; |
916 | 1 |
m.count++; // count up |
917 | 1 |
return 0; |
918 |
});
|
|
919 |
|
|
920 | 1 |
if (m.count == 1) // exact match |
921 |
{
|
|
922 | 1 |
hasOverloads = false; |
923 |
}
|
|
924 | 1 |
else if (m.count > 1) // better or ambiguous match |
925 |
{
|
|
926 | 1 |
hasOverloads = true; |
927 |
}
|
|
928 |
else // no match |
|
929 |
{
|
|
930 | 1 |
hasOverloads = true; |
931 | 1 |
auto tf = this.type.toTypeFunction(); |
932 | 1 |
assert(tthis); |
933 | 1 |
assert(!MODimplicitConv(tthis.mod, tf.mod)); // modifier mismatch |
934 |
{
|
|
935 | 1 |
OutBuffer thisBuf, funcBuf; |
936 | 1 |
MODMatchToBuffer(&thisBuf, tthis.mod, tf.mod); |
937 | 1 |
MODMatchToBuffer(&funcBuf, tf.mod, tthis.mod); |
938 | 1 |
.error(loc, "%smethod %s is not callable using a %sobject", |
939 |
funcBuf.peekChars(), this.toPrettyChars(), thisBuf.peekChars()); |
|
940 |
}
|
|
941 |
}
|
|
942 | 1 |
return m.lastf; |
943 |
}
|
|
944 |
|
|
945 |
/********************************************
|
|
946 |
* find function template root in overload list
|
|
947 |
*/
|
|
948 |
extern (D) final TemplateDeclaration findTemplateDeclRoot() |
|
949 |
{
|
|
950 | 1 |
FuncDeclaration f = this; |
951 | 1 |
while (f && f.overnext) |
952 |
{
|
|
953 |
//printf("f.overnext = %p %s\n", f.overnext, f.overnext.toChars());
|
|
954 | 1 |
TemplateDeclaration td = f.overnext.isTemplateDeclaration(); |
955 | 1 |
if (td) |
956 | 1 |
return td; |
957 | 1 |
f = f.overnext.isFuncDeclaration(); |
958 |
}
|
|
959 | 1 |
return null; |
960 |
}
|
|
961 |
|
|
962 |
/********************************************
|
|
963 |
* Returns true if function was declared
|
|
964 |
* directly or indirectly in a unittest block
|
|
965 |
*/
|
|
966 |
final bool inUnittest() |
|
967 |
{
|
|
968 | 1 |
Dsymbol f = this; |
969 |
do
|
|
970 |
{
|
|
971 | 1 |
if (f.isUnitTestDeclaration()) |
972 |
return true; |
|
973 | 1 |
f = f.toParent(); |
974 |
}
|
|
975 | 1 |
while (f); |
976 | 1 |
return false; |
977 |
}
|
|
978 |
|
|
979 |
/*************************************
|
|
980 |
* Determine partial specialization order of 'this' vs g.
|
|
981 |
* This is very similar to TemplateDeclaration::leastAsSpecialized().
|
|
982 |
* Returns:
|
|
983 |
* match 'this' is at least as specialized as g
|
|
984 |
* 0 g is more specialized than 'this'
|
|
985 |
*/
|
|
986 |
final MATCH leastAsSpecialized(FuncDeclaration g) |
|
987 |
{
|
|
988 |
enum LOG_LEASTAS = 0; |
|
989 |
static if (LOG_LEASTAS) |
|
990 |
{
|
|
991 |
printf("%s.leastAsSpecialized(%s)\n", toChars(), g.toChars()); |
|
992 |
printf("%s, %s\n", type.toChars(), g.type.toChars()); |
|
993 |
}
|
|
994 |
|
|
995 |
/* This works by calling g() with f()'s parameters, and
|
|
996 |
* if that is possible, then f() is at least as specialized
|
|
997 |
* as g() is.
|
|
998 |
*/
|
|
999 |
|
|
1000 | 1 |
TypeFunction tf = type.toTypeFunction(); |
1001 | 1 |
TypeFunction tg = g.type.toTypeFunction(); |
1002 |
|
|
1003 |
/* If both functions have a 'this' pointer, and the mods are not
|
|
1004 |
* the same and g's is not const, then this is less specialized.
|
|
1005 |
*/
|
|
1006 | 1 |
if (needThis() && g.needThis() && tf.mod != tg.mod) |
1007 |
{
|
|
1008 | 1 |
if (isCtorDeclaration()) |
1009 |
{
|
|
1010 | 1 |
if (!MODimplicitConv(tg.mod, tf.mod)) |
1011 | 1 |
return MATCH.nomatch; |
1012 |
}
|
|
1013 |
else
|
|
1014 |
{
|
|
1015 | 1 |
if (!MODimplicitConv(tf.mod, tg.mod)) |
1016 | 1 |
return MATCH.nomatch; |
1017 |
}
|
|
1018 |
}
|
|
1019 |
|
|
1020 |
/* Create a dummy array of arguments out of the parameters to f()
|
|
1021 |
*/
|
|
1022 | 1 |
Expressions args; |
1023 | 1 |
foreach (u, p; tf.parameterList) |
1024 |
{
|
|
1025 | 1 |
Expression e; |
1026 | 1 |
if (p.isReference()) |
1027 |
{
|
|
1028 | 1 |
e = new IdentifierExp(Loc.initial, p.ident); |
1029 | 1 |
e.type = p.type; |
1030 |
}
|
|
1031 |
else
|
|
1032 | 1 |
e = p.type.defaultInitLiteral(Loc.initial); |
1033 | 1 |
args.push(e); |
1034 |
}
|
|
1035 |
|
|
1036 | 1 |
MATCH m = tg.callMatch(null, args[], 1); |
1037 | 1 |
if (m > MATCH.nomatch) |
1038 |
{
|
|
1039 |
/* A variadic parameter list is less specialized than a
|
|
1040 |
* non-variadic one.
|
|
1041 |
*/
|
|
1042 | 1 |
if (tf.parameterList.varargs && !tg.parameterList.varargs) |
1043 | 1 |
goto L1; // less specialized |
1044 |
|
|
1045 |
static if (LOG_LEASTAS) |
|
1046 |
{
|
|
1047 |
printf(" matches %d, so is least as specialized\n", m); |
|
1048 |
}
|
|
1049 | 1 |
return m; |
1050 |
}
|
|
1051 |
L1: |
|
1052 |
static if (LOG_LEASTAS) |
|
1053 |
{
|
|
1054 |
printf(" doesn't match, so is not as specialized\n"); |
|
1055 |
}
|
|
1056 | 1 |
return MATCH.nomatch; |
1057 |
}
|
|
1058 |
|
|
1059 |
/********************************
|
|
1060 |
* Labels are in a separate scope, one per function.
|
|
1061 |
*/
|
|
1062 |
final LabelDsymbol searchLabel(Identifier ident) |
|
1063 |
{
|
|
1064 | 1 |
Dsymbol s; |
1065 | 1 |
if (!labtab) |
1066 | 1 |
labtab = new DsymbolTable(); // guess we need one |
1067 |
|
|
1068 | 1 |
s = labtab.lookup(ident); |
1069 | 1 |
if (!s) |
1070 |
{
|
|
1071 | 1 |
s = new LabelDsymbol(ident); |
1072 | 1 |
labtab.insert(s); |
1073 |
}
|
|
1074 | 1 |
return cast(LabelDsymbol)s; |
1075 |
}
|
|
1076 |
|
|
1077 |
/*****************************************
|
|
1078 |
* Determine lexical level difference from `this` to nested function `fd`.
|
|
1079 |
* Params:
|
|
1080 |
* fd = target of call
|
|
1081 |
* intypeof = !=0 if inside typeof
|
|
1082 |
* Returns:
|
|
1083 |
* 0 same level
|
|
1084 |
* >0 decrease nesting by number
|
|
1085 |
* -1 increase nesting by 1 (`fd` is nested within `this`)
|
|
1086 |
* LevelError error, `this` cannot call `fd`
|
|
1087 |
*/
|
|
1088 |
final int getLevel(FuncDeclaration fd, int intypeof) |
|
1089 |
{
|
|
1090 |
//printf("FuncDeclaration::getLevel(fd = '%s')\n", fd.toChars());
|
|
1091 | 1 |
Dsymbol fdparent = fd.toParent2(); |
1092 | 1 |
if (fdparent == this) |
1093 |
return -1; |
|
1094 |
|
|
1095 | 1 |
Dsymbol s = this; |
1096 | 1 |
int level = 0; |
1097 | 1 |
while (fd != s && fdparent != s.toParent2()) |
1098 |
{
|
|
1099 |
//printf("\ts = %s, '%s'\n", s.kind(), s.toChars());
|
|
1100 | 1 |
if (auto thisfd = s.isFuncDeclaration()) |
1101 |
{
|
|
1102 | 1 |
if (!thisfd.isNested() && !thisfd.vthis && !intypeof) |
1103 | 1 |
return LevelError; |
1104 |
}
|
|
1105 |
else
|
|
1106 |
{
|
|
1107 | 1 |
if (auto thiscd = s.isAggregateDeclaration()) |
1108 |
{
|
|
1109 |
/* AggregateDeclaration::isNested returns true only when
|
|
1110 |
* it has a hidden pointer.
|
|
1111 |
* But, calling the function belongs unrelated lexical scope
|
|
1112 |
* is still allowed inside typeof.
|
|
1113 |
*
|
|
1114 |
* struct Map(alias fun) {
|
|
1115 |
* typeof({ return fun(); }) RetType;
|
|
1116 |
* // No member function makes Map struct 'not nested'.
|
|
1117 |
* }
|
|
1118 |
*/
|
|
1119 | 1 |
if (!thiscd.isNested() && !intypeof) |
1120 |
return LevelError; |
|
1121 |
}
|
|
1122 |
else
|
|
1123 | 1 |
return LevelError; |
1124 |
}
|
|
1125 |
|
|
1126 | 1 |
s = s.toParentP(fd); |
1127 | 1 |
assert(s); |
1128 | 1 |
level++; |
1129 |
}
|
|
1130 | 1 |
return level; |
1131 |
}
|
|
1132 |
|
|
1133 |
/***********************************
|
|
1134 |
* Determine lexical level difference from `this` to nested function `fd`.
|
|
1135 |
* Issue error if `this` cannot call `fd`.
|
|
1136 |
* Params:
|
|
1137 |
* loc = location for error messages
|
|
1138 |
* sc = context
|
|
1139 |
* fd = target of call
|
|
1140 |
* Returns:
|
|
1141 |
* 0 same level
|
|
1142 |
* >0 decrease nesting by number
|
|
1143 |
* -1 increase nesting by 1 (`fd` is nested within 'this')
|
|
1144 |
* LevelError error
|
|
1145 |
*/
|
|
1146 |
final int getLevelAndCheck(const ref Loc loc, Scope* sc, FuncDeclaration fd) |
|
1147 |
{
|
|
1148 | 1 |
int level = getLevel(fd, sc.intypeof); |
1149 | 1 |
if (level != LevelError) |
1150 | 1 |
return level; |
1151 |
|
|
1152 |
// Don't give error if in template constraint
|
|
1153 | 1 |
if (!(sc.flags & SCOPE.constraint)) |
1154 |
{
|
|
1155 | 1 |
const(char)* xstatic = isStatic() ? "static " : ""; |
1156 |
// better diagnostics for static functions
|
|
1157 | 1 |
.error(loc, "%s%s %s cannot access frame of function %s", |
1158 |
xstatic, kind(), toPrettyChars(), fd.toPrettyChars()); |
|
1159 | 1 |
return LevelError; |
1160 |
}
|
|
1161 | 1 |
return 1; |
1162 |
}
|
|
1163 |
|
|
1164 |
enum LevelError = -2; |
|
1165 |
|
|
1166 |
override const(char)* toPrettyChars(bool QualifyTypes = false) |
|
1167 |
{
|
|
1168 | 1 |
if (isMain()) |
1169 | 1 |
return "D main"; |
1170 |
else
|
|
1171 | 1 |
return Dsymbol.toPrettyChars(QualifyTypes); |
1172 |
}
|
|
1173 |
|
|
1174 |
/** for diagnostics, e.g. 'int foo(int x, int y) pure' */
|
|
1175 |
final const(char)* toFullSignature() |
|
1176 |
{
|
|
1177 | 1 |
OutBuffer buf; |
1178 | 1 |
functionToBufferWithIdent(type.toTypeFunction(), &buf, toChars()); |
1179 | 1 |
return buf.extractChars(); |
1180 |
}
|
|
1181 |
|
|
1182 |
final bool isMain() const |
|
1183 |
{
|
|
1184 | 1 |
return ident == Id.main && linkage != LINK.c && !isMember() && !isNested(); |
1185 |
}
|
|
1186 |
|
|
1187 |
final bool isCMain() const |
|
1188 |
{
|
|
1189 | 1 |
return ident == Id.main && linkage == LINK.c && !isMember() && !isNested(); |
1190 |
}
|
|
1191 |
|
|
1192 |
final bool isWinMain() const |
|
1193 |
{
|
|
1194 |
//printf("FuncDeclaration::isWinMain() %s\n", toChars());
|
|
1195 |
version (none) |
|
1196 |
{
|
|
1197 |
bool x = ident == Id.WinMain && linkage != LINK.c && !isMember(); |
|
1198 |
printf("%s\n", x ? "yes" : "no"); |
|
1199 |
return x; |
|
1200 |
}
|
|
1201 |
else
|
|
1202 |
{
|
|
1203 | 1 |
return ident == Id.WinMain && linkage != LINK.c && !isMember(); |
1204 |
}
|
|
1205 |
}
|
|
1206 |
|
|
1207 |
final bool isDllMain() const |
|
1208 |
{
|
|
1209 | 1 |
return ident == Id.DllMain && linkage != LINK.c && !isMember(); |
1210 |
}
|
|
1211 |
|
|
1212 |
final bool isRtInit() const |
|
1213 |
{
|
|
1214 | 1 |
return ident == Id.rt_init && linkage == LINK.c && !isMember() && !isNested(); |
1215 |
}
|
|
1216 |
|
|
1217 |
override final bool isExport() const |
|
1218 |
{
|
|
1219 | 1 |
return protection.kind == Prot.Kind.export_; |
1220 |
}
|
|
1221 |
|
|
1222 |
override final bool isImportedSymbol() const |
|
1223 |
{
|
|
1224 |
//printf("isImportedSymbol()\n");
|
|
1225 |
//printf("protection = %d\n", protection);
|
|
1226 | 1 |
return (protection.kind == Prot.Kind.export_) && !fbody; |
1227 |
}
|
|
1228 |
|
|
1229 |
override final bool isCodeseg() const pure nothrow @nogc @safe |
|
1230 |
{
|
|
1231 | 1 |
return true; // functions are always in the code segment |
1232 |
}
|
|
1233 |
|
|
1234 |
override final bool isOverloadable() const |
|
1235 |
{
|
|
1236 | 1 |
return true; // functions can be overloaded |
1237 |
}
|
|
1238 |
|
|
1239 |
/***********************************
|
|
1240 |
* Override so it can work even if semantic() hasn't yet
|
|
1241 |
* been run.
|
|
1242 |
*/
|
|
1243 |
override final bool isAbstract() |
|
1244 |
{
|
|
1245 | 1 |
if (storage_class & STC.abstract_) |
1246 | 1 |
return true; |
1247 | 1 |
if (semanticRun >= PASS.semanticdone) |
1248 | 1 |
return false; |
1249 |
|
|
1250 | 1 |
if (_scope) |
1251 |
{
|
|
1252 | 1 |
if (_scope.stc & STC.abstract_) |
1253 |
return true; |
|
1254 | 1 |
parent = _scope.parent; |
1255 | 1 |
Dsymbol parent = toParent(); |
1256 | 1 |
if (parent.isInterfaceDeclaration()) |
1257 |
return true; |
|
1258 |
}
|
|
1259 | 1 |
return false; |
1260 |
}
|
|
1261 |
|
|
1262 |
/**********************************
|
|
1263 |
* Decide if attributes for this function can be inferred from examining
|
|
1264 |
* the function body.
|
|
1265 |
* Returns:
|
|
1266 |
* true if can
|
|
1267 |
*/
|
|
1268 |
final bool canInferAttributes(Scope* sc) |
|
1269 |
{
|
|
1270 | 1 |
if (!fbody) |
1271 | 1 |
return false; |
1272 |
|
|
1273 | 1 |
if (isVirtualMethod()) |
1274 | 1 |
return false; // since they may be overridden |
1275 |
|
|
1276 | 1 |
if (sc.func && |
1277 |
/********** this is for backwards compatibility for the moment ********/
|
|
1278 | 1 |
(!isMember() || sc.func.isSafeBypassingInference() && !isInstantiated())) |
1279 | 1 |
return true; |
1280 |
|
|
1281 | 1 |
if (isFuncLiteralDeclaration() || // externs are not possible with literals |
1282 | 1 |
(storage_class & STC.inference) || // do attribute inference |
1283 | 1 |
(inferRetType && !isCtorDeclaration())) |
1284 | 1 |
return true; |
1285 |
|
|
1286 | 1 |
if (isInstantiated()) |
1287 |
{
|
|
1288 | 1 |
auto ti = parent.isTemplateInstance(); |
1289 | 1 |
if (ti is null || ti.isTemplateMixin() || ti.tempdecl.ident == ident) |
1290 | 1 |
return true; |
1291 |
}
|
|
1292 |
|
|
1293 | 1 |
return false; |
1294 |
}
|
|
1295 |
|
|
1296 |
/*****************************************
|
|
1297 |
* Initialize for inferring the attributes of this function.
|
|
1298 |
*/
|
|
1299 |
final void initInferAttributes() |
|
1300 |
{
|
|
1301 |
//printf("initInferAttributes() for %s (%s)\n", toPrettyChars(), ident.toChars());
|
|
1302 | 1 |
TypeFunction tf = type.toTypeFunction(); |
1303 | 1 |
if (tf.purity == PURE.impure) // purity not specified |
1304 | 1 |
flags |= FUNCFLAG.purityInprocess; |
1305 |
|
|
1306 | 1 |
if (tf.trust == TRUST.default_) |
1307 | 1 |
flags |= FUNCFLAG.safetyInprocess; |
1308 |
|
|
1309 | 1 |
if (!tf.isnothrow) |
1310 | 1 |
flags |= FUNCFLAG.nothrowInprocess; |
1311 |
|
|
1312 | 1 |
if (!tf.isnogc) |
1313 | 1 |
flags |= FUNCFLAG.nogcInprocess; |
1314 |
|
|
1315 | 1 |
if (!isVirtual() || introducing) |
1316 | 1 |
flags |= FUNCFLAG.returnInprocess; |
1317 |
|
|
1318 |
// Initialize for inferring STC.scope_
|
|
1319 | 1 |
if (global.params.vsafe) |
1320 | 1 |
flags |= FUNCFLAG.inferScope; |
1321 |
}
|
|
1322 |
|
|
1323 |
final PURE isPure() |
|
1324 |
{
|
|
1325 |
//printf("FuncDeclaration::isPure() '%s'\n", toChars());
|
|
1326 | 1 |
TypeFunction tf = type.toTypeFunction(); |
1327 | 1 |
if (flags & FUNCFLAG.purityInprocess) |
1328 | 1 |
setImpure(); |
1329 | 1 |
if (tf.purity == PURE.fwdref) |
1330 | 1 |
tf.purityLevel(); |
1331 | 1 |
PURE purity = tf.purity; |
1332 | 1 |
if (purity > PURE.weak && isNested()) |
1333 | 1 |
purity = PURE.weak; |
1334 | 1 |
if (purity > PURE.weak && needThis()) |
1335 |
{
|
|
1336 |
// The attribute of the 'this' reference affects purity strength
|
|
1337 | 1 |
if (type.mod & MODFlags.immutable_) |
1338 |
{
|
|
1339 |
}
|
|
1340 | 1 |
else if (type.mod & (MODFlags.const_ | MODFlags.wild) && purity >= PURE.const_) |
1341 | 1 |
purity = PURE.const_; |
1342 |
else
|
|
1343 | 1 |
purity = PURE.weak; |
1344 |
}
|
|
1345 | 1 |
tf.purity = purity; |
1346 |
// ^ This rely on the current situation that every FuncDeclaration has a
|
|
1347 |
// unique TypeFunction.
|
|
1348 | 1 |
return purity; |
1349 |
}
|
|
1350 |
|
|
1351 |
final PURE isPureBypassingInference() |
|
1352 |
{
|
|
1353 | 1 |
if (flags & FUNCFLAG.purityInprocess) |
1354 | 1 |
return PURE.fwdref; |
1355 |
else
|
|
1356 | 1 |
return isPure(); |
1357 |
}
|
|
1358 |
|
|
1359 |
/**************************************
|
|
1360 |
* The function is doing something impure,
|
|
1361 |
* so mark it as impure.
|
|
1362 |
* If there's a purity error, return true.
|
|
1363 |
*/
|
|
1364 |
extern (D) final bool setImpure() |
|
1365 |
{
|
|
1366 | 1 |
if (flags & FUNCFLAG.purityInprocess) |
1367 |
{
|
|
1368 | 1 |
flags &= ~FUNCFLAG.purityInprocess; |
1369 | 1 |
if (fes) |
1370 | 1 |
fes.func.setImpure(); |
1371 |
}
|
|
1372 | 1 |
else if (isPure()) |
1373 | 1 |
return true; |
1374 | 1 |
return false; |
1375 |
}
|
|
1376 |
|
|
1377 |
final bool isSafe() |
|
1378 |
{
|
|
1379 | 1 |
if (flags & FUNCFLAG.safetyInprocess) |
1380 | 1 |
setUnsafe(); |
1381 | 1 |
return type.toTypeFunction().trust == TRUST.safe; |
1382 |
}
|
|
1383 |
|
|
1384 |
final bool isSafeBypassingInference() |
|
1385 |
{
|
|
1386 | 1 |
return !(flags & FUNCFLAG.safetyInprocess) && isSafe(); |
1387 |
}
|
|
1388 |
|
|
1389 |
final bool isTrusted() |
|
1390 |
{
|
|
1391 | 1 |
if (flags & FUNCFLAG.safetyInprocess) |
1392 |
setUnsafe(); |
|
1393 | 1 |
return type.toTypeFunction().trust == TRUST.trusted; |
1394 |
}
|
|
1395 |
|
|
1396 |
/**************************************
|
|
1397 |
* The function is doing something unsafe,
|
|
1398 |
* so mark it as unsafe.
|
|
1399 |
* If there's a safe error, return true.
|
|
1400 |
*/
|
|
1401 |
extern (D) final bool setUnsafe() |
|
1402 |
{
|
|
1403 | 1 |
if (flags & FUNCFLAG.safetyInprocess) |
1404 |
{
|
|
1405 | 1 |
flags &= ~FUNCFLAG.safetyInprocess; |
1406 | 1 |
type.toTypeFunction().trust = TRUST.system; |
1407 | 1 |
if (fes) |
1408 | 1 |
fes.func.setUnsafe(); |
1409 |
}
|
|
1410 | 1 |
else if (isSafe()) |
1411 | 1 |
return true; |
1412 | 1 |
return false; |
1413 |
}
|
|
1414 |
|
|
1415 |
final bool isNogc() |
|
1416 |
{
|
|
1417 |
//printf("isNogc() %s, inprocess: %d\n", toChars(), !!(flags & FUNCFLAG.nogcInprocess));
|
|
1418 | 1 |
if (flags & FUNCFLAG.nogcInprocess) |
1419 | 1 |
setGC(); |
1420 | 1 |
return type.toTypeFunction().isnogc; |
1421 |
}
|
|
1422 |
|
|
1423 |
final bool isNogcBypassingInference() |
|
1424 |
{
|
|
1425 | 1 |
return !(flags & FUNCFLAG.nogcInprocess) && isNogc(); |
1426 |
}
|
|
1427 |
|
|
1428 |
/**************************************
|
|
1429 |
* The function is doing something that may allocate with the GC,
|
|
1430 |
* so mark it as not nogc (not no-how).
|
|
1431 |
* Returns:
|
|
1432 |
* true if function is marked as @nogc, meaning a user error occurred
|
|
1433 |
*/
|
|
1434 |
extern (D) final bool setGC() |
|
1435 |
{
|
|
1436 |
//printf("setGC() %s\n", toChars());
|
|
1437 | 1 |
if (flags & FUNCFLAG.nogcInprocess && semanticRun < PASS.semantic3 && _scope) |
1438 |
{
|
|
1439 | 1 |
this.semantic2(_scope); |
1440 | 1 |
this.semantic3(_scope); |
1441 |
}
|
|
1442 |
|
|
1443 | 1 |
if (flags & FUNCFLAG.nogcInprocess) |
1444 |
{
|
|
1445 | 1 |
flags &= ~FUNCFLAG.nogcInprocess; |
1446 | 1 |
type.toTypeFunction().isnogc = false; |
1447 | 1 |
if (fes) |
1448 | 1 |
fes.func.setGC(); |
1449 |
}
|
|
1450 | 1 |
else if (isNogc()) |
1451 | 1 |
return true; |
1452 | 1 |
return false; |
1453 |
}
|
|
1454 |
|
|
1455 |
extern (D) final void printGCUsage(const ref Loc loc, const(char)* warn) |
|
1456 |
{
|
|
1457 | 1 |
if (!global.params.vgc) |
1458 | 1 |
return; |
1459 |
|
|
1460 | 1 |
Module m = getModule(); |
1461 | 1 |
if (m && m.isRoot() && !inUnittest()) |
1462 |
{
|
|
1463 | 1 |
message(loc, "vgc: %s", warn); |
1464 |
}
|
|
1465 |
}
|
|
1466 |
|
|
1467 |
/********************************************
|
|
1468 |
* See if pointers from function parameters, mutable globals, or uplevel functions
|
|
1469 |
* could leak into return value.
|
|
1470 |
* Returns:
|
|
1471 |
* true if the function return value is isolated from
|
|
1472 |
* any inputs to the function
|
|
1473 |
*/
|
|
1474 |
extern (D) final bool isReturnIsolated() |
|
1475 |
{
|
|
1476 |
//printf("isReturnIsolated(this: %s)\n", this.toChars);
|
|
1477 | 1 |
TypeFunction tf = type.toTypeFunction(); |
1478 | 1 |
assert(tf.next); |
1479 |
|
|
1480 | 1 |
Type treti = tf.next; |
1481 | 1 |
if (tf.isref) |
1482 | 1 |
return isTypeIsolatedIndirect(treti); // check influence from parameters |
1483 |
|
|
1484 | 1 |
return isTypeIsolated(treti); |
1485 |
}
|
|
1486 |
|
|
1487 |
/********************
|
|
1488 |
* See if pointers from function parameters, mutable globals, or uplevel functions
|
|
1489 |
* could leak into type `t`.
|
|
1490 |
* Params:
|
|
1491 |
* t = type to check if it is isolated
|
|
1492 |
* Returns:
|
|
1493 |
* true if `t` is isolated from
|
|
1494 |
* any inputs to the function
|
|
1495 |
*/
|
|
1496 |
extern (D) final bool isTypeIsolated(Type t) |
|
1497 |
{
|
|
1498 | 1 |
StringTable!Type parentTypes; |
1499 | 1 |
parentTypes._init(); |
1500 | 1 |
return isTypeIsolated(t, parentTypes); |
1501 |
}
|
|
1502 |
|
|
1503 |
///ditto
|
|
1504 |
extern (D) final bool isTypeIsolated(Type t, ref StringTable!Type parentTypes) |
|
1505 |
{
|
|
1506 |
//printf("this: %s, isTypeIsolated(t: %s)\n", this.toChars(), t.toChars());
|
|
1507 |
|
|
1508 | 1 |
t = t.baseElemOf(); |
1509 | 1 |
switch (t.ty) |
1510 |
{
|
|
1511 | 1 |
case Tarray: |
1512 | 1 |
case Tpointer: |
1513 | 1 |
return isTypeIsolatedIndirect(t.nextOf()); // go down one level |
1514 |
|
|
1515 |
case Taarray: |
|
1516 | 1 |
case Tclass: |
1517 | 1 |
return isTypeIsolatedIndirect(t); |
1518 |
|
|
1519 | 1 |
case Tstruct: |
1520 |
/* Drill down and check the struct's fields
|
|
1521 |
*/
|
|
1522 | 1 |
auto sym = t.toDsymbol(null).isStructDeclaration(); |
1523 | 1 |
const tName = t.toChars.toDString; |
1524 | 1 |
const entry = parentTypes.insert(tName, t); |
1525 | 1 |
if (entry == null) |
1526 |
{
|
|
1527 |
//we've already seen this type in a parent, not isolated
|
|
1528 | 1 |
return false; |
1529 |
}
|
|
1530 | 1 |
foreach (v; sym.fields) |
1531 |
{
|
|
1532 | 1 |
Type tmi = v.type.addMod(t.mod); |
1533 |
//printf("\tt = %s, v: %s, vtype: %s, tmi = %s\n",
|
|
1534 |
// t.toChars(), v.toChars(), v.type.toChars(), tmi.toChars());
|
|
1535 | 1 |
if (!isTypeIsolated(tmi, parentTypes)) |
1536 | 1 |
return false; |
1537 |
}
|
|
1538 | 1 |
return true; |
1539 |
|
|
1540 | 1 |
default: |
1541 | 1 |
return true; |
1542 |
}
|
|
1543 |
}
|
|
1544 |
|
|
1545 |
/********************************************
|
|
1546 |
* Params:
|
|
1547 |
* t = type of object to test one level of indirection down
|
|
1548 |
* Returns:
|
|
1549 |
* true if an object typed `t` has no indirections
|
|
1550 |
* which could have come from the function's parameters, mutable
|
|
1551 |
* globals, or uplevel functions.
|
|
1552 |
*/
|
|
1553 |
private bool isTypeIsolatedIndirect(Type t) |
|
1554 |
{
|
|
1555 |
//printf("isTypeIsolatedIndirect(t: %s)\n", t.toChars());
|
|
1556 | 1 |
assert(t); |
1557 |
|
|
1558 |
/* Since `t` is one level down from an indirection, it could pick
|
|
1559 |
* up a reference to a mutable global or an outer function, so
|
|
1560 |
* return false.
|
|
1561 |
*/
|
|
1562 | 1 |
if (!isPureBypassingInference() || isNested()) |
1563 | 1 |
return false; |
1564 |
|
|
1565 | 1 |
TypeFunction tf = type.toTypeFunction(); |
1566 |
|
|
1567 |
//printf("isTypeIsolatedIndirect(%s) t = %s\n", tf.toChars(), t.toChars());
|
|
1568 |
|
|
1569 | 1 |
foreach (i, fparam; tf.parameterList) |
1570 |
{
|
|
1571 | 1 |
Type tp = fparam.type; |
1572 | 1 |
if (!tp) |
1573 |
continue; |
|
1574 |
|
|
1575 | 1 |
if (fparam.storageClass & (STC.lazy_ | STC.out_ | STC.ref_)) |
1576 |
{
|
|
1577 | 1 |
if (!traverseIndirections(tp, t)) |
1578 | 1 |
return false; |
1579 |
continue; |
|
1580 |
}
|
|
1581 |
|
|
1582 |
/* Goes down one level of indirection, then calls traverseIndirection() on
|
|
1583 |
* the result.
|
|
1584 |
* Returns:
|
|
1585 |
* true if t is isolated from tp
|
|
1586 |
*/
|
|
1587 |
static bool traverse(Type tp, Type t) |
|
1588 |
{
|
|
1589 | 1 |
tp = tp.baseElemOf(); |
1590 | 1 |
switch (tp.ty) |
1591 |
{
|
|
1592 | 1 |
case Tarray: |
1593 | 1 |
case Tpointer: |
1594 | 1 |
return traverseIndirections(tp.nextOf(), t); |
1595 |
|
|
1596 |
case Taarray: |
|
1597 | 1 |
case Tclass: |
1598 | 1 |
return traverseIndirections(tp, t); |
1599 |
|
|
1600 | 1 |
case Tstruct: |
1601 |
/* Drill down and check the struct's fields
|
|
1602 |
*/
|
|
1603 | 1 |
auto sym = tp.toDsymbol(null).isStructDeclaration(); |
1604 | 1 |
foreach (v; sym.fields) |
1605 |
{
|
|
1606 | 1 |
Type tprmi = v.type.addMod(tp.mod); |
1607 |
//printf("\ttp = %s, tprmi = %s\n", tp.toChars(), tprmi.toChars());
|
|
1608 | 1 |
if (!traverse(tprmi, t)) |
1609 | 1 |
return false; |
1610 |
}
|
|
1611 | 1 |
return true; |
1612 |
|
|
1613 | 1 |
default: |
1614 | 1 |
return true; |
1615 |
}
|
|
1616 |
}
|
|
1617 |
|
|
1618 | 1 |
if (!traverse(tp, t)) |
1619 | 1 |
return false; |
1620 |
}
|
|
1621 |
// The 'this' reference is a parameter, too
|
|
1622 | 1 |
if (AggregateDeclaration ad = isCtorDeclaration() ? null : isThis()) |
1623 |
{
|
|
1624 | 1 |
Type tthis = ad.getType().addMod(tf.mod); |
1625 |
//printf("\ttthis = %s\n", tthis.toChars());
|
|
1626 | 1 |
if (!traverseIndirections(tthis, t)) |
1627 | 1 |
return false; |
1628 |
}
|
|
1629 |
|
|
1630 | 1 |
return true; |
1631 |
}
|
|
1632 |
|
|
1633 |
/****************************************
|
|
1634 |
* Determine if function needs a static frame pointer.
|
|
1635 |
* Returns:
|
|
1636 |
* `true` if function is really nested within other function.
|
|
1637 |
* Contracts:
|
|
1638 |
* If isNested() returns true, isThis() should return false,
|
|
1639 |
* unless the function needs a dual-context pointer.
|
|
1640 |
*/
|
|
1641 |
bool isNested() const |
|
1642 |
{
|
|
1643 | 1 |
auto f = toAliasFunc(); |
1644 |
//printf("\ttoParent2() = '%s'\n", f.toParent2().toChars());
|
|
1645 | 1 |
return ((f.storage_class & STC.static_) == 0) && |
1646 | 1 |
(f.linkage == LINK.d) && |
1647 | 1 |
(f.toParent2().isFuncDeclaration() !is null || |
1648 | 1 |
f.toParent2() !is f.toParentLocal()); |
1649 |
}
|
|
1650 |
|
|
1651 |
/****************************************
|
|
1652 |
* Determine if function is a non-static member function
|
|
1653 |
* that has an implicit 'this' expression.
|
|
1654 |
* Returns:
|
|
1655 |
* The aggregate it is a member of, or null.
|
|
1656 |
* Contracts:
|
|
1657 |
* Both isThis() and isNested() should return true if function needs a dual-context pointer,
|
|
1658 |
* otherwise if isThis() returns true, isNested() should return false.
|
|
1659 |
*/
|
|
1660 |
override inout(AggregateDeclaration) isThis() inout |
|
1661 |
{
|
|
1662 |
//printf("+FuncDeclaration::isThis() '%s'\n", toChars());
|
|
1663 | 1 |
auto ad = (storage_class & STC.static_) ? objc.isThis(this) : isMemberLocal(); |
1664 |
//printf("-FuncDeclaration::isThis() %p\n", ad);
|
|
1665 | 1 |
return ad; |
1666 |
}
|
|
1667 |
|
|
1668 |
override final bool needThis() |
|
1669 |
{
|
|
1670 |
//printf("FuncDeclaration::needThis() '%s'\n", toChars());
|
|
1671 | 1 |
return toAliasFunc().isThis() !is null; |
1672 |
}
|
|
1673 |
|
|
1674 |
// Determine if a function is pedantically virtual
|
|
1675 |
final bool isVirtualMethod() |
|
1676 |
{
|
|
1677 | 1 |
if (toAliasFunc() != this) |
1678 | 1 |
return toAliasFunc().isVirtualMethod(); |
1679 |
|
|
1680 |
//printf("FuncDeclaration::isVirtualMethod() %s\n", toChars());
|
|
1681 | 1 |
if (!isVirtual()) |
1682 | 1 |
return false; |
1683 |
// If it's a final method, and does not override anything, then it is not virtual
|
|
1684 | 1 |
if (isFinalFunc() && foverrides.dim == 0) |
1685 |
{
|
|
1686 | 1 |
return false; |
1687 |
}
|
|
1688 | 1 |
return true; |
1689 |
}
|
|
1690 |
|
|
1691 |
// Determine if function goes into virtual function pointer table
|
|
1692 |
bool isVirtual() const |
|
1693 |
{
|
|
1694 | 1 |
if (toAliasFunc() != this) |
1695 | 1 |
return toAliasFunc().isVirtual(); |
1696 |
|
|
1697 | 1 |
auto p = toParent(); |
1698 |
|
|
1699 | 1 |
if (!isMember || !p.isClassDeclaration) |
1700 | 1 |
return false; |
1701 |
// https://issues.dlang.org/show_bug.cgi?id=19654
|
|
1702 | 1 |
if (p.isClassDeclaration.classKind == ClassKind.objc && !p.isInterfaceDeclaration) |
1703 |
return objc.isVirtual(this); |
|
1704 |
|
|
1705 |
version (none) |
|
1706 |
{
|
|
1707 |
printf("FuncDeclaration::isVirtual(%s)\n", toChars()); |
|
1708 |
printf("isMember:%p isStatic:%d private:%d ctor:%d !Dlinkage:%d\n", isMember(), isStatic(), protection == Prot.Kind.private_, isCtorDeclaration(), linkage != LINK.d); |
|
1709 |
printf("result is %d\n", isMember() && !(isStatic() || protection == Prot.Kind.private_ || protection == Prot.Kind.package_) && p.isClassDeclaration() && !(p.isInterfaceDeclaration() && isFinalFunc())); |
|
1710 |
}
|
|
1711 | 1 |
return !(isStatic() || protection.kind == Prot.Kind.private_ || protection.kind == Prot.Kind.package_) && !(p.isInterfaceDeclaration() && isFinalFunc()); |
1712 |
}
|
|
1713 |
|
|
1714 |
final bool isFinalFunc() const |
|
1715 |
{
|
|
1716 | 1 |
if (toAliasFunc() != this) |
1717 | 1 |
return toAliasFunc().isFinalFunc(); |
1718 |
|
|
1719 |
version (none) |
|
1720 |
{{
|
|
1721 |
auto cd = toParent().isClassDeclaration(); |
|
1722 |
printf("FuncDeclaration::isFinalFunc(%s), %x\n", toChars(), Declaration.isFinal()); |
|
1723 |
printf("%p %d %d %d\n", isMember(), isStatic(), Declaration.isFinal(), ((cd = toParent().isClassDeclaration()) !is null && cd.storage_class & STC.final_)); |
|
1724 |
printf("result is %d\n", isMember() && (Declaration.isFinal() || (cd !is null && cd.storage_class & STC.final_))); |
|
1725 |
if (cd) |
|
1726 |
printf("\tmember of %s\n", cd.toChars()); |
|
1727 |
}}
|
|
1728 | 1 |
if (!isMember()) |
1729 |
return false; |
|
1730 | 1 |
if (Declaration.isFinal()) |
1731 | 1 |
return true; |
1732 | 1 |
auto cd = toParent().isClassDeclaration(); |
1733 | 1 |
return (cd !is null) && (cd.storage_class & STC.final_); |
1734 |
}
|
|
1735 |
|
|
1736 |
bool addPreInvariant() |
|
1737 |
{
|
|
1738 | 1 |
auto ad = isThis(); |
1739 | 1 |
ClassDeclaration cd = ad ? ad.isClassDeclaration() : null; |
1740 | 1 |
return (ad && !(cd && cd.isCPPclass()) && global.params.useInvariants == CHECKENABLE.on && (protection.kind == Prot.Kind.protected_ || protection.kind == Prot.Kind.public_ || protection.kind == Prot.Kind.export_) && !naked); |
1741 |
}
|
|
1742 |
|
|
1743 |
bool addPostInvariant() |
|
1744 |
{
|
|
1745 | 1 |
auto ad = isThis(); |
1746 | 1 |
ClassDeclaration cd = ad ? ad.isClassDeclaration() : null; |
1747 | 1 |
return (ad && !(cd && cd.isCPPclass()) && ad.inv && global.params.useInvariants == CHECKENABLE.on && (protection.kind == Prot.Kind.protected_ || protection.kind == Prot.Kind.public_ || protection.kind == Prot.Kind.export_) && !naked); |
1748 |
}
|
|
1749 |
|
|
1750 |
override const(char)* kind() const |
|
1751 |
{
|
|
1752 | 1 |
return generated ? "generated function" : "function"; |
1753 |
}
|
|
1754 |
|
|
1755 |
/********************************************
|
|
1756 |
* Returns:
|
|
1757 |
* true if there are no overloads of this function
|
|
1758 |
*/
|
|
1759 |
final bool isUnique() const |
|
1760 |
{
|
|
1761 | 1 |
bool result = false; |
1762 | 1 |
overloadApply(cast() this, (Dsymbol s) |
1763 |
{
|
|
1764 | 1 |
auto f = s.isFuncDeclaration(); |
1765 | 1 |
if (!f) |
1766 | 1 |
return 0; |
1767 | 1 |
if (result) |
1768 |
{
|
|
1769 | 1 |
result = false; |
1770 | 1 |
return 1; // ambiguous, done |
1771 |
}
|
|
1772 |
else
|
|
1773 |
{
|
|
1774 | 1 |
result = true; |
1775 | 1 |
return 0; |
1776 |
}
|
|
1777 |
});
|
|
1778 | 1 |
return result; |
1779 |
}
|
|
1780 |
|
|
1781 |
/*********************************************
|
|
1782 |
* In the current function, we are calling 'this' function.
|
|
1783 |
* 1. Check to see if the current function can call 'this' function, issue error if not.
|
|
1784 |
* 2. If the current function is not the parent of 'this' function, then add
|
|
1785 |
* the current function to the list of siblings of 'this' function.
|
|
1786 |
* 3. If the current function is a literal, and it's accessing an uplevel scope,
|
|
1787 |
* then mark it as a delegate.
|
|
1788 |
* Returns true if error occurs.
|
|
1789 |
*/
|
|
1790 |
extern (D) final bool checkNestedReference(Scope* sc, const ref Loc loc) |
|
1791 |
{
|
|
1792 |
//printf("FuncDeclaration::checkNestedReference() %s\n", toPrettyChars());
|
|
1793 |
|
|
1794 | 1 |
if (auto fld = this.isFuncLiteralDeclaration()) |
1795 |
{
|
|
1796 | 1 |
if (fld.tok == TOK.reserved) |
1797 |
{
|
|
1798 | 1 |
fld.tok = TOK.function_; |
1799 | 1 |
fld.vthis = null; |
1800 |
}
|
|
1801 |
}
|
|
1802 |
|
|
1803 | 1 |
if (!parent || parent == sc.parent) |
1804 | 1 |
return false; |
1805 | 1 |
if (ident == Id.require || ident == Id.ensure) |
1806 | 1 |
return false; |
1807 | 1 |
if (!isThis() && !isNested()) |
1808 | 1 |
return false; |
1809 |
|
|
1810 |
// The current function
|
|
1811 | 1 |
FuncDeclaration fdthis = sc.parent.isFuncDeclaration(); |
1812 | 1 |
if (!fdthis) |
1813 | 1 |
return false; // out of function scope |
1814 |
|
|
1815 | 1 |
Dsymbol p = toParentLocal(); |
1816 | 1 |
Dsymbol p2 = toParent2(); |
1817 |
|
|
1818 |
// Function literals from fdthis to p must be delegates
|
|
1819 | 1 |
ensureStaticLinkTo(fdthis, p); |
1820 | 1 |
if (p != p2) |
1821 | 1 |
ensureStaticLinkTo(fdthis, p2); |
1822 |
|
|
1823 | 1 |
if (isNested()) |
1824 |
{
|
|
1825 |
// The function that this function is in
|
|
1826 |
bool checkEnclosing(FuncDeclaration fdv) |
|
1827 |
{
|
|
1828 | 1 |
if (!fdv) |
1829 | 1 |
return false; |
1830 | 1 |
if (fdv == fdthis) |
1831 | 1 |
return false; |
1832 |
|
|
1833 |
//printf("this = %s in [%s]\n", this.toChars(), this.loc.toChars());
|
|
1834 |
//printf("fdv = %s in [%s]\n", fdv .toChars(), fdv .loc.toChars());
|
|
1835 |
//printf("fdthis = %s in [%s]\n", fdthis.toChars(), fdthis.loc.toChars());
|
|
1836 |
|
|
1837 |
// Add this function to the list of those which called us
|
|
1838 | 1 |
if (fdthis != this) |
1839 |
{
|
|
1840 | 1 |
bool found = false; |
1841 | 1 |
for (size_t i = 0; i < siblingCallers.dim; ++i) |
1842 |
{
|
|
1843 | 1 |
if (siblingCallers[i] == fdthis) |
1844 | 1 |
found = true; |
1845 |
}
|
|
1846 | 1 |
if (!found) |
1847 |
{
|
|
1848 |
//printf("\tadding sibling %s\n", fdthis.toPrettyChars());
|
|
1849 | 1 |
if (!sc.intypeof && !(sc.flags & SCOPE.compile)) |
1850 | 1 |
siblingCallers.push(fdthis); |
1851 |
}
|
|
1852 |
}
|
|
1853 |
|
|
1854 | 1 |
const lv = fdthis.getLevelAndCheck(loc, sc, fdv); |
1855 | 1 |
if (lv == LevelError) |
1856 | 1 |
return true; // error |
1857 | 1 |
if (lv == -1) |
1858 |
return false; // downlevel call |
|
1859 | 1 |
if (lv == 0) |
1860 | 1 |
return false; // same level call |
1861 |
|
|
1862 | 1 |
return false; // Uplevel call |
1863 |
}
|
|
1864 |
|
|
1865 | 1 |
if (checkEnclosing(p.isFuncDeclaration())) |
1866 | 1 |
return true; |
1867 | 1 |
if (checkEnclosing(p == p2 ? null : p2.isFuncDeclaration())) |
1868 |
return true; |
|
1869 |
}
|
|
1870 | 1 |
return false; |
1871 |
}
|
|
1872 |
|
|
1873 |
/*******************************
|
|
1874 |
* Look at all the variables in this function that are referenced
|
|
1875 |
* by nested functions, and determine if a closure needs to be
|
|
1876 |
* created for them.
|
|
1877 |
*/
|
|
1878 |
final bool needsClosure() |
|
1879 |
{
|
|
1880 |
/* Need a closure for all the closureVars[] if any of the
|
|
1881 |
* closureVars[] are accessed by a
|
|
1882 |
* function that escapes the scope of this function.
|
|
1883 |
* We take the conservative approach and decide that a function needs
|
|
1884 |
* a closure if it:
|
|
1885 |
* 1) is a virtual function
|
|
1886 |
* 2) has its address taken
|
|
1887 |
* 3) has a parent that escapes
|
|
1888 |
* 4) calls another nested function that needs a closure
|
|
1889 |
*
|
|
1890 |
* Note that since a non-virtual function can be called by
|
|
1891 |
* a virtual one, if that non-virtual function accesses a closure
|
|
1892 |
* var, the closure still has to be taken. Hence, we check for isThis()
|
|
1893 |
* instead of isVirtual(). (thanks to David Friedman)
|
|
1894 |
*
|
|
1895 |
* When the function returns a local struct or class, `requiresClosure`
|
|
1896 |
* is already set to `true` upon entering this function when the
|
|
1897 |
* struct/class refers to a local variable and a closure is needed.
|
|
1898 |
*/
|
|
1899 |
|
|
1900 |
//printf("FuncDeclaration::needsClosure() %s\n", toChars());
|
|
1901 |
|
|
1902 | 1 |
if (requiresClosure) |
1903 | 1 |
goto Lyes; |
1904 |
|
|
1905 | 1 |
for (size_t i = 0; i < closureVars.dim; i++) |
1906 |
{
|
|
1907 | 1 |
VarDeclaration v = closureVars[i]; |
1908 |
//printf("\tv = %s\n", v.toChars());
|
|
1909 |
|
|
1910 | 1 |
for (size_t j = 0; j < v.nestedrefs.dim; j++) |
1911 |
{
|
|
1912 | 1 |
FuncDeclaration f = v.nestedrefs[j]; |
1913 | 1 |
assert(f != this); |
1914 |
|
|
1915 |
/* __require and __ensure will always get called directly,
|
|
1916 |
* so they never make outer functions closure.
|
|
1917 |
*/
|
|
1918 | 1 |
if (f.ident == Id.require || f.ident == Id.ensure) |
1919 | 1 |
continue; |
1920 |
|
|
1921 |
//printf("\t\tf = %p, %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", f, f.toChars(), f.isVirtual(), f.isThis(), f.tookAddressOf);
|
|
1922 |
|
|
1923 |
/* Look to see if f escapes. We consider all parents of f within
|
|
1924 |
* this, and also all siblings which call f; if any of them escape,
|
|
1925 |
* so does f.
|
|
1926 |
* Mark all affected functions as requiring closures.
|
|
1927 |
*/
|
|
1928 | 1 |
for (Dsymbol s = f; s && s != this; s = s.toParentP(this)) |
1929 |
{
|
|
1930 | 1 |
FuncDeclaration fx = s.isFuncDeclaration(); |
1931 | 1 |
if (!fx) |
1932 | 1 |
continue; |
1933 | 1 |
if (fx.isThis() || fx.tookAddressOf) |
1934 |
{
|
|
1935 |
//printf("\t\tfx = %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", fx.toChars(), fx.isVirtual(), fx.isThis(), fx.tookAddressOf);
|
|
1936 |
|
|
1937 |
/* Mark as needing closure any functions between this and f
|
|
1938 |
*/
|
|
1939 | 1 |
markAsNeedingClosure((fx == f) ? fx.toParentP(this) : fx, this); |
1940 |
|
|
1941 | 1 |
requiresClosure = true; |
1942 |
}
|
|
1943 |
|
|
1944 |
/* We also need to check if any sibling functions that
|
|
1945 |
* called us, have escaped. This is recursive: we need
|
|
1946 |
* to check the callers of our siblings.
|
|
1947 |
*/
|
|
1948 | 1 |
if (checkEscapingSiblings(fx, this)) |
1949 | 1 |
requiresClosure = true; |
1950 |
|
|
1951 |
/* https://issues.dlang.org/show_bug.cgi?id=12406
|
|
1952 |
* Iterate all closureVars to mark all descendant
|
|
1953 |
* nested functions that access to the closing context of this function.
|
|
1954 |
*/
|
|
1955 |
}
|
|
1956 |
}
|
|
1957 |
}
|
|
1958 | 1 |
if (requiresClosure) |
1959 | 1 |
goto Lyes; |
1960 |
|
|
1961 | 1 |
return false; |
1962 |
|
|
1963 |
Lyes: |
|
1964 |
//printf("\tneeds closure\n");
|
|
1965 | 1 |
return true; |
1966 |
}
|
|
1967 |
|
|
1968 |
/***********************************************
|
|
1969 |
* Check that the function contains any closure.
|
|
1970 |
* If it's @nogc, report suitable errors.
|
|
1971 |
* This is mostly consistent with FuncDeclaration::needsClosure().
|
|
1972 |
*
|
|
1973 |
* Returns:
|
|
1974 |
* true if any errors occur.
|
|
1975 |
*/
|
|
1976 |
extern (D) final bool checkClosure() |
|
1977 |
{
|
|
1978 | 1 |
if (!needsClosure()) |
1979 | 1 |
return false; |
1980 |
|
|
1981 | 1 |
if (setGC()) |
1982 |
{
|
|
1983 | 1 |
error("is `@nogc` yet allocates closures with the GC"); |
1984 | 1 |
if (global.gag) // need not report supplemental errors |
1985 |
return true; |
|
1986 |
}
|
|
1987 |
else
|
|
1988 |
{
|
|
1989 | 1 |
printGCUsage(loc, "using closure causes GC allocation"); |
1990 | 1 |
return false; |
1991 |
}
|
|
1992 |
|
|
1993 | 1 |
FuncDeclarations a; |
1994 | 1 |
foreach (v; closureVars) |
1995 |
{
|
|
1996 | 1 |
foreach (f; v.nestedrefs) |
1997 |
{
|
|
1998 | 1 |
assert(f !is this); |
1999 |
|
|
2000 |
LcheckAncestorsOfANestedRef: |
|
2001 | 1 |
for (Dsymbol s = f; s && s !is this; s = s.toParentP(this)) |
2002 |
{
|
|
2003 | 1 |
auto fx = s.isFuncDeclaration(); |
2004 | 1 |
if (!fx) |
2005 |
continue; |
|
2006 | 1 |
if (fx.isThis() || |
2007 | 1 |
fx.tookAddressOf || |
2008 |
checkEscapingSiblings(fx, this)) |
|
2009 |
{
|
|
2010 | 1 |
foreach (f2; a) |
2011 |
{
|
|
2012 | 1 |
if (f2 == f) |
2013 |
break LcheckAncestorsOfANestedRef; |
|
2014 |
}
|
|
2015 | 1 |
a.push(f); |
2016 | 1 |
.errorSupplemental(f.loc, "%s closes over variable %s at %s", |
2017 |
f.toPrettyChars(), v.toChars(), v.loc.toChars()); |
|
2018 | 1 |
break LcheckAncestorsOfANestedRef; |
2019 |
}
|
|
2020 |
}
|
|
2021 |
}
|
|
2022 |
}
|
|
2023 |
|
|
2024 | 1 |
return true; |
2025 |
}
|
|
2026 |
|
|
2027 |
/***********************************************
|
|
2028 |
* Determine if function's variables are referenced by a function
|
|
2029 |
* nested within it.
|
|
2030 |
*/
|
|
2031 |
final bool hasNestedFrameRefs() |
|
2032 |
{
|
|
2033 | 1 |
if (closureVars.dim) |
2034 | 1 |
return true; |
2035 |
|
|
2036 |
/* If a virtual function has contracts, assume its variables are referenced
|
|
2037 |
* by those contracts, even if they aren't. Because they might be referenced
|
|
2038 |
* by the overridden or overriding function's contracts.
|
|
2039 |
* This can happen because frequire and fensure are implemented as nested functions,
|
|
2040 |
* and they can be called directly by an overriding function and the overriding function's
|
|
2041 |
* context had better match, or
|
|
2042 |
* https://issues.dlang.org/show_bug.cgi?id=7335 will bite.
|
|
2043 |
*/
|
|
2044 | 1 |
if (fdrequire || fdensure) |
2045 | 1 |
return true; |
2046 |
|
|
2047 | 1 |
if (foverrides.dim && isVirtualMethod()) |
2048 |
{
|
|
2049 | 1 |
for (size_t i = 0; i < foverrides.dim; i++) |
2050 |
{
|
|
2051 | 1 |
FuncDeclaration fdv = foverrides[i]; |
2052 | 1 |
if (fdv.hasNestedFrameRefs()) |
2053 | 1 |
return true; |
2054 |
}
|
|
2055 |
}
|
|
2056 | 1 |
return false; |
2057 |
}
|
|
2058 |
|
|
2059 |
/****************************************************
|
|
2060 |
* Check whether result variable can be built.
|
|
2061 |
* Returns:
|
|
2062 |
* `true` if the function has a return type that
|
|
2063 |
* is different from `void`.
|
|
2064 |
*/
|
|
2065 |
extern (D) private bool canBuildResultVar() |
|
2066 |
{
|
|
2067 | 1 |
auto f = cast(TypeFunction)type; |
2068 | 1 |
return f && f.nextOf() && f.nextOf().toBasetype().ty != Tvoid; |
2069 |
}
|
|
2070 |
|
|
2071 |
/****************************************************
|
|
2072 |
* Declare result variable lazily.
|
|
2073 |
*/
|
|
2074 |
extern (D) final void buildResultVar(Scope* sc, Type tret) |
|
2075 |
{
|
|
2076 | 1 |
if (!vresult) |
2077 |
{
|
|
2078 | 1 |
Loc loc = fensure ? fensure.loc : this.loc; |
2079 |
|
|
2080 |
/* If inferRetType is true, tret may not be a correct return type yet.
|
|
2081 |
* So, in here it may be a temporary type for vresult, and after
|
|
2082 |
* fbody.dsymbolSemantic() running, vresult.type might be modified.
|
|
2083 |
*/
|
|
2084 | 1 |
vresult = new VarDeclaration(loc, tret, Id.result, null); |
2085 | 1 |
vresult.storage_class |= STC.nodtor | STC.temp; |
2086 | 1 |
if (!isVirtual()) |
2087 | 1 |
vresult.storage_class |= STC.const_; |
2088 | 1 |
vresult.storage_class |= STC.result; |
2089 |
|
|
2090 |
// set before the semantic() for checkNestedReference()
|
|
2091 | 1 |
vresult.parent = this; |
2092 |
}
|
|
2093 |
|
|
2094 | 1 |
if (sc && vresult.semanticRun == PASS.init) |
2095 |
{
|
|
2096 | 1 |
TypeFunction tf = type.toTypeFunction(); |
2097 | 1 |
if (tf.isref) |
2098 | 1 |
vresult.storage_class |= STC.ref_; |
2099 | 1 |
vresult.type = tret; |
2100 |
|
|
2101 | 1 |
vresult.dsymbolSemantic(sc); |
2102 |
|
|
2103 | 1 |
if (!sc.insert(vresult)) |
2104 |
error("out result %s is already defined", vresult.toChars()); |
|
2105 | 1 |
assert(vresult.parent == this); |
2106 |
}
|
|
2107 |
}
|
|
2108 |
|
|
2109 |
/****************************************************
|
|
2110 |
* Merge into this function the 'in' contracts of all it overrides.
|
|
2111 |
* 'in's are OR'd together, i.e. only one of them needs to pass.
|
|
2112 |
*/
|
|
2113 |
extern (D) final Statement mergeFrequire(Statement sf, Expressions* params) |
|
2114 |
{
|
|
2115 |
/* If a base function and its override both have an IN contract, then
|
|
2116 |
* only one of them needs to succeed. This is done by generating:
|
|
2117 |
*
|
|
2118 |
* void derived.in() {
|
|
2119 |
* try {
|
|
2120 |
* base.in();
|
|
2121 |
* }
|
|
2122 |
* catch () {
|
|
2123 |
* ... body of derived.in() ...
|
|
2124 |
* }
|
|
2125 |
* }
|
|
2126 |
*
|
|
2127 |
* So if base.in() doesn't throw, derived.in() need not be executed, and the contract is valid.
|
|
2128 |
* If base.in() throws, then derived.in()'s body is executed.
|
|
2129 |
*/
|
|
2130 |
|
|
2131 | 1 |
foreach (fdv; foverrides) |
2132 |
{
|
|
2133 |
/* The semantic pass on the contracts of the overridden functions must
|
|
2134 |
* be completed before code generation occurs.
|
|
2135 |
* https://issues.dlang.org/show_bug.cgi?id=3602
|
|
2136 |
*/
|
|
2137 | 1 |
if (fdv.frequires && fdv.semanticRun != PASS.semantic3done) |
2138 |
{
|
|
2139 | 1 |
assert(fdv._scope); |
2140 | 1 |
Scope* sc = fdv._scope.push(); |
2141 | 1 |
sc.stc &= ~STC.override_; |
2142 | 1 |
fdv.semantic3(sc); |
2143 | 1 |
sc.pop(); |
2144 |
}
|
|
2145 |
|
|
2146 | 1 |
sf = fdv.mergeFrequire(sf, params); |
2147 | 1 |
if (sf && fdv.fdrequire) |
2148 |
{
|
|
2149 |
//printf("fdv.frequire: %s\n", fdv.frequire.toChars());
|
|
2150 |
/* Make the call:
|
|
2151 |
* try { __require(params); }
|
|
2152 |
* catch (Throwable) { frequire; }
|
|
2153 |
*/
|
|
2154 | 1 |
params = Expression.arraySyntaxCopy(params); |
2155 | 1 |
Expression e = new CallExp(loc, new VarExp(loc, fdv.fdrequire, false), params); |
2156 | 1 |
Statement s2 = new ExpStatement(loc, e); |
2157 |
|
|
2158 | 1 |
auto c = new Catch(loc, getThrowable(), null, sf); |
2159 | 1 |
c.internalCatch = true; |
2160 | 1 |
auto catches = new Catches(); |
2161 | 1 |
catches.push(c); |
2162 | 1 |
sf = new TryCatchStatement(loc, s2, catches); |
2163 |
}
|
|
2164 |
else
|
|
2165 | 1 |
return null; |
2166 |
}
|
|
2167 | 1 |
return sf; |
2168 |
}
|
|
2169 |
|
|
2170 |
/****************************************************
|
|
2171 |
* Determine whether an 'out' contract is declared inside
|
|
2172 |
* the given function or any of its overrides.
|
|
2173 |
* Params:
|
|
2174 |
* fd = the function to search
|
|
2175 |
* Returns:
|
|
2176 |
* true found an 'out' contract
|
|
2177 |
*/
|
|
2178 |
static bool needsFensure(FuncDeclaration fd) |
|
2179 |
{
|
|
2180 | 1 |
if (fd.fensures) |
2181 | 1 |
return true; |
2182 |
|
|
2183 | 1 |
foreach (fdv; fd.foverrides) |
2184 |
{
|
|
2185 | 1 |
if (needsFensure(fdv)) |
2186 | 1 |
return true; |
2187 |
}
|
|
2188 | 1 |
return false; |
2189 |
}
|
|
2190 |
|
|
2191 |
/****************************************************
|
|
2192 |
* Rewrite contracts as statements.
|
|
2193 |
*/
|
|
2194 |
final void buildEnsureRequire() |
|
2195 |
{
|
|
2196 |
|
|
2197 | 1 |
if (frequires) |
2198 |
{
|
|
2199 |
/* in { statements1... }
|
|
2200 |
* in { statements2... }
|
|
2201 |
* ...
|
|
2202 |
* becomes:
|
|
2203 |
* in { { statements1... } { statements2... } ... }
|
|
2204 |
*/
|
|
2205 | 1 |
assert(frequires.dim); |
2206 | 1 |
auto loc = (*frequires)[0].loc; |
2207 | 1 |
auto s = new Statements; |
2208 | 1 |
foreach (r; *frequires) |
2209 |
{
|
|
2210 | 1 |
s.push(new ScopeStatement(r.loc, r, r.loc)); |
2211 |
}
|
|
2212 | 1 |
frequire = new CompoundStatement(loc, s); |
2213 |
}
|
|
2214 |
|
|
2215 | 1 |
if (fensures) |
2216 |
{
|
|
2217 |
/* out(id1) { statements1... }
|
|
2218 |
* out(id2) { statements2... }
|
|
2219 |
* ...
|
|
2220 |
* becomes:
|
|
2221 |
* out(__result) { { ref id1 = __result; { statements1... } }
|
|
2222 |
* { ref id2 = __result; { statements2... } } ... }
|
|
2223 |
*/
|
|
2224 | 1 |
assert(fensures.dim); |
2225 | 1 |
auto loc = (*fensures)[0].ensure.loc; |
2226 | 1 |
auto s = new Statements; |
2227 | 1 |
foreach (r; *fensures) |
2228 |
{
|
|
2229 | 1 |
if (r.id && canBuildResultVar()) |
2230 |
{
|
|
2231 | 1 |
auto rloc = r.ensure.loc; |
2232 | 1 |
auto resultId = new IdentifierExp(rloc, Id.result); |
2233 | 1 |
auto init = new ExpInitializer(rloc, resultId); |
2234 | 1 |
auto stc = STC.ref_ | STC.temp | STC.result; |
2235 | 1 |
auto decl = new VarDeclaration(rloc, null, r.id, init, stc); |
2236 | 1 |
auto sdecl = new ExpStatement(rloc, decl); |
2237 | 1 |
s.push(new ScopeStatement(rloc, new CompoundStatement(rloc, sdecl, r.ensure), rloc)); |
2238 |
}
|
|
2239 |
else
|
|
2240 |
{
|
|
2241 | 1 |
s.push(r.ensure); |
2242 |
}
|
|
2243 |
}
|
|
2244 | 1 |
fensure = new CompoundStatement(loc, s); |
2245 |
}
|
|
2246 |
|
|
2247 | 1 |
if (!isVirtual()) |
2248 | 1 |
return; |
2249 |
|
|
2250 |
/* Rewrite contracts as nested functions, then call them. Doing it as nested
|
|
2251 |
* functions means that overriding functions can call them.
|
|
2252 |
*/
|
|
2253 | 1 |
TypeFunction f = cast(TypeFunction) type; |
2254 |
|
|
2255 |
/* Make a copy of the parameters and make them all ref */
|
|
2256 |
static Parameters* toRefCopy(ParameterList parameterList) |
|
2257 |
{
|
|
2258 | 1 |
auto result = new Parameters(); |
2259 |
|
|
2260 | 1 |
foreach (n, p; parameterList) |
2261 |
{
|
|
2262 | 1 |
p = p.syntaxCopy(); |
2263 | 1 |
if (!(p.storageClass & STC.lazy_)) |
2264 | 1 |
p.storageClass = (p.storageClass | STC.ref_) & ~STC.out_; |
2265 | 1 |
p.defaultArg = null; // won't be the same with ref |
2266 | 1 |
result.push(p); |
2267 |
}
|
|
2268 |
|
|
2269 | 1 |
return result; |
2270 |
}
|
|
2271 |
|
|
2272 | 1 |
if (frequire) |
2273 |
{
|
|
2274 |
/* in { ... }
|
|
2275 |
* becomes:
|
|
2276 |
* void __require(ref params) { ... }
|
|
2277 |
* __require(params);
|
|
2278 |
*/
|
|
2279 | 1 |
Loc loc = frequire.loc; |
2280 | 1 |
fdrequireParams = new Expressions(); |
2281 | 1 |
if (parameters) |
2282 |
{
|
|
2283 | 1 |
foreach (vd; *parameters) |
2284 | 1 |
fdrequireParams.push(new VarExp(loc, vd)); |
2285 |
}
|
|
2286 | 1 |
auto fo = cast(TypeFunction)(originalType ? originalType : f); |
2287 | 1 |
auto fparams = toRefCopy(fo.parameterList); |
2288 | 1 |
auto tf = new TypeFunction(ParameterList(fparams), Type.tvoid, LINK.d); |
2289 | 1 |
tf.isnothrow = f.isnothrow; |
2290 | 1 |
tf.isnogc = f.isnogc; |
2291 | 1 |
tf.purity = f.purity; |
2292 | 1 |
tf.trust = f.trust; |
2293 | 1 |
auto fd = new FuncDeclaration(loc, loc, Id.require, STC.undefined_, tf); |
2294 | 1 |
fd.fbody = frequire; |
2295 | 1 |
Statement s1 = new ExpStatement(loc, fd); |
2296 | 1 |
Expression e = new CallExp(loc, new VarExp(loc, fd, false), fdrequireParams); |
2297 | 1 |
Statement s2 = new ExpStatement(loc, e); |
2298 | 1 |
frequire = new CompoundStatement(loc, s1, s2); |
2299 | 1 |
fdrequire = fd; |
2300 |
}
|
|
2301 |
|
|
2302 |
/* We need to set fdensureParams here and not in the block below to
|
|
2303 |
* have the parameters available when calling a base class ensure(),
|
|
2304 |
* even if this function doesn't have an out contract.
|
|
2305 |
*/
|
|
2306 | 1 |
fdensureParams = new Expressions(); |
2307 | 1 |
if (canBuildResultVar()) |
2308 | 1 |
fdensureParams.push(new IdentifierExp(loc, Id.result)); |
2309 | 1 |
if (parameters) |
2310 |
{
|
|
2311 | 1 |
foreach (vd; *parameters) |
2312 | 1 |
fdensureParams.push(new VarExp(loc, vd)); |
2313 |
}
|
|
2314 |
|
|
2315 | 1 |
if (fensure) |
2316 |
{
|
|
2317 |
/* out (result) { ... }
|
|
2318 |
* becomes:
|
|
2319 |
* void __ensure(ref tret result, ref params) { ... }
|
|
2320 |
* __ensure(result, params);
|
|
2321 |
*/
|
|
2322 | 1 |
Loc loc = fensure.loc; |
2323 | 1 |
auto fparams = new Parameters(); |
2324 | 1 |
if (canBuildResultVar()) |
2325 |
{
|
|
2326 | 1 |
Parameter p = new Parameter(STC.ref_ | STC.const_, f.nextOf(), Id.result, null, null); |
2327 | 1 |
fparams.push(p); |
2328 |
}
|
|
2329 | 1 |
auto fo = cast(TypeFunction)(originalType ? originalType : f); |
2330 | 1 |
fparams.pushSlice((*toRefCopy(fo.parameterList))[]); |
2331 | 1 |
auto tf = new TypeFunction(ParameterList(fparams), Type.tvoid, LINK.d); |
2332 | 1 |
tf.isnothrow = f.isnothrow; |
2333 | 1 |
tf.isnogc = f.isnogc; |
2334 | 1 |
tf.purity = f.purity; |
2335 | 1 |
tf.trust = f.trust; |
2336 | 1 |
auto fd = new FuncDeclaration(loc, loc, Id.ensure, STC.undefined_, tf); |
2337 | 1 |
fd.fbody = fensure; |
2338 | 1 |
Statement s1 = new ExpStatement(loc, fd); |
2339 | 1 |
Expression e = new CallExp(loc, new VarExp(loc, fd, false), fdensureParams); |
2340 | 1 |
Statement s2 = new ExpStatement(loc, e); |
2341 | 1 |
fensure = new CompoundStatement(loc, s1, s2); |
2342 | 1 |
fdensure = fd; |
2343 |
}
|
|
2344 |
}
|
|
2345 |
|
|
2346 |
/****************************************************
|
|
2347 |
* Merge into this function the 'out' contracts of all it overrides.
|
|
2348 |
* 'out's are AND'd together, i.e. all of them need to pass.
|
|
2349 |
*/
|
|
2350 |
extern (D) final Statement mergeFensure(Statement sf, Identifier oid, Expressions* params) |
|
2351 |
{
|
|
2352 |
/* Same comments as for mergeFrequire(), except that we take care
|
|
2353 |
* of generating a consistent reference to the 'result' local by
|
|
2354 |
* explicitly passing 'result' to the nested function as a reference
|
|
2355 |
* argument.
|
|
2356 |
* This won't work for the 'this' parameter as it would require changing
|
|
2357 |
* the semantic code for the nested function so that it looks on the parameter
|
|
2358 |
* list for the 'this' pointer, something that would need an unknown amount
|
|
2359 |
* of tweaking of various parts of the compiler that I'd rather leave alone.
|
|
2360 |
*/
|
|
2361 | 1 |
foreach (fdv; foverrides) |
2362 |
{
|
|
2363 |
/* The semantic pass on the contracts of the overridden functions must
|
|
2364 |
* be completed before code generation occurs.
|
|
2365 |
* https://issues.dlang.org/show_bug.cgi?id=3602 and
|
|
2366 |
* https://issues.dlang.org/show_bug.cgi?id=5230
|
|
2367 |
*/
|
|
2368 | 1 |
if (needsFensure(fdv) && fdv.semanticRun != PASS.semantic3done) |
2369 |
{
|
|
2370 | 1 |
assert(fdv._scope); |
2371 | 1 |
Scope* sc = fdv._scope.push(); |
2372 | 1 |
sc.stc &= ~STC.override_; |
2373 | 1 |
fdv.semantic3(sc); |
2374 | 1 |
sc.pop(); |
2375 |
}
|
|
2376 |
|
|
2377 | 1 |
sf = fdv.mergeFensure(sf, oid, params); |
2378 | 1 |
if (fdv.fdensure) |
2379 |
{
|
|
2380 |
//printf("fdv.fensure: %s\n", fdv.fensure.toChars());
|
|
2381 |
// Make the call: __ensure(result, params)
|
|
2382 | 1 |
params = Expression.arraySyntaxCopy(params); |
2383 | 1 |
if (canBuildResultVar()) |
2384 |
{
|
|
2385 | 1 |
Type t1 = fdv.type.nextOf().toBasetype(); |
2386 | 1 |
Type t2 = this.type.nextOf().toBasetype(); |
2387 | 1 |
if (t1.isBaseOf(t2, null)) |
2388 |
{
|
|
2389 |
/* Making temporary reference variable is necessary
|
|
2390 |
* in covariant return.
|
|
2391 |
* https://issues.dlang.org/show_bug.cgi?id=5204
|
|
2392 |
* https://issues.dlang.org/show_bug.cgi?id=10479
|
|
2393 |
*/
|
|
2394 | 1 |
Expression* eresult = &(*params)[0]; |
2395 | 1 |
auto ei = new ExpInitializer(Loc.initial, *eresult); |
2396 | 1 |
auto v = new VarDeclaration(Loc.initial, t1, Identifier.generateId("__covres"), ei); |
2397 | 1 |
v.storage_class |= STC.temp; |
2398 | 1 |
auto de = new DeclarationExp(Loc.initial, v); |
2399 | 1 |
auto ve = new VarExp(Loc.initial, v); |
2400 | 1 |
*eresult = new CommaExp(Loc.initial, de, ve); |
2401 |
}
|
|
2402 |
}
|
|
2403 | 1 |
Expression e = new CallExp(loc, new VarExp(loc, fdv.fdensure, false), params); |
2404 | 1 |
Statement s2 = new ExpStatement(loc, e); |
2405 |
|
|
2406 | 1 |
if (sf) |
2407 |
{
|
|
2408 | 1 |
sf = new CompoundStatement(sf.loc, s2, sf); |
2409 |
}
|
|
2410 |
else
|
|
2411 | 1 |
sf = s2; |
2412 |
}
|
|
2413 |
}
|
|
2414 | 1 |
return sf; |
2415 |
}
|
|
2416 |
|
|
2417 |
/*********************************************
|
|
2418 |
* Returns: the function's parameter list, and whether
|
|
2419 |
* it is variadic or not.
|
|
2420 |
*/
|
|
2421 |
final ParameterList getParameterList() |
|
2422 |
{
|
|
2423 | 1 |
if (type) |
2424 |
{
|
|
2425 | 1 |
TypeFunction fdtype = type.isTypeFunction(); |
2426 | 1 |
return fdtype.parameterList; |
2427 |
}
|
|
2428 |
|
|
2429 |
return ParameterList(null, VarArg.none); |
|
2430 |
}
|
|
2431 |
|
|
2432 |
/**********************************
|
|
2433 |
* Generate a FuncDeclaration for a runtime library function.
|
|
2434 |
*/
|
|
2435 |
static FuncDeclaration genCfunc(Parameters* fparams, Type treturn, const(char)* name, StorageClass stc = 0) |
|
2436 |
{
|
|
2437 | 1 |
return genCfunc(fparams, treturn, Identifier.idPool(name, cast(uint)strlen(name)), stc); |
2438 |
}
|
|
2439 |
|
|
2440 |
static FuncDeclaration genCfunc(Parameters* fparams, Type treturn, Identifier id, StorageClass stc = 0) |
|
2441 |
{
|
|
2442 | 1 |
FuncDeclaration fd; |
2443 | 1 |
TypeFunction tf; |
2444 | 1 |
Dsymbol s; |
2445 | 1 |
__gshared DsymbolTable st = null; |
2446 |
|
|
2447 |
//printf("genCfunc(name = '%s')\n", id.toChars());
|
|
2448 |
//printf("treturn\n\t"); treturn.print();
|
|
2449 |
|
|
2450 |
// See if already in table
|
|
2451 | 1 |
if (!st) |
2452 | 1 |
st = new DsymbolTable(); |
2453 | 1 |
s = st.lookup(id); |
2454 | 1 |
if (s) |
2455 |
{
|
|
2456 | 1 |
fd = s.isFuncDeclaration(); |
2457 | 1 |
assert(fd); |
2458 | 1 |
assert(fd.type.nextOf().equals(treturn)); |
2459 |
}
|
|
2460 |
else
|
|
2461 |
{
|
|
2462 | 1 |
tf = new TypeFunction(ParameterList(fparams), treturn, LINK.c, stc); |
2463 | 1 |
fd = new FuncDeclaration(Loc.initial, Loc.initial, id, STC.static_, tf); |
2464 | 1 |
fd.protection = Prot(Prot.Kind.public_); |
2465 | 1 |
fd.linkage = LINK.c; |
2466 |
|
|
2467 | 1 |
st.insert(fd); |
2468 |
}
|
|
2469 | 1 |
return fd; |
2470 |
}
|
|
2471 |
|
|
2472 |
/******************
|
|
2473 |
* Check parameters and return type of D main() function.
|
|
2474 |
* Issue error messages.
|
|
2475 |
*/
|
|
2476 |
extern (D) final void checkDmain() |
|
2477 |
{
|
|
2478 | 1 |
TypeFunction tf = type.toTypeFunction(); |
2479 | 1 |
const nparams = tf.parameterList.length; |
2480 | 1 |
bool argerr; |
2481 | 1 |
if (nparams == 1) |
2482 |
{
|
|
2483 | 1 |
auto fparam0 = tf.parameterList[0]; |
2484 | 1 |
auto t = fparam0.type.toBasetype(); |
2485 | 1 |
if (t.ty != Tarray || |
2486 | 1 |
t.nextOf().ty != Tarray || |
2487 | 1 |
t.nextOf().nextOf().ty != Tchar || |
2488 | 1 |
fparam0.storageClass & (STC.out_ | STC.ref_ | STC.lazy_)) |
2489 |
{
|
|
2490 | 1 |
argerr = true; |
2491 |
}
|
|
2492 |
}
|
|
2493 |
|
|
2494 | 1 |
if (!tf.nextOf()) |
2495 | 1 |
error("must return `int` or `void`"); |
2496 | 1 |
else if (tf.nextOf().ty != Tint32 && tf.nextOf().ty != Tvoid) |
2497 |
error("must return `int` or `void`, not `%s`", tf.nextOf().toChars()); |
|
2498 | 1 |
else if (tf.parameterList.varargs || nparams >= 2 || argerr) |
2499 | 1 |
error("parameters must be `main()` or `main(string[] args)`"); |
2500 |
}
|
|
2501 |
|
|
2502 |
/***********************************************
|
|
2503 |
* Check all return statements for a function to verify that returning
|
|
2504 |
* using NRVO is possible.
|
|
2505 |
*
|
|
2506 |
* Returns:
|
|
2507 |
* true if the result cannot be returned by hidden reference.
|
|
2508 |
*/
|
|
2509 |
final bool checkNrvo() |
|
2510 |
{
|
|
2511 | 1 |
if (!nrvo_can) |
2512 |
return true; |
|
2513 |
|
|
2514 | 1 |
if (returns is null) |
2515 | 1 |
return true; |
2516 |
|
|
2517 | 1 |
auto tf = type.toTypeFunction(); |
2518 | 1 |
if (tf.isref) |
2519 |
return true; |
|
2520 |
|
|
2521 | 1 |
foreach (rs; *returns) |
2522 |
{
|
|
2523 | 1 |
if (auto ve = rs.exp.isVarExp()) |
2524 |
{
|
|
2525 | 1 |
auto v = ve.var.isVarDeclaration(); |
2526 | 1 |
if (!v || v.isOut() || v.isRef()) |
2527 |
return true; |
|
2528 | 1 |
else if (nrvo_var is null) |
2529 |
{
|
|
2530 | 1 |
if (!v.isDataseg() && !v.isParameter() && v.toParent2() == this) |
2531 |
{
|
|
2532 |
//printf("Setting nrvo to %s\n", v.toChars());
|
|
2533 | 1 |
nrvo_var = v; |
2534 |
}
|
|
2535 |
else
|
|
2536 | 1 |
return true; |
2537 |
}
|
|
2538 | 1 |
else if (nrvo_var != v) |
2539 | 1 |
return true; |
2540 |
}
|
|
2541 |
else //if (!exp.isLvalue()) // keep NRVO-ability |
|
2542 | 1 |
return true; |
2543 |
}
|
|
2544 | 1 |
return false; |
2545 |
}
|
|
2546 |
|
|
2547 |
override final inout(FuncDeclaration) isFuncDeclaration() inout |
|
2548 |
{
|
|
2549 | 1 |
return this; |
2550 |
}
|
|
2551 |
|
|
2552 |
inout(FuncDeclaration) toAliasFunc() inout |
|
2553 |
{
|
|
2554 | 1 |
return this; |
2555 |
}
|
|
2556 |
|
|
2557 |
override void accept(Visitor v) |
|
2558 |
{
|
|
2559 | 1 |
v.visit(this); |
2560 |
}
|
|
2561 |
}
|
|
2562 |
|
|
2563 |
/********************************************************
|
|
2564 |
* Generate Expression to call the invariant.
|
|
2565 |
* Input:
|
|
2566 |
* ad aggregate with the invariant
|
|
2567 |
* vthis variable with 'this'
|
|
2568 |
* Returns:
|
|
2569 |
* void expression that calls the invariant
|
|
2570 |
*/
|
|
2571 |
Expression addInvariant(AggregateDeclaration ad, VarDeclaration vthis) |
|
2572 |
{
|
|
2573 | 1 |
Expression e = null; |
2574 |
// Call invariant directly only if it exists
|
|
2575 | 1 |
FuncDeclaration inv = ad.inv; |
2576 | 1 |
ClassDeclaration cd = ad.isClassDeclaration(); |
2577 |
|
|
2578 | 1 |
while (!inv && cd) |
2579 |
{
|
|
2580 | 1 |
cd = cd.baseClass; |
2581 | 1 |
if (!cd) |
2582 | 1 |
break; |
2583 | 1 |
inv = cd.inv; |
2584 |
}
|
|
2585 | 1 |
if (inv) |
2586 |
{
|
|
2587 |
version (all) |
|
2588 |
{
|
|
2589 |
// Workaround for https://issues.dlang.org/show_bug.cgi?id=13394
|
|
2590 |
// For the correct mangling,
|
|
2591 |
// run attribute inference on inv if needed.
|
|
2592 | 1 |
inv.functionSemantic(); |
2593 |
}
|
|
2594 |
|
|
2595 |
//e = new DsymbolExp(Loc.initial, inv);
|
|
2596 |
//e = new CallExp(Loc.initial, e);
|
|
2597 |
//e = e.semantic(sc2);
|
|
2598 |
|
|
2599 |
/* https://issues.dlang.org/show_bug.cgi?id=13113
|
|
2600 |
* Currently virtual invariant calls completely
|
|
2601 |
* bypass attribute enforcement.
|
|
2602 |
* Change the behavior of pre-invariant call by following it.
|
|
2603 |
*/
|
|
2604 | 1 |
e = new ThisExp(Loc.initial); |
2605 | 1 |
e.type = ad.type.addMod(vthis.type.mod); |
2606 | 1 |
e = new DotVarExp(Loc.initial, e, inv, false); |
2607 | 1 |
e.type = inv.type; |
2608 | 1 |
e = new CallExp(Loc.initial, e); |
2609 | 1 |
e.type = Type.tvoid; |
2610 |
}
|
|
2611 | 1 |
return e; |
2612 |
}
|
|
2613 |
|
|
2614 |
/***************************************************
|
|
2615 |
* Visit each overloaded function/template in turn, and call dg(s) on it.
|
|
2616 |
* Exit when no more, or dg(s) returns nonzero.
|
|
2617 |
*
|
|
2618 |
* Params:
|
|
2619 |
* fstart = symbol to start from
|
|
2620 |
* dg = the delegate to be called on the overload
|
|
2621 |
* sc = context used to check if symbol is accessible (and therefore visible),
|
|
2622 |
* can be null
|
|
2623 |
*
|
|
2624 |
* Returns:
|
|
2625 |
* ==0 continue
|
|
2626 |
* !=0 done (and the return value from the last dg() call)
|
|
2627 |
*/
|
|
2628 |
extern (D) int overloadApply(Dsymbol fstart, scope int delegate(Dsymbol) dg, Scope* sc = null) |
|
2629 |
{
|
|
2630 | 1 |
Dsymbol next; |
2631 | 1 |
for (auto d = fstart; d; d = next) |
2632 |
{
|
|
2633 |
import dmd.access : checkSymbolAccess; |
|
2634 | 1 |
if (auto od = d.isOverDeclaration()) |
2635 |
{
|
|
2636 | 1 |
if (od.hasOverloads) |
2637 |
{
|
|
2638 |
/* The scope is needed here to check whether a function in
|
|
2639 |
an overload set was added by means of a private alias (or a
|
|
2640 |
selective import). If the scope where the alias is created
|
|
2641 |
is imported somewhere, the overload set is visible, but the private
|
|
2642 |
alias is not.
|
|
2643 |
*/
|
|
2644 | 1 |
if (sc) |
2645 |
{
|
|
2646 | 1 |
if (checkSymbolAccess(sc, od)) |
2647 |
{
|
|
2648 | 1 |
if (int r = overloadApply(od.aliassym, dg, sc)) |
2649 |
return r; |
|
2650 |
}
|
|
2651 |
}
|
|
2652 | 1 |
else if (int r = overloadApply(od.aliassym, dg, sc)) |
2653 | 1 |
return r; |
2654 |
}
|
|
2655 |
else
|
|
2656 |
{
|
|
2657 |
if (int r = dg(od.aliassym)) |
|
2658 |
return r; |
|
2659 |
}
|
|
2660 | 1 |
next = od.overnext; |
2661 |
}
|
|
2662 | 1 |
else if (auto fa = d.isFuncAliasDeclaration()) |
2663 |
{
|
|
2664 | 1 |
if (fa.hasOverloads) |
2665 |
{
|
|
2666 | 1 |
if (int r = overloadApply(fa.funcalias, dg, sc)) |
2667 | 1 |
return r; |
2668 |
}
|
|
2669 | 1 |
else if (auto fd = fa.toAliasFunc()) |
2670 |
{
|
|
2671 | 1 |
if (int r = dg(fd)) |
2672 | 1 |
return r; |
2673 |
}
|
|
2674 |
else
|
|
2675 |
{
|
|
2676 |
d.error("is aliased to a function"); |
|
2677 |
break; |
|
2678 |
}
|
|
2679 | 1 |
next = fa.overnext; |
2680 |
}
|
|
2681 | 1 |
else if (auto ad = d.isAliasDeclaration()) |
2682 |
{
|
|
2683 | 1 |
if (sc) |
2684 |
{
|
|
2685 | 1 |
if (checkSymbolAccess(sc, ad)) |
2686 | 1 |
next = ad.toAlias(); |
2687 |
}
|
|
2688 |
else
|
|
2689 | 1 |
next = ad.toAlias(); |
2690 | 1 |
if (next == ad) |
2691 |
break; |
|
2692 | 1 |
if (next == fstart) |
2693 |
break; |
|
2694 |
}
|
|
2695 | 1 |
else if (auto td = d.isTemplateDeclaration()) |
2696 |
{
|
|
2697 | 1 |
if (int r = dg(td)) |
2698 | 1 |
return r; |
2699 | 1 |
next = td.overnext; |
2700 |
}
|
|
2701 | 1 |
else if (auto fd = d.isFuncDeclaration()) |
2702 |
{
|
|
2703 | 1 |
if (int r = dg(fd)) |
2704 | 1 |
return r; |
2705 | 1 |
next = fd.overnext; |
2706 |
}
|
|
2707 |
else
|
|
2708 |
{
|
|
2709 | 1 |
d.error("is aliased to a function"); |
2710 | 1 |
break; |
2711 |
// BUG: should print error message?
|
|
2712 |
}
|
|
2713 |
}
|
|
2714 | 1 |
return 0; |
2715 |
}
|
|
2716 |
|
|
2717 |
/**
|
|
2718 |
Checks for mismatching modifiers between `lhsMod` and `rhsMod` and prints the
|
|
2719 |
mismatching modifiers to `buf`.
|
|
2720 |
|
|
2721 |
The modifiers of the `lhsMod` mismatching the ones with the `rhsMod` are printed, i.e.
|
|
2722 |
lhs(shared) vs. rhs() prints "`shared`", wheras lhs() vs rhs(shared) prints "non-shared".
|
|
2723 |
|
|
2724 |
Params:
|
|
2725 |
buf = output buffer to write to
|
|
2726 |
lhsMod = modifier on the left-hand side
|
|
2727 |
lhsMod = modifier on the right-hand side
|
|
2728 |
|
|
2729 |
Returns:
|
|
2730 |
|
|
2731 |
A tuple with `isMutable` and `isNotShared` set
|
|
2732 |
if the `lhsMod` is missing those modifiers (compared to rhs).
|
|
2733 |
*/
|
|
2734 |
auto MODMatchToBuffer(OutBuffer* buf, ubyte lhsMod, ubyte rhsMod) |
|
2735 |
{
|
|
2736 |
static struct Mismatches |
|
2737 |
{
|
|
2738 |
bool isNotShared; |
|
2739 |
bool isMutable; |
|
2740 |
}
|
|
2741 |
|
|
2742 | 1 |
Mismatches mismatches; |
2743 |
|
|
2744 | 1 |
bool bothMutable = ((lhsMod & rhsMod) == 0); |
2745 | 1 |
bool sharedMismatch = ((lhsMod ^ rhsMod) & MODFlags.shared_) != 0; |
2746 | 1 |
bool sharedMismatchOnly = ((lhsMod ^ rhsMod) == MODFlags.shared_); |
2747 |
|
|
2748 | 1 |
if (lhsMod & MODFlags.shared_) |
2749 | 1 |
buf.writestring("`shared` "); |
2750 | 1 |
else if (sharedMismatch && !(lhsMod & MODFlags.immutable_)) |
2751 |
{
|
|
2752 | 1 |
buf.writestring("non-shared "); |
2753 | 1 |
mismatches.isNotShared = true; |
2754 |
}
|
|
2755 |
|
|
2756 | 1 |
if (bothMutable && sharedMismatchOnly) |
2757 |
{
|
|
2758 |
}
|
|
2759 | 1 |
else if (lhsMod & MODFlags.immutable_) |
2760 | 1 |
buf.writestring("`immutable` "); |
2761 | 1 |
else if (lhsMod & MODFlags.const_) |
2762 | 1 |
buf.writestring("`const` "); |
2763 | 1 |
else if (lhsMod & MODFlags.wild) |
2764 | 1 |
buf.writestring("`inout` "); |
2765 |
else
|
|
2766 |
{
|
|
2767 | 1 |
buf.writestring("mutable "); |
2768 | 1 |
mismatches.isMutable = true; |
2769 |
}
|
|
2770 |
|
|
2771 | 1 |
return mismatches; |
2772 |
}
|
|
2773 |
|
|
2774 |
///
|
|
2775 |
unittest
|
|
2776 |
{
|
|
2777 |
OutBuffer buf; |
|
2778 |
auto mismatches = MODMatchToBuffer(&buf, MODFlags.shared_, 0); |
|
2779 |
assert(buf[] == "`shared` "); |
|
2780 |
assert(!mismatches.isNotShared); |
|
2781 |
|
|
2782 |
buf.setsize(0); |
|
2783 |
mismatches = MODMatchToBuffer(&buf, 0, MODFlags.shared_); |
|
2784 |
assert(buf[] == "non-shared "); |
|
2785 |
assert(mismatches.isNotShared); |
|
2786 |
|
|
2787 |
buf.setsize(0); |
|
2788 |
mismatches = MODMatchToBuffer(&buf, MODFlags.const_, 0); |
|
2789 |
assert(buf[] == "`const` "); |
|
2790 |
assert(!mismatches.isMutable); |
|
2791 |
|
|
2792 |
buf.setsize(0); |
|
2793 |
mismatches = MODMatchToBuffer(&buf, 0, MODFlags.const_); |
|
2794 |
assert(buf[] == "mutable "); |
|
2795 |
assert(mismatches.isMutable); |
|
2796 |
}
|
|
2797 |
|
|
2798 |
private const(char)* prependSpace(const(char)* str) |
|
2799 |
{
|
|
2800 | 1 |
if (!str || !*str) return ""; |
2801 |
|
|
2802 | 1 |
return (" " ~ str.toDString() ~ "\0").ptr; |
2803 |
}
|
|
2804 |
|
|
2805 |
/// Flag used by $(LREF resolveFuncCall).
|
|
2806 |
enum FuncResolveFlag : ubyte |
|
2807 |
{
|
|
2808 |
standard = 0, /// issue error messages, solve the call. |
|
2809 |
quiet = 1, /// do not issue error message on no match, just return `null`. |
|
2810 |
overloadOnly = 2, /// only resolve overloads. |
|
2811 |
}
|
|
2812 |
|
|
2813 |
/*******************************************
|
|
2814 |
* Given a symbol that could be either a FuncDeclaration or
|
|
2815 |
* a function template, resolve it to a function symbol.
|
|
2816 |
* Params:
|
|
2817 |
* loc = instantiation location
|
|
2818 |
* sc = instantiation scope
|
|
2819 |
* s = instantiation symbol
|
|
2820 |
* tiargs = initial list of template arguments
|
|
2821 |
* tthis = if !NULL, the `this` argument type
|
|
2822 |
* fargs = arguments to function
|
|
2823 |
* flags = see $(LREF FuncResolveFlag).
|
|
2824 |
* Returns:
|
|
2825 |
* if match is found, then function symbol, else null
|
|
2826 |
*/
|
|
2827 |
FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, |
|
2828 |
Objects* tiargs, Type tthis, Expressions* fargs, FuncResolveFlag flags) |
|
2829 |
{
|
|
2830 | 1 |
if (!s) |
2831 |
return null; // no match |
|
2832 |
|
|
2833 |
version (none) |
|
2834 |
{
|
|
2835 |
printf("resolveFuncCall('%s')\n", s.toChars()); |
|
2836 |
if (tthis) |
|
2837 |
printf("\tthis: %s\n", tthis.toChars()); |
|
2838 |
if (fargs) |
|
2839 |
{
|
|
2840 |
for (size_t i = 0; i < fargs.dim; i++) |
|
2841 |
{
|
|
2842 |
Expression arg = (*fargs)[i]; |
|
2843 |
assert(arg.type); |
|
2844 |
printf("\t%s: ", arg.toChars()); |
|
2845 |
arg.type.print(); |
|
2846 |
}
|
|
2847 |
}
|
|
2848 |
}
|
|
2849 |
|
|
2850 | 1 |
if (tiargs && arrayObjectIsError(tiargs) || |
2851 | 1 |
fargs && arrayObjectIsError(cast(Objects*)fargs)) |
2852 |
{
|
|
2853 | 1 |
return null; |
2854 |
}
|
|
2855 |
|
|
2856 | 1 |
MatchAccumulator m; |
2857 | 1 |
functionResolve(m, s, loc, sc, tiargs, tthis, fargs, null); |
2858 | 1 |
auto orig_s = s; |
2859 |
|
|
2860 | 1 |
if (m.last > MATCH.nomatch && m.lastf) |
2861 |
{
|
|
2862 | 1 |
if (m.count == 1) // exactly one match |
2863 |
{
|
|
2864 | 1 |
if (!(flags & FuncResolveFlag.quiet)) |
2865 | 1 |
m.lastf.functionSemantic(); |
2866 | 1 |
return m.lastf; |
2867 |
}
|
|
2868 | 1 |
if ((flags & FuncResolveFlag.overloadOnly) && !tthis && m.lastf.needThis()) |
2869 |
{
|
|
2870 | 1 |
return m.lastf; |
2871 |
}
|
|
2872 |
}
|
|
2873 |
|
|
2874 |
/* Failed to find a best match.
|
|
2875 |
* Do nothing or print error.
|
|
2876 |
*/
|
|
2877 | 1 |
if (m.last <= MATCH.nomatch) |
2878 |
{
|
|
2879 |
// error was caused on matched function, not on the matching itself,
|
|
2880 |
// so return the function to produce a better diagnostic
|
|
2881 | 1 |
if (m.count == 1) |
2882 | 1 |
return m.lastf; |
2883 |
}
|
|
2884 |
|
|
2885 |
// We are done at this point, as the rest of this function generate
|
|
2886 |
// a diagnostic on invalid match
|
|
2887 | 1 |
if (flags & FuncResolveFlag.quiet) |
2888 | 1 |
return null; |
2889 |
|
|
2890 | 1 |
auto fd = s.isFuncDeclaration(); |
2891 | 1 |
auto od = s.isOverDeclaration(); |
2892 | 1 |
auto td = s.isTemplateDeclaration(); |
2893 | 1 |
if (td && td.funcroot) |
2894 | 1 |
s = fd = td.funcroot; |
2895 |
|
|
2896 | 1 |
OutBuffer tiargsBuf; |
2897 | 1 |
arrayObjectsToBuffer(&tiargsBuf, tiargs); |
2898 |
|
|
2899 | 1 |
OutBuffer fargsBuf; |
2900 | 1 |
fargsBuf.writeByte('('); |
2901 | 1 |
argExpTypesToCBuffer(&fargsBuf, fargs); |
2902 | 1 |
fargsBuf.writeByte(')'); |
2903 | 1 |
if (tthis) |
2904 | 1 |
tthis.modToBuffer(&fargsBuf); |
2905 |
|
|
2906 |
// The call is ambiguous
|
|
2907 | 1 |
if (m.lastf && m.nextf) |
2908 |
{
|
|
2909 | 1 |
TypeFunction tf1 = m.lastf.type.toTypeFunction(); |
2910 | 1 |
TypeFunction tf2 = m.nextf.type.toTypeFunction(); |
2911 | 1 |
const(char)* lastprms = parametersTypeToChars(tf1.parameterList); |
2912 | 1 |
const(char)* nextprms = parametersTypeToChars(tf2.parameterList); |
2913 |
|
|
2914 | 1 |
const(char)* mod1 = prependSpace(MODtoChars(tf1.mod)); |
2915 | 1 |
const(char)* mod2 = prependSpace(MODtoChars(tf2.mod)); |
2916 |
|
|
2917 | 1 |
.error(loc, "`%s.%s` called with argument types `%s` matches both:\n%s: `%s%s%s`\nand:\n%s: `%s%s%s`", |
2918 |
s.parent.toPrettyChars(), s.ident.toChars(), |
|
2919 |
fargsBuf.peekChars(), |
|
2920 |
m.lastf.loc.toChars(), m.lastf.toPrettyChars(), lastprms, mod1, |
|
2921 |
m.nextf.loc.toChars(), m.nextf.toPrettyChars(), nextprms, mod2); |
|
2922 | 1 |
return null; |
2923 |
}
|
|
2924 |
|
|
2925 |
// no match, generate an error messages
|
|
2926 | 1 |
if (!fd) |
2927 |
{
|
|
2928 |
// all of overloads are templates
|
|
2929 | 1 |
if (td) |
2930 |
{
|
|
2931 | 1 |
.error(loc, "%s `%s.%s` cannot deduce function from argument types `!(%s)%s`, candidates are:", |
2932 |
td.kind(), td.parent.toPrettyChars(), td.ident.toChars(), |
|
2933 |
tiargsBuf.peekChars(), fargsBuf.peekChars()); |
|
2934 |
|
|
2935 | 1 |
printCandidates(loc, td, sc.isDeprecated()); |
2936 | 1 |
return null; |
2937 |
}
|
|
2938 |
/* This case happens when several ctors are mixed in an agregate.
|
|
2939 |
A (bad) error message is already generated in overloadApply().
|
|
2940 |
see https://issues.dlang.org/show_bug.cgi?id=19729
|
|
2941 |
*/
|
|
2942 | 1 |
if (!od) |
2943 | 1 |
return null; |
2944 |
}
|
|
2945 |
|
|
2946 | 1 |
if (od) |
2947 |
{
|
|
2948 | 1 |
.error(loc, "none of the overloads of `%s` are callable using argument types `!(%s)%s`", |
2949 |
od.ident.toChars(), tiargsBuf.peekChars(), fargsBuf.peekChars()); |
|
2950 | 1 |
return null; |
2951 |
}
|
|
2952 |
|
|
2953 |
// remove when deprecation period of class allocators and deallocators is over
|
|
2954 | 1 |
if (fd.isNewDeclaration() && fd.checkDisabled(loc, sc)) |
2955 | 1 |
return null; |
2956 |
|
|
2957 | 1 |
bool hasOverloads = fd.overnext !is null; |
2958 | 1 |
auto tf = fd.type.toTypeFunction(); |
2959 | 1 |
if (tthis && !MODimplicitConv(tthis.mod, tf.mod)) // modifier mismatch |
2960 |
{
|
|
2961 | 1 |
OutBuffer thisBuf, funcBuf; |
2962 | 1 |
MODMatchToBuffer(&thisBuf, tthis.mod, tf.mod); |
2963 | 1 |
auto mismatches = MODMatchToBuffer(&funcBuf, tf.mod, tthis.mod); |
2964 | 1 |
if (hasOverloads) |
2965 |
{
|
|
2966 | 1 |
.error(loc, "none of the overloads of `%s` are callable using a %sobject, candidates are:", |
2967 |
fd.ident.toChars(), thisBuf.peekChars()); |
|
2968 | 1 |
printCandidates(loc, fd, sc.isDeprecated()); |
2969 | 1 |
return null; |
2970 |
}
|
|
2971 |
|
|
2972 | 1 |
const(char)* failMessage; |
2973 | 1 |
functionResolve(m, orig_s, loc, sc, tiargs, tthis, fargs, &failMessage); |
2974 | 1 |
if (failMessage) |
2975 |
{
|
|
2976 | 1 |
.error(loc, "%s `%s%s%s` is not callable using argument types `%s`", |
2977 |
fd.kind(), fd.toPrettyChars(), parametersTypeToChars(tf.parameterList), |
|
2978 |
tf.modToChars(), fargsBuf.peekChars()); |
|
2979 | 1 |
errorSupplemental(loc, failMessage); |
2980 | 1 |
return null; |
2981 |
}
|
|
2982 |
|
|
2983 | 1 |
auto fullFdPretty = fd.toPrettyChars(); |
2984 | 1 |
.error(loc, "%smethod `%s` is not callable using a %sobject", |
2985 |
funcBuf.peekChars(), fullFdPretty, thisBuf.peekChars()); |
|
2986 |
|
|
2987 | 1 |
if (mismatches.isNotShared) |
2988 | 1 |
.errorSupplemental(loc, "Consider adding `shared` to %s", fullFdPretty); |
2989 | 1 |
else if (mismatches.isMutable) |
2990 | 1 |
.errorSupplemental(loc, "Consider adding `const` or `inout` to %s", fullFdPretty); |
2991 | 1 |
return null; |
2992 |
}
|
|
2993 |
|
|
2994 |
//printf("tf = %s, args = %s\n", tf.deco, (*fargs)[0].type.deco);
|
|
2995 | 1 |
if (hasOverloads) |
2996 |
{
|
|
2997 | 1 |
.error(loc, "none of the overloads of `%s` are callable using argument types `%s`, candidates are:", |
2998 |
fd.toChars(), fargsBuf.peekChars()); |
|
2999 | 1 |
printCandidates(loc, fd, sc.isDeprecated()); |
3000 | 1 |
return null; |
3001 |
}
|
|
3002 |
|
|
3003 | 1 |
.error(loc, "%s `%s%s%s` is not callable using argument types `%s`", |
3004 |
fd.kind(), fd.toPrettyChars(), parametersTypeToChars(tf.parameterList), |
|
3005 |
tf.modToChars(), fargsBuf.peekChars()); |
|
3006 |
// re-resolve to check for supplemental message
|
|
3007 | 1 |
const(char)* failMessage; |
3008 | 1 |
functionResolve(m, orig_s, loc, sc, tiargs, tthis, fargs, &failMessage); |
3009 | 1 |
if (failMessage) |
3010 | 1 |
errorSupplemental(loc, failMessage); |
3011 | 1 |
return null; |
3012 |
}
|
|
3013 |
|
|
3014 |
/*******************************************
|
|
3015 |
* Prints template and function overload candidates as supplemental errors.
|
|
3016 |
* Params:
|
|
3017 |
* loc = instantiation location
|
|
3018 |
* declaration = the declaration to print overload candidates for
|
|
3019 |
* showDeprecated = If `false`, `deprecated` function won't be shown
|
|
3020 |
*/
|
|
3021 |
private void printCandidates(Decl)(const ref Loc loc, Decl declaration, bool showDeprecated) |
|
3022 |
if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration)) |
|
3023 |
{
|
|
3024 |
// max num of overloads to print (-v overrides this).
|
|
3025 |
enum int DisplayLimit = 5; |
|
3026 | 1 |
int displayed; |
3027 | 1 |
const(char)* constraintsTip; |
3028 |
|
|
3029 | 1 |
overloadApply(declaration, (Dsymbol s) |
3030 |
{
|
|
3031 | 1 |
Dsymbol nextOverload; |
3032 |
|
|
3033 | 1 |
if (auto fd = s.isFuncDeclaration()) |
3034 |
{
|
|
3035 |
// Don't print overloads which have errors.
|
|
3036 |
// Not that if the whole overload set has errors, we'll never reach
|
|
3037 |
// this point so there's no risk of printing no candidate
|
|
3038 | 1 |
if (fd.errors || fd.type.ty == Terror) |
3039 | 1 |
return 0; |
3040 |
// Don't print disabled functions, or `deprecated` outside of deprecated scope
|
|
3041 | 1 |
if (fd.storage_class & STC.disable || (fd.isDeprecated() && !showDeprecated)) |
3042 | 1 |
return 0; |
3043 |
|
|
3044 | 1 |
auto tf = cast(TypeFunction) fd.type; |
3045 | 1 |
.errorSupplemental(fd.loc, "`%s%s`", fd.toPrettyChars(), |
3046 |
parametersTypeToChars(tf.parameterList)); |
|
3047 | 1 |
nextOverload = fd.overnext; |
3048 |
}
|
|
3049 | 1 |
else if (auto td = s.isTemplateDeclaration()) |
3050 |
{
|
|
3051 |
import dmd.staticcond; |
|
3052 |
|
|
3053 | 1 |
const tmsg = td.toCharsNoConstraints(); |
3054 | 1 |
const cmsg = td.getConstraintEvalError(constraintsTip); |
3055 | 1 |
if (cmsg) |
3056 | 1 |
.errorSupplemental(td.loc, "`%s`\n%s", tmsg, cmsg); |
3057 |
else
|
|
3058 | 1 |
.errorSupplemental(td.loc, "`%s`", tmsg); |
3059 | 1 |
nextOverload = td.overnext; |
3060 |
}
|
|
3061 |
|
|
3062 | 1 |
if (global.params.verbose || ++displayed < DisplayLimit) |
3063 | 1 |
return 0; |
3064 |
|
|
3065 |
// Too many overloads to sensibly display.
|
|
3066 |
// Just show count of remaining overloads.
|
|
3067 | 1 |
int num = 0; |
3068 | 1 |
overloadApply(nextOverload, (s) { ++num; return 0; }); |
3069 |
|
|
3070 | 1 |
if (num > 0) |
3071 | 1 |
.errorSupplemental(loc, "... (%d more, -v to show) ...", num); |
3072 | 1 |
return 1; // stop iterating |
3073 |
});
|
|
3074 |
|
|
3075 |
// Nothing was displayed, all overloads are either disabled or deprecated
|
|
3076 | 1 |
if (!displayed) |
3077 | 1 |
.errorSupplemental(loc, "All possible candidates are marked as `deprecated` or `@disable`"); |
3078 |
// should be only in verbose mode
|
|
3079 | 1 |
if (constraintsTip) |
3080 | 1 |
.tip(constraintsTip); |
3081 |
}
|
|
3082 |
|
|
3083 |
/**************************************
|
|
3084 |
* Returns an indirect type one step from t.
|
|
3085 |
*/
|
|
3086 |
Type getIndirection(Type t) |
|
3087 |
{
|
|
3088 | 1 |
t = t.baseElemOf(); |
3089 | 1 |
if (t.ty == Tarray || t.ty == Tpointer) |
3090 | 1 |
return t.nextOf().toBasetype(); |
3091 | 1 |
if (t.ty == Taarray || t.ty == Tclass) |
3092 | 1 |
return t; |
3093 | 1 |
if (t.ty == Tstruct) |
3094 | 1 |
return t.hasPointers() ? t : null; // TODO |
3095 |
|
|
3096 |
// should consider TypeDelegate?
|
|
3097 |
return null; |
|
3098 |
}
|
|
3099 |
|
|
3100 |
/**************************************
|
|
3101 |
* Performs type-based alias analysis between a newly created value and a pre-
|
|
3102 |
* existing memory reference:
|
|
3103 | <