]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/d/dmd/staticcond.d
Merge dmd, druntime ceff48bf7d, phobos dcbfbd43a
[thirdparty/gcc.git] / gcc / d / dmd / staticcond.d
1 /**
2 * Lazily evaluate static conditions for `static if`, `static assert` and template constraints.
3 *
4 * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved
5 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
6 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/staticcond.d, _staticcond.d)
8 * Documentation: https://dlang.org/phobos/dmd_staticcond.html
9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/staticcond.d
10 */
11
12 module dmd.staticcond;
13
14 import dmd.expression;
15 import dmd.root.array;
16 import dmd.common.outbuffer;
17 import dmd.tokens;
18
19
20
21
22 /********************************************
23 * Format a static condition as a tree-like structure, marking failed and
24 * bypassed expressions.
25 * Params:
26 * original = original expression
27 * instantiated = instantiated expression
28 * negatives = array with negative clauses from `instantiated` expression
29 * full = controls whether it shows the full output or only failed parts
30 * itemCount = returns the number of written clauses
31 * Returns:
32 * formatted string or `null` if the expressions were `null`, or if the
33 * instantiated expression is not based on the original one
34 */
35 const(char)* visualizeStaticCondition(Expression original, Expression instantiated,
36 const Expression[] negatives, bool full, ref uint itemCount)
37 {
38 if (!original || !instantiated || original.loc !is instantiated.loc)
39 return null;
40
41 OutBuffer buf;
42
43 if (full)
44 itemCount = visualizeFull(original, instantiated, negatives, buf);
45 else
46 itemCount = visualizeShort(original, instantiated, negatives, buf);
47
48 return buf.extractChars();
49 }
50
51 private uint visualizeFull(Expression original, Expression instantiated,
52 const Expression[] negatives, ref OutBuffer buf)
53 {
54 // tree-like structure; traverse and format simultaneously
55 uint count;
56 uint indent;
57
58 static void printOr(uint indent, ref OutBuffer buf)
59 {
60 buf.reserve(indent * 4 + 8);
61 foreach (i; 0 .. indent)
62 buf.writestring(" ");
63 buf.writestring(" or:\n");
64 }
65
66 // returns true if satisfied
67 bool impl(Expression orig, Expression e, bool inverted, bool orOperand, bool unreached)
68 {
69 EXP op = orig.op;
70
71 // lower all 'not' to the bottom
72 // !(A && B) -> !A || !B
73 // !(A || B) -> !A && !B
74 if (inverted)
75 {
76 if (op == EXP.andAnd)
77 op = EXP.orOr;
78 else if (op == EXP.orOr)
79 op = EXP.andAnd;
80 }
81
82 if (op == EXP.not)
83 {
84 NotExp no = cast(NotExp)orig;
85 NotExp ne = cast(NotExp)e;
86 assert(ne);
87 return impl(no.e1, ne.e1, !inverted, orOperand, unreached);
88 }
89 else if (op == EXP.andAnd)
90 {
91 BinExp bo = cast(BinExp)orig;
92 BinExp be = cast(BinExp)e;
93 assert(be);
94 const r1 = impl(bo.e1, be.e1, inverted, false, unreached);
95 const r2 = impl(bo.e2, be.e2, inverted, false, unreached || !r1);
96 return r1 && r2;
97 }
98 else if (op == EXP.orOr)
99 {
100 if (!orOperand) // do not indent A || B || C twice
101 indent++;
102 BinExp bo = cast(BinExp)orig;
103 BinExp be = cast(BinExp)e;
104 assert(be);
105 const r1 = impl(bo.e1, be.e1, inverted, true, unreached);
106 printOr(indent, buf);
107 const r2 = impl(bo.e2, be.e2, inverted, true, unreached);
108 if (!orOperand)
109 indent--;
110 return r1 || r2;
111 }
112 else if (op == EXP.question)
113 {
114 CondExp co = cast(CondExp)orig;
115 CondExp ce = cast(CondExp)e;
116 assert(ce);
117 if (!inverted)
118 {
119 // rewrite (A ? B : C) as (A && B || !A && C)
120 if (!orOperand)
121 indent++;
122 const r1 = impl(co.econd, ce.econd, inverted, false, unreached);
123 const r2 = impl(co.e1, ce.e1, inverted, false, unreached || !r1);
124 printOr(indent, buf);
125 const r3 = impl(co.econd, ce.econd, !inverted, false, unreached);
126 const r4 = impl(co.e2, ce.e2, inverted, false, unreached || !r3);
127 if (!orOperand)
128 indent--;
129 return r1 && r2 || r3 && r4;
130 }
131 else
132 {
133 // rewrite !(A ? B : C) as (!A || !B) && (A || !C)
134 if (!orOperand)
135 indent++;
136 const r1 = impl(co.econd, ce.econd, inverted, false, unreached);
137 printOr(indent, buf);
138 const r2 = impl(co.e1, ce.e1, inverted, false, unreached);
139 const r12 = r1 || r2;
140 const r3 = impl(co.econd, ce.econd, !inverted, false, unreached || !r12);
141 printOr(indent, buf);
142 const r4 = impl(co.e2, ce.e2, inverted, false, unreached || !r12);
143 if (!orOperand)
144 indent--;
145 return (r1 || r2) && (r3 || r4);
146 }
147 }
148 else // 'primitive' expression
149 {
150 buf.reserve(indent * 4 + 4);
151 foreach (i; 0 .. indent)
152 buf.writestring(" ");
153
154 // find its value; it may be not computed, if there was a short circuit,
155 // but we handle this case with `unreached` flag
156 bool value = true;
157 if (!unreached)
158 {
159 foreach (fe; negatives)
160 {
161 if (fe is e)
162 {
163 value = false;
164 break;
165 }
166 }
167 }
168 // write the marks first
169 const satisfied = inverted ? !value : value;
170 if (!satisfied && !unreached)
171 buf.writestring(" > ");
172 else if (unreached)
173 buf.writestring(" - ");
174 else
175 buf.writestring(" ");
176 // then the expression itself
177 if (inverted)
178 buf.writeByte('!');
179 buf.writestring(orig.toChars);
180 buf.writenl();
181 count++;
182 return satisfied;
183 }
184 }
185
186 impl(original, instantiated, false, true, false);
187 return count;
188 }
189
190 private uint visualizeShort(Expression original, Expression instantiated,
191 const Expression[] negatives, ref OutBuffer buf)
192 {
193 // simple list; somewhat similar to long version, so no comments
194 // one difference is that it needs to hold items to display in a stack
195
196 static struct Item
197 {
198 Expression orig;
199 bool inverted;
200 }
201
202 Array!Item stack;
203
204 bool impl(Expression orig, Expression e, bool inverted)
205 {
206 EXP op = orig.op;
207
208 if (inverted)
209 {
210 if (op == EXP.andAnd)
211 op = EXP.orOr;
212 else if (op == EXP.orOr)
213 op = EXP.andAnd;
214 }
215
216 if (op == EXP.not)
217 {
218 NotExp no = cast(NotExp)orig;
219 NotExp ne = cast(NotExp)e;
220 assert(ne);
221 return impl(no.e1, ne.e1, !inverted);
222 }
223 else if (op == EXP.andAnd)
224 {
225 BinExp bo = cast(BinExp)orig;
226 BinExp be = cast(BinExp)e;
227 assert(be);
228 bool r = impl(bo.e1, be.e1, inverted);
229 r = r && impl(bo.e2, be.e2, inverted);
230 return r;
231 }
232 else if (op == EXP.orOr)
233 {
234 BinExp bo = cast(BinExp)orig;
235 BinExp be = cast(BinExp)e;
236 assert(be);
237 const lbefore = stack.length;
238 bool r = impl(bo.e1, be.e1, inverted);
239 r = r || impl(bo.e2, be.e2, inverted);
240 if (r)
241 stack.setDim(lbefore); // purge added positive items
242 return r;
243 }
244 else if (op == EXP.question)
245 {
246 CondExp co = cast(CondExp)orig;
247 CondExp ce = cast(CondExp)e;
248 assert(ce);
249 if (!inverted)
250 {
251 const lbefore = stack.length;
252 bool a = impl(co.econd, ce.econd, inverted);
253 a = a && impl(co.e1, ce.e1, inverted);
254 bool b;
255 if (!a)
256 {
257 b = impl(co.econd, ce.econd, !inverted);
258 b = b && impl(co.e2, ce.e2, inverted);
259 }
260 const r = a || b;
261 if (r)
262 stack.setDim(lbefore);
263 return r;
264 }
265 else
266 {
267 bool a;
268 {
269 const lbefore = stack.length;
270 a = impl(co.econd, ce.econd, inverted);
271 a = a || impl(co.e1, ce.e1, inverted);
272 if (a)
273 stack.setDim(lbefore);
274 }
275 bool b;
276 if (a)
277 {
278 const lbefore = stack.length;
279 b = impl(co.econd, ce.econd, !inverted);
280 b = b || impl(co.e2, ce.e2, inverted);
281 if (b)
282 stack.setDim(lbefore);
283 }
284 return a && b;
285 }
286 }
287 else // 'primitive' expression
288 {
289 bool value = true;
290 foreach (fe; negatives)
291 {
292 if (fe is e)
293 {
294 value = false;
295 break;
296 }
297 }
298 const satisfied = inverted ? !value : value;
299 if (!satisfied)
300 stack.push(Item(orig, inverted));
301 return satisfied;
302 }
303 }
304
305 impl(original, instantiated, false);
306
307 foreach (i; 0 .. stack.length)
308 {
309 // write the expression only
310 buf.writestring(" ");
311 if (stack[i].inverted)
312 buf.writeByte('!');
313 buf.writestring(stack[i].orig.toChars);
314 // here with no trailing newline
315 if (i + 1 < stack.length)
316 buf.writenl();
317 }
318 return cast(uint)stack.length;
319 }