]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/d/dmd/cond.c
d: Merge upstream dmd 47ed0330f
[thirdparty/gcc.git] / gcc / d / dmd / cond.c
1
2 /* Compiler implementation of the D programming language
3 * Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
4 * written by Walter Bright
5 * http://www.digitalmars.com
6 * Distributed under the Boost Software License, Version 1.0.
7 * http://www.boost.org/LICENSE_1_0.txt
8 * https://github.com/D-Programming-Language/dmd/blob/master/src/cond.c
9 */
10
11 #include "root/dsystem.h" // strcmp()
12
13 #include "mars.h"
14 #include "id.h"
15 #include "init.h"
16 #include "aggregate.h"
17 #include "declaration.h"
18 #include "identifier.h"
19 #include "expression.h"
20 #include "cond.h"
21 #include "module.h"
22 #include "template.h"
23 #include "mtype.h"
24 #include "scope.h"
25 #include "statement.h"
26 #include "arraytypes.h"
27 #include "tokens.h"
28
29 Expression *semantic(Expression *e, Scope *sc);
30 bool evalStaticCondition(Scope *sc, Expression *exp, Expression *e, bool &errors);
31
32 int findCondition(Strings *ids, Identifier *ident)
33 {
34 if (ids)
35 {
36 for (size_t i = 0; i < ids->dim; i++)
37 {
38 const char *id = (*ids)[i];
39
40 if (strcmp(id, ident->toChars()) == 0)
41 return true;
42 }
43 }
44
45 return false;
46 }
47
48 /* ============================================================ */
49
50 Condition::Condition(Loc loc)
51 {
52 this->loc = loc;
53 inc = 0;
54 }
55
56 /* ============================================================ */
57
58 StaticForeach::StaticForeach(Loc loc, ForeachStatement *aggrfe, ForeachRangeStatement *rangefe)
59 {
60 assert(!!aggrfe ^ !!rangefe);
61 this->loc = loc;
62 this->aggrfe = aggrfe;
63 this->rangefe = rangefe;
64 this->needExpansion = false;
65 }
66
67 StaticForeach *StaticForeach::syntaxCopy()
68 {
69 return new StaticForeach(
70 loc,
71 aggrfe ? (ForeachStatement *)aggrfe->syntaxCopy() : NULL,
72 rangefe ? (ForeachRangeStatement *)rangefe->syntaxCopy() : NULL
73 );
74 }
75
76 /*****************************************
77 * Turn an aggregate which is an array into an expression tuple
78 * of its elements. I.e., lower
79 * static foreach (x; [1, 2, 3, 4]) { ... }
80 * to
81 * static foreach (x; AliasSeq!(1, 2, 3, 4)) { ... }
82 */
83
84 static void lowerArrayAggregate(StaticForeach *sfe, Scope *sc)
85 {
86 Expression *aggr = sfe->aggrfe->aggr;
87 Expression *el = new ArrayLengthExp(aggr->loc, aggr);
88 sc = sc->startCTFE();
89 el = semantic(el, sc);
90 sc = sc->endCTFE();
91 el = el->optimize(WANTvalue);
92 el = el->ctfeInterpret();
93 if (el->op == TOKint64)
94 {
95 dinteger_t length = el->toInteger();
96 Expressions *es = new Expressions();
97 for (size_t i = 0; i < length; i++)
98 {
99 IntegerExp *index = new IntegerExp(sfe->loc, i, Type::tsize_t);
100 Expression *value = new IndexExp(aggr->loc, aggr, index);
101 es->push(value);
102 }
103 sfe->aggrfe->aggr = new TupleExp(aggr->loc, es);
104 sfe->aggrfe->aggr = semantic(sfe->aggrfe->aggr, sc);
105 sfe->aggrfe->aggr = sfe->aggrfe->aggr->optimize(WANTvalue);
106 }
107 else
108 {
109 sfe->aggrfe->aggr = new ErrorExp();
110 }
111 }
112
113 /*****************************************
114 * Wrap a statement into a function literal and call it.
115 *
116 * Params:
117 * loc = The source location.
118 * s = The statement.
119 * Returns:
120 * AST of the expression `(){ s; }()` with location loc.
121 */
122
123 static Expression *wrapAndCall(Loc loc, Statement *s)
124 {
125 TypeFunction *tf = new TypeFunction(new Parameters(), NULL, 0, LINKdefault, 0);
126 FuncLiteralDeclaration *fd = new FuncLiteralDeclaration(loc, loc, tf, TOKreserved, NULL);
127 fd->fbody = s;
128 FuncExp *fe = new FuncExp(loc, fd);
129 Expression *ce = new CallExp(loc, fe, new Expressions());
130 return ce;
131 }
132
133 /*****************************************
134 * Create a `foreach` statement from `aggrefe/rangefe` with given
135 * `foreach` variables and body `s`.
136 *
137 * Params:
138 * loc = The source location.
139 * parameters = The foreach variables.
140 * s = The `foreach` body.
141 * Returns:
142 * `foreach (parameters; aggregate) s;` or
143 * `foreach (parameters; lower .. upper) s;`
144 * Where aggregate/lower, upper are as for the current StaticForeach.
145 */
146
147 static Statement *createForeach(StaticForeach *sfe, Loc loc, Parameters *parameters, Statement *s)
148 {
149 if (sfe->aggrfe)
150 {
151 return new ForeachStatement(loc, sfe->aggrfe->op, parameters, sfe->aggrfe->aggr->syntaxCopy(), s, loc);
152 }
153 else
154 {
155 assert(sfe->rangefe && parameters->dim == 1);
156 return new ForeachRangeStatement(loc, sfe->rangefe->op, (*parameters)[0],
157 sfe->rangefe->lwr->syntaxCopy(),
158 sfe->rangefe->upr->syntaxCopy(), s, loc);
159 }
160 }
161
162 /*****************************************
163 * For a `static foreach` with multiple loop variables, the
164 * aggregate is lowered to an array of tuples. As D does not have
165 * built-in tuples, we need a suitable tuple type. This generates
166 * a `struct` that serves as the tuple type. This type is only
167 * used during CTFE and hence its typeinfo will not go to the
168 * object file.
169 *
170 * Params:
171 * loc = The source location.
172 * e = The expressions we wish to store in the tuple.
173 * sc = The current scope.
174 * Returns:
175 * A struct type of the form
176 * struct Tuple
177 * {
178 * typeof(AliasSeq!(e)) tuple;
179 * }
180 */
181
182 static TypeStruct *createTupleType(Loc loc, Expressions *e)
183 { // TODO: move to druntime?
184 Identifier *sid = Identifier::generateId("Tuple");
185 StructDeclaration *sdecl = new StructDeclaration(loc, sid, false);
186 sdecl->storage_class |= STCstatic;
187 sdecl->members = new Dsymbols();
188 Identifier *fid = Identifier::idPool("tuple");
189 Type *ty = new TypeTypeof(loc, new TupleExp(loc, e));
190 sdecl->members->push(new VarDeclaration(loc, ty, fid, NULL));
191 TypeStruct *r = (TypeStruct *)sdecl->type;
192 r->vtinfo = TypeInfoStructDeclaration::create(r); // prevent typeinfo from going to object file
193 return r;
194 }
195
196 /*****************************************
197 * Create the AST for an instantiation of a suitable tuple type.
198 *
199 * Params:
200 * loc = The source location.
201 * type = A Tuple type, created with createTupleType.
202 * e = The expressions we wish to store in the tuple.
203 * Returns:
204 * An AST for the expression `Tuple(e)`.
205 */
206
207 static Expression *createTuple(Loc loc, TypeStruct *type, Expressions *e)
208 { // TODO: move to druntime?
209 return new CallExp(loc, new TypeExp(loc, type), e);
210 }
211
212 /*****************************************
213 * Lower any aggregate that is not an array to an array using a
214 * regular foreach loop within CTFE. If there are multiple
215 * `static foreach` loop variables, an array of tuples is
216 * generated. In thise case, the field `needExpansion` is set to
217 * true to indicate that the static foreach loop expansion will
218 * need to expand the tuples into multiple variables.
219 *
220 * For example, `static foreach (x; range) { ... }` is lowered to:
221 *
222 * static foreach (x; {
223 * typeof({
224 * foreach (x; range) return x;
225 * }())[] __res;
226 * foreach (x; range) __res ~= x;
227 * return __res;
228 * }()) { ... }
229 *
230 * Finally, call `lowerArrayAggregate` to turn the produced
231 * array into an expression tuple.
232 *
233 * Params:
234 * sc = The current scope.
235 */
236
237 static void lowerNonArrayAggregate(StaticForeach *sfe, Scope *sc)
238 {
239 size_t nvars = sfe->aggrfe ? sfe->aggrfe->parameters->dim : 1;
240 Loc aloc = sfe->aggrfe ? sfe->aggrfe->aggr->loc : sfe->rangefe->lwr->loc;
241 // We need three sets of foreach loop variables because the
242 // lowering contains three foreach loops.
243 Parameters *pparams[3] = {new Parameters(), new Parameters(), new Parameters()};
244 for (size_t i = 0; i < nvars; i++)
245 {
246 for (size_t j = 0; j < 3; j++)
247 {
248 Parameters *params = pparams[j];
249 Parameter *p = sfe->aggrfe ? (*sfe->aggrfe->parameters)[i] : sfe->rangefe->prm;
250 params->push(new Parameter(p->storageClass, p->type, p->ident, NULL));
251 }
252 }
253 Expression *res[2];
254 TypeStruct *tplty = NULL;
255 if (nvars == 1) // only one `static foreach` variable, generate identifiers.
256 {
257 for (size_t i = 0; i < 2; i++)
258 {
259 res[i] = new IdentifierExp(aloc, (*pparams[i])[0]->ident);
260 }
261 }
262 else // multiple `static foreach` variables, generate tuples.
263 {
264 for (size_t i = 0; i < 2; i++)
265 {
266 Expressions *e = new Expressions();
267 for (size_t j = 0; j < pparams[0]->dim; j++)
268 {
269 Parameter *p = (*pparams[i])[j];
270 e->push(new IdentifierExp(aloc, p->ident));
271 }
272 if (!tplty)
273 {
274 tplty = createTupleType(aloc, e);
275 }
276 res[i] = createTuple(aloc, tplty, e);
277 }
278 sfe->needExpansion = true; // need to expand the tuples later
279 }
280 // generate remaining code for the new aggregate which is an
281 // array (see documentation comment).
282 if (sfe->rangefe)
283 {
284 sc = sc->startCTFE();
285 sfe->rangefe->lwr = semantic(sfe->rangefe->lwr, sc);
286 sfe->rangefe->lwr = resolveProperties(sc, sfe->rangefe->lwr);
287 sfe->rangefe->upr = semantic(sfe->rangefe->upr, sc);
288 sfe->rangefe->upr = resolveProperties(sc, sfe->rangefe->upr);
289 sc = sc->endCTFE();
290 sfe->rangefe->lwr = sfe->rangefe->lwr->optimize(WANTvalue);
291 sfe->rangefe->lwr = sfe->rangefe->lwr->ctfeInterpret();
292 sfe->rangefe->upr = sfe->rangefe->upr->optimize(WANTvalue);
293 sfe->rangefe->upr = sfe->rangefe->upr->ctfeInterpret();
294 }
295 Statements *s1 = new Statements();
296 Statements *sfebody = new Statements();
297 if (tplty) sfebody->push(new ExpStatement(sfe->loc, tplty->sym));
298 sfebody->push(new ReturnStatement(aloc, res[0]));
299 s1->push(createForeach(sfe, aloc, pparams[0], new CompoundStatement(aloc, sfebody)));
300 s1->push(new ExpStatement(aloc, new AssertExp(aloc, new IntegerExp(aloc, 0, Type::tint32))));
301 Type *ety = new TypeTypeof(aloc, wrapAndCall(aloc, new CompoundStatement(aloc, s1)));
302 Type *aty = ety->arrayOf();
303 Identifier *idres = Identifier::generateId("__res");
304 VarDeclaration *vard = new VarDeclaration(aloc, aty, idres, NULL);
305 Statements *s2 = new Statements();
306 s2->push(new ExpStatement(aloc, vard));
307 Expression *catass = new CatAssignExp(aloc, new IdentifierExp(aloc, idres), res[1]);
308 s2->push(createForeach(sfe, aloc, pparams[1], new ExpStatement(aloc, catass)));
309 s2->push(new ReturnStatement(aloc, new IdentifierExp(aloc, idres)));
310 Expression *aggr = wrapAndCall(aloc, new CompoundStatement(aloc, s2));
311 sc = sc->startCTFE();
312 aggr = semantic(aggr, sc);
313 aggr = resolveProperties(sc, aggr);
314 sc = sc->endCTFE();
315 aggr = aggr->optimize(WANTvalue);
316 aggr = aggr->ctfeInterpret();
317
318 assert(!!sfe->aggrfe ^ !!sfe->rangefe);
319 sfe->aggrfe = new ForeachStatement(sfe->loc, TOKforeach, pparams[2], aggr,
320 sfe->aggrfe ? sfe->aggrfe->_body : sfe->rangefe->_body,
321 sfe->aggrfe ? sfe->aggrfe->endloc : sfe->rangefe->endloc);
322 sfe->rangefe = NULL;
323 lowerArrayAggregate(sfe, sc); // finally, turn generated array into expression tuple
324 }
325
326 /*****************************************
327 * Perform `static foreach` lowerings that are necessary in order
328 * to finally expand the `static foreach` using
329 * `ddmd.statementsem.makeTupleForeach`.
330 */
331
332 void staticForeachPrepare(StaticForeach *sfe, Scope *sc)
333 {
334 assert(sc);
335 if (sfe->aggrfe)
336 {
337 sc = sc->startCTFE();
338 sfe->aggrfe->aggr = semantic(sfe->aggrfe->aggr, sc);
339 sc = sc->endCTFE();
340 sfe->aggrfe->aggr = sfe->aggrfe->aggr->optimize(WANTvalue);
341 Type *tab = sfe->aggrfe->aggr->type->toBasetype();
342 if (tab->ty != Ttuple)
343 {
344 sfe->aggrfe->aggr = sfe->aggrfe->aggr->ctfeInterpret();
345 }
346 }
347
348 if (sfe->aggrfe && sfe->aggrfe->aggr->type->toBasetype()->ty == Terror)
349 {
350 return;
351 }
352
353 if (!staticForeachReady(sfe))
354 {
355 if (sfe->aggrfe && sfe->aggrfe->aggr->type->toBasetype()->ty == Tarray)
356 {
357 lowerArrayAggregate(sfe, sc);
358 }
359 else
360 {
361 lowerNonArrayAggregate(sfe, sc);
362 }
363 }
364 }
365
366 /*****************************************
367 * Returns:
368 * `true` iff ready to call `ddmd.statementsem.makeTupleForeach`.
369 */
370
371 bool staticForeachReady(StaticForeach *sfe)
372 {
373 return sfe->aggrfe && sfe->aggrfe->aggr && sfe->aggrfe->aggr->type &&
374 sfe->aggrfe->aggr->type->toBasetype()->ty == Ttuple;
375 }
376
377 /* ============================================================ */
378
379 DVCondition::DVCondition(Module *mod, unsigned level, Identifier *ident)
380 : Condition(Loc())
381 {
382 this->mod = mod;
383 this->level = level;
384 this->ident = ident;
385 }
386
387 Condition *DVCondition::syntaxCopy()
388 {
389 return this; // don't need to copy
390 }
391
392 /* ============================================================ */
393
394 void DebugCondition::setGlobalLevel(unsigned level)
395 {
396 global.params.debuglevel = level;
397 }
398
399 void DebugCondition::addGlobalIdent(const char *ident)
400 {
401 if (!global.params.debugids)
402 global.params.debugids = new Strings();
403 global.params.debugids->push(ident);
404 }
405
406
407 DebugCondition::DebugCondition(Module *mod, unsigned level, Identifier *ident)
408 : DVCondition(mod, level, ident)
409 {
410 }
411
412 // Helper for printing dependency information
413 void printDepsConditional(Scope *sc, DVCondition* condition, const char* depType)
414 {
415 if (!global.params.moduleDeps || global.params.moduleDepsFile)
416 return;
417 OutBuffer *ob = global.params.moduleDeps;
418 Module* imod = sc ? sc->instantiatingModule() : condition->mod;
419 if (!imod)
420 return;
421 ob->writestring(depType);
422 ob->writestring(imod->toPrettyChars());
423 ob->writestring(" (");
424 escapePath(ob, imod->srcfile->toChars());
425 ob->writestring(") : ");
426 if (condition->ident)
427 ob->printf("%s\n", condition->ident->toChars());
428 else
429 ob->printf("%d\n", condition->level);
430 }
431
432
433 int DebugCondition::include(Scope *sc, ScopeDsymbol *)
434 {
435 //printf("DebugCondition::include() level = %d, debuglevel = %d\n", level, global.params.debuglevel);
436 if (inc == 0)
437 {
438 inc = 2;
439 bool definedInModule = false;
440 if (ident)
441 {
442 if (findCondition(mod->debugids, ident))
443 {
444 inc = 1;
445 definedInModule = true;
446 }
447 else if (findCondition(global.params.debugids, ident))
448 inc = 1;
449 else
450 { if (!mod->debugidsNot)
451 mod->debugidsNot = new Strings();
452 mod->debugidsNot->push(ident->toChars());
453 }
454 }
455 else if (level <= global.params.debuglevel || level <= mod->debuglevel)
456 inc = 1;
457 if (!definedInModule)
458 printDepsConditional(sc, this, "depsDebug ");
459 }
460 return (inc == 1);
461 }
462
463 /* ============================================================ */
464
465 void VersionCondition::setGlobalLevel(unsigned level)
466 {
467 global.params.versionlevel = level;
468 }
469
470 static bool isReserved(const char *ident)
471 {
472 static const char* reserved[] =
473 {
474 "DigitalMars",
475 "GNU",
476 "LDC",
477 "SDC",
478 "Windows",
479 "Win32",
480 "Win64",
481 "linux",
482 "OSX",
483 "FreeBSD",
484 "OpenBSD",
485 "NetBSD",
486 "DragonFlyBSD",
487 "BSD",
488 "Solaris",
489 "Posix",
490 "AIX",
491 "Haiku",
492 "SkyOS",
493 "SysV3",
494 "SysV4",
495 "Hurd",
496 "Android",
497 "PlayStation",
498 "PlayStation4",
499 "Cygwin",
500 "MinGW",
501 "FreeStanding",
502 "X86",
503 "X86_64",
504 "ARM",
505 "ARM_Thumb",
506 "ARM_SoftFloat",
507 "ARM_SoftFP",
508 "ARM_HardFloat",
509 "AArch64",
510 "Epiphany",
511 "PPC",
512 "PPC_SoftFloat",
513 "PPC_HardFloat",
514 "PPC64",
515 "IA64",
516 "MIPS32",
517 "MIPS64",
518 "MIPS_O32",
519 "MIPS_N32",
520 "MIPS_O64",
521 "MIPS_N64",
522 "MIPS_EABI",
523 "MIPS_SoftFloat",
524 "MIPS_HardFloat",
525 "MSP430",
526 "NVPTX",
527 "NVPTX64",
528 "RISCV32",
529 "RISCV64",
530 "SPARC",
531 "SPARC_V8Plus",
532 "SPARC_SoftFloat",
533 "SPARC_HardFloat",
534 "SPARC64",
535 "S390",
536 "S390X",
537 "HPPA",
538 "HPPA64",
539 "SH",
540 "Alpha",
541 "Alpha_SoftFloat",
542 "Alpha_HardFloat",
543 "LittleEndian",
544 "BigEndian",
545 "ELFv1",
546 "ELFv2",
547 "CRuntime_Digitalmars",
548 "CRuntime_Glibc",
549 "CRuntime_Microsoft",
550 "CRuntime_Musl",
551 "CRuntime_UClibc",
552 "CppRuntime_Clang",
553 "CppRuntime_DigitalMars",
554 "CppRuntime_Gcc",
555 "CppRuntime_Microsoft",
556 "CppRuntime_Sun",
557 "D_Coverage",
558 "D_Ddoc",
559 "D_InlineAsm_X86",
560 "D_InlineAsm_X86_64",
561 "D_LP64",
562 "D_X32",
563 "D_HardFloat",
564 "D_SoftFloat",
565 "D_PIC",
566 "D_SIMD",
567 "D_Version2",
568 "D_NoBoundsChecks",
569 "unittest",
570 "assert",
571 "all",
572 "none",
573 NULL
574 };
575
576 for (unsigned i = 0; reserved[i]; i++)
577 {
578 if (strcmp(ident, reserved[i]) == 0)
579 return true;
580 }
581
582 if (ident[0] == 'D' && ident[1] == '_')
583 return true;
584 return false;
585 }
586
587 void checkReserved(Loc loc, const char *ident)
588 {
589 if (isReserved(ident))
590 error(loc, "version identifier '%s' is reserved and cannot be set", ident);
591 }
592
593 void VersionCondition::addGlobalIdent(const char *ident)
594 {
595 checkReserved(Loc(), ident);
596 addPredefinedGlobalIdent(ident);
597 }
598
599 void VersionCondition::addPredefinedGlobalIdent(const char *ident)
600 {
601 if (!global.params.versionids)
602 global.params.versionids = new Strings();
603 global.params.versionids->push(ident);
604 }
605
606
607 VersionCondition::VersionCondition(Module *mod, unsigned level, Identifier *ident)
608 : DVCondition(mod, level, ident)
609 {
610 }
611
612 int VersionCondition::include(Scope *sc, ScopeDsymbol *)
613 {
614 //printf("VersionCondition::include() level = %d, versionlevel = %d\n", level, global.params.versionlevel);
615 //if (ident) printf("\tident = '%s'\n", ident->toChars());
616 if (inc == 0)
617 {
618 inc = 2;
619 bool definedInModule=false;
620 if (ident)
621 {
622 if (findCondition(mod->versionids, ident))
623 {
624 inc = 1;
625 definedInModule = true;
626 }
627 else if (findCondition(global.params.versionids, ident))
628 inc = 1;
629 else
630 {
631 if (!mod->versionidsNot)
632 mod->versionidsNot = new Strings();
633 mod->versionidsNot->push(ident->toChars());
634 }
635 }
636 else if (level <= global.params.versionlevel || level <= mod->versionlevel)
637 inc = 1;
638 if (!definedInModule && (!ident || (!isReserved(ident->toChars()) && ident != Id::_unittest && ident != Id::_assert)))
639 printDepsConditional(sc, this, "depsVersion ");
640 }
641 return (inc == 1);
642 }
643
644 /**************************** StaticIfCondition *******************************/
645
646 StaticIfCondition::StaticIfCondition(Loc loc, Expression *exp)
647 : Condition(loc)
648 {
649 this->exp = exp;
650 }
651
652 Condition *StaticIfCondition::syntaxCopy()
653 {
654 return new StaticIfCondition(loc, exp->syntaxCopy());
655 }
656
657 int StaticIfCondition::include(Scope *sc, ScopeDsymbol *sds)
658 {
659 if (inc == 0)
660 {
661 if (!sc)
662 {
663 error(loc, "static if conditional cannot be at global scope");
664 inc = 2;
665 return 0;
666 }
667
668 sc = sc->push(sc->scopesym);
669 sc->sds = sds; // sds gets any addMember()
670
671 bool errors = false;
672 bool result = evalStaticCondition(sc, exp, exp, errors);
673 sc->pop();
674
675 // Prevent repeated condition evaluation.
676 // See: fail_compilation/fail7815.d
677 if (inc != 0)
678 return (inc == 1);
679 if (errors)
680 goto Lerror;
681 if (result)
682 inc = 1;
683 else
684 inc = 2;
685 }
686 return (inc == 1);
687
688 Lerror:
689 if (!global.gag)
690 inc = 2; // so we don't see the error message again
691 return 0;
692 }