1
2 /* Compiler implementation of the D programming language
3 * Copyright (C) 1999-2019 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 "declaration.h"
17 #include "identifier.h"
18 #include "expression.h"
19 #include "cond.h"
20 #include "module.h"
21 #include "template.h"
22 #include "mtype.h"
23 #include "scope.h"
24 #include "arraytypes.h"
25 #include "tokens.h"
26
27 Expression *semantic(Expression *e, Scope *sc);
28 bool evalStaticCondition(Scope *sc, Expression *exp, Expression *e, bool &errors);
29
findCondition(Strings * ids,Identifier * ident)30 int findCondition(Strings *ids, Identifier *ident)
31 {
32 if (ids)
33 {
34 for (size_t i = 0; i < ids->dim; i++)
35 {
36 const char *id = (*ids)[i];
37
38 if (strcmp(id, ident->toChars()) == 0)
39 return true;
40 }
41 }
42
43 return false;
44 }
45
46 /* ============================================================ */
47
Condition(Loc loc)48 Condition::Condition(Loc loc)
49 {
50 this->loc = loc;
51 inc = 0;
52 }
53
54 /* ============================================================ */
55
DVCondition(Module * mod,unsigned level,Identifier * ident)56 DVCondition::DVCondition(Module *mod, unsigned level, Identifier *ident)
57 : Condition(Loc())
58 {
59 this->mod = mod;
60 this->level = level;
61 this->ident = ident;
62 }
63
syntaxCopy()64 Condition *DVCondition::syntaxCopy()
65 {
66 return this; // don't need to copy
67 }
68
69 /* ============================================================ */
70
setGlobalLevel(unsigned level)71 void DebugCondition::setGlobalLevel(unsigned level)
72 {
73 global.params.debuglevel = level;
74 }
75
addGlobalIdent(const char * ident)76 void DebugCondition::addGlobalIdent(const char *ident)
77 {
78 if (!global.params.debugids)
79 global.params.debugids = new Strings();
80 global.params.debugids->push(ident);
81 }
82
83
DebugCondition(Module * mod,unsigned level,Identifier * ident)84 DebugCondition::DebugCondition(Module *mod, unsigned level, Identifier *ident)
85 : DVCondition(mod, level, ident)
86 {
87 }
88
89 // Helper for printing dependency information
printDepsConditional(Scope * sc,DVCondition * condition,const char * depType)90 void printDepsConditional(Scope *sc, DVCondition* condition, const char* depType)
91 {
92 if (!global.params.moduleDeps || global.params.moduleDepsFile)
93 return;
94 OutBuffer *ob = global.params.moduleDeps;
95 Module* imod = sc ? sc->instantiatingModule() : condition->mod;
96 if (!imod)
97 return;
98 ob->writestring(depType);
99 ob->writestring(imod->toPrettyChars());
100 ob->writestring(" (");
101 escapePath(ob, imod->srcfile->toChars());
102 ob->writestring(") : ");
103 if (condition->ident)
104 ob->printf("%s\n", condition->ident->toChars());
105 else
106 ob->printf("%d\n", condition->level);
107 }
108
109
include(Scope * sc,ScopeDsymbol *)110 int DebugCondition::include(Scope *sc, ScopeDsymbol *)
111 {
112 //printf("DebugCondition::include() level = %d, debuglevel = %d\n", level, global.params.debuglevel);
113 if (inc == 0)
114 {
115 inc = 2;
116 bool definedInModule = false;
117 if (ident)
118 {
119 if (findCondition(mod->debugids, ident))
120 {
121 inc = 1;
122 definedInModule = true;
123 }
124 else if (findCondition(global.params.debugids, ident))
125 inc = 1;
126 else
127 { if (!mod->debugidsNot)
128 mod->debugidsNot = new Strings();
129 mod->debugidsNot->push(ident->toChars());
130 }
131 }
132 else if (level <= global.params.debuglevel || level <= mod->debuglevel)
133 inc = 1;
134 if (!definedInModule)
135 printDepsConditional(sc, this, "depsDebug ");
136 }
137 return (inc == 1);
138 }
139
140 /* ============================================================ */
141
setGlobalLevel(unsigned level)142 void VersionCondition::setGlobalLevel(unsigned level)
143 {
144 global.params.versionlevel = level;
145 }
146
isReserved(const char * ident)147 static bool isReserved(const char *ident)
148 {
149 static const char* reserved[] =
150 {
151 "DigitalMars",
152 "GNU",
153 "LDC",
154 "SDC",
155 "Windows",
156 "Win32",
157 "Win64",
158 "linux",
159 "OSX",
160 "FreeBSD",
161 "OpenBSD",
162 "NetBSD",
163 "DragonFlyBSD",
164 "BSD",
165 "Solaris",
166 "Posix",
167 "AIX",
168 "Haiku",
169 "SkyOS",
170 "SysV3",
171 "SysV4",
172 "Hurd",
173 "Android",
174 "PlayStation",
175 "PlayStation4",
176 "Cygwin",
177 "MinGW",
178 "FreeStanding",
179 "X86",
180 "X86_64",
181 "ARM",
182 "ARM_Thumb",
183 "ARM_SoftFloat",
184 "ARM_SoftFP",
185 "ARM_HardFloat",
186 "AArch64",
187 "Epiphany",
188 "PPC",
189 "PPC_SoftFloat",
190 "PPC_HardFloat",
191 "PPC64",
192 "IA64",
193 "MIPS32",
194 "MIPS64",
195 "MIPS_O32",
196 "MIPS_N32",
197 "MIPS_O64",
198 "MIPS_N64",
199 "MIPS_EABI",
200 "MIPS_SoftFloat",
201 "MIPS_HardFloat",
202 "MSP430",
203 "NVPTX",
204 "NVPTX64",
205 "RISCV32",
206 "RISCV64",
207 "SPARC",
208 "SPARC_V8Plus",
209 "SPARC_SoftFloat",
210 "SPARC_HardFloat",
211 "SPARC64",
212 "S390",
213 "S390X",
214 "HPPA",
215 "HPPA64",
216 "SH",
217 "Alpha",
218 "Alpha_SoftFloat",
219 "Alpha_HardFloat",
220 "LittleEndian",
221 "BigEndian",
222 "ELFv1",
223 "ELFv2",
224 "CRuntime_Digitalmars",
225 "CRuntime_Glibc",
226 "CRuntime_Microsoft",
227 "CRuntime_Musl",
228 "CRuntime_UClibc",
229 "CppRuntime_Clang",
230 "CppRuntime_DigitalMars",
231 "CppRuntime_Gcc",
232 "CppRuntime_Microsoft",
233 "CppRuntime_Sun",
234 "D_Coverage",
235 "D_Ddoc",
236 "D_InlineAsm_X86",
237 "D_InlineAsm_X86_64",
238 "D_LP64",
239 "D_X32",
240 "D_HardFloat",
241 "D_SoftFloat",
242 "D_PIC",
243 "D_SIMD",
244 "D_Version2",
245 "D_NoBoundsChecks",
246 "unittest",
247 "assert",
248 "all",
249 "none",
250 NULL
251 };
252
253 for (unsigned i = 0; reserved[i]; i++)
254 {
255 if (strcmp(ident, reserved[i]) == 0)
256 return true;
257 }
258
259 if (ident[0] == 'D' && ident[1] == '_')
260 return true;
261 return false;
262 }
263
checkReserved(Loc loc,const char * ident)264 void checkReserved(Loc loc, const char *ident)
265 {
266 if (isReserved(ident))
267 error(loc, "version identifier '%s' is reserved and cannot be set", ident);
268 }
269
addGlobalIdent(const char * ident)270 void VersionCondition::addGlobalIdent(const char *ident)
271 {
272 checkReserved(Loc(), ident);
273 addPredefinedGlobalIdent(ident);
274 }
275
addPredefinedGlobalIdent(const char * ident)276 void VersionCondition::addPredefinedGlobalIdent(const char *ident)
277 {
278 if (!global.params.versionids)
279 global.params.versionids = new Strings();
280 global.params.versionids->push(ident);
281 }
282
283
VersionCondition(Module * mod,unsigned level,Identifier * ident)284 VersionCondition::VersionCondition(Module *mod, unsigned level, Identifier *ident)
285 : DVCondition(mod, level, ident)
286 {
287 }
288
include(Scope * sc,ScopeDsymbol *)289 int VersionCondition::include(Scope *sc, ScopeDsymbol *)
290 {
291 //printf("VersionCondition::include() level = %d, versionlevel = %d\n", level, global.params.versionlevel);
292 //if (ident) printf("\tident = '%s'\n", ident->toChars());
293 if (inc == 0)
294 {
295 inc = 2;
296 bool definedInModule=false;
297 if (ident)
298 {
299 if (findCondition(mod->versionids, ident))
300 {
301 inc = 1;
302 definedInModule = true;
303 }
304 else if (findCondition(global.params.versionids, ident))
305 inc = 1;
306 else
307 {
308 if (!mod->versionidsNot)
309 mod->versionidsNot = new Strings();
310 mod->versionidsNot->push(ident->toChars());
311 }
312 }
313 else if (level <= global.params.versionlevel || level <= mod->versionlevel)
314 inc = 1;
315 if (!definedInModule && (!ident || (!isReserved(ident->toChars()) && ident != Id::_unittest && ident != Id::_assert)))
316 printDepsConditional(sc, this, "depsVersion ");
317 }
318 return (inc == 1);
319 }
320
321 /**************************** StaticIfCondition *******************************/
322
StaticIfCondition(Loc loc,Expression * exp)323 StaticIfCondition::StaticIfCondition(Loc loc, Expression *exp)
324 : Condition(loc)
325 {
326 this->exp = exp;
327 this->nest = 0;
328 }
329
syntaxCopy()330 Condition *StaticIfCondition::syntaxCopy()
331 {
332 return new StaticIfCondition(loc, exp->syntaxCopy());
333 }
334
include(Scope * sc,ScopeDsymbol * sds)335 int StaticIfCondition::include(Scope *sc, ScopeDsymbol *sds)
336 {
337 if (inc == 0)
338 {
339 if (exp->op == TOKerror || nest > 100)
340 {
341 error(loc, (nest > 1000) ? "unresolvable circular static if expression"
342 : "error evaluating static if expression");
343 goto Lerror;
344 }
345
346 if (!sc)
347 {
348 error(loc, "static if conditional cannot be at global scope");
349 inc = 2;
350 return 0;
351 }
352
353 ++nest;
354 sc = sc->push(sc->scopesym);
355 sc->sds = sds; // sds gets any addMember()
356
357 bool errors = false;
358 bool result = evalStaticCondition(sc, exp, exp, errors);
359 sc->pop();
360 --nest;
361
362 // Prevent repeated condition evaluation.
363 // See: fail_compilation/fail7815.d
364 if (inc != 0)
365 return (inc == 1);
366 if (errors)
367 goto Lerror;
368 if (result)
369 inc = 1;
370 else
371 inc = 2;
372 }
373 return (inc == 1);
374
375 Lerror:
376 if (!global.gag)
377 inc = 2; // so we don't see the error message again
378 return 0;
379 }
380