1 module d.ir.dscope;
2 
3 import d.ir.symbol;
4 
5 import d.ast.conditional;
6 
7 import source.location;
8 import source.name;
9 
10 struct ConditionalBranch {
11 	// XXX: Can't be private because mixin templates are a glorious hack.
12 	// private:
13 	// XXX: Because mixin if such a fucking broken way to go:
14 	static import d.ast.declaration;
15 	alias Declaration = d.ast.declaration.Declaration;
16 
17 	import std.bitmanip;
18 	mixin(taggedClassRef!(
19 		// sdfmt off
20 		StaticIfDeclaration, "sif",
21 		bool, "branch", 1,
22 		// sdfmt on
23 	));
24 
25 public:
26 	this(StaticIfDeclaration sif, bool branch) {
27 		// XXX: Need to set the branch first because of
28 		// https://issues.dlang.org/show_bug.cgi?id=15305
29 		this.branch = branch;
30 		this.sif = sif;
31 	}
32 }
33 
34 /**
35  * Tools to make symbols Scopes.
36  */
37 interface Scope {
38 	Module getModule();
39 	Scope getParentScope();
40 
41 	Module[] getImports();
42 	void addImport(Module m);
43 
44 	Symbol search(Location location, Name name);
45 	Symbol resolve(Location location, Name name);
46 
47 	void addSymbol(Symbol s);
48 	void addOverloadableSymbol(Symbol s);
49 	void addConditionalSymbol(Symbol s, ConditionalBranch[] cdBranches);
50 
51 	void setPoisoningMode();
52 	void clearPoisoningMode();
53 }
54 
55 enum ScopeType {
56 	Module,
57 	WithParent,
58 	Nested,
59 }
60 
61 mixin template ScopeImpl(ScopeType ST = ScopeType.WithParent,
62                          ParentScope = Scope) {
63 private:
64 	import d.ir.symbol;
65 	Module dmodule;
66 	static if (ST) {
67 		ParentScope parentScope;
68 	}
69 
70 	import source.name;
71 	Symbol[Name] symbols;
72 
73 	static if (ST) {
74 		// XXX: Use a proper set :D
75 		bool[Variable] captures;
76 	}
77 
78 	Module[] imports;
79 
80 	bool isPoisoning;
81 	bool isPoisoned;
82 	bool hasConditional;
83 
84 final:
85 	static if (ST) {
86 		void fillParentScope(ParentScope parentScope) {
87 			this.dmodule = parentScope.getModule();
88 			this.parentScope = parentScope;
89 		}
90 	}
91 
92 public:
93 	Module getModule() {
94 		assert(dmodule !is null, "No module");
95 		return dmodule;
96 	}
97 
98 	ParentScope getParentScope() {
99 		static if (ST) {
100 			assert(parentScope !is null);
101 			return parentScope;
102 		} else {
103 			return null;
104 		}
105 	}
106 
107 	static if (ST) {
108 		bool[Variable] getCaptures() {
109 			return captures;
110 		}
111 	}
112 
113 	Module[] getImports() {
114 		return imports;
115 	}
116 
117 	void addImport(Module m) {
118 		imports ~= m;
119 	}
120 
121 	Symbol search(Location location, Name name) {
122 		if (auto s = resolve(location, name)) {
123 			return s;
124 		}
125 
126 		Symbol s = null;
127 		static if (ST) {
128 			s = parentScope.search(location, name);
129 		}
130 
131 		static if (!is(typeof(hasContext))) {
132 			enum hasContext = false;
133 		}
134 
135 		if (s is null || !hasContext) {
136 			return s;
137 		}
138 
139 		static if (ST) {
140 			if (auto v = cast(Variable) s) {
141 				import d.common.qualifier;
142 				if (v.storage.isLocal) {
143 					captures[v] = true;
144 					v.storage = Storage.Capture;
145 				}
146 			}
147 		}
148 
149 		return s;
150 	}
151 
152 	Symbol resolve(Location location, Name name) {
153 		auto sPtr = name in symbols;
154 		if (sPtr is null) {
155 			if (isPoisoning) {
156 				symbols[name] = new Poison(location, name);
157 				isPoisoned = true;
158 			}
159 
160 			return null;
161 		}
162 
163 		auto s = *sPtr;
164 
165 		static if (ST == ScopeType.Nested) {
166 			// For nested scope we unpack and/or mark overloadset as already
167 			// resolved so we make copy of it when adding new overloads.
168 			if (auto os = cast(OverloadSet) s) {
169 				os.isPoisoned = isPoisoning;
170 				if (os.set.length == 1) {
171 					return os.set[0];
172 				}
173 
174 				os.isResolved = true;
175 				return os;
176 			}
177 		}
178 
179 		// If we are poisoning, we need to make sure we poison.
180 		// If not, we can return directly.
181 		if (!isPoisoning) {
182 			return s;
183 		}
184 
185 		static if (ST != ScopeType.Nested) {
186 			// If we have an overloadset, make it poisoned.
187 			if (auto os = cast(OverloadSet) s) {
188 				os.isPoisoned = true;
189 				return s;
190 			}
191 		}
192 
193 		// If we have a poison, then pretend there is nothing.
194 		if (cast(Poison) s) {
195 			return null;
196 		}
197 
198 		// If we have no conditionals, no need to check for them.
199 		if (!hasConditional) {
200 			return s;
201 		}
202 
203 		// If we have a conditional, poison it.
204 		if (auto cs = cast(ConditionalSet) s) {
205 			cs.isPoisoned = true;
206 			return cs.selected;
207 		}
208 
209 		return s;
210 	}
211 
212 	void addSymbol(Symbol s) {
213 		assert(!s.name.isEmpty,
214 		       "Symbol can't be added to scope as it has no name.");
215 
216 		if (auto sPtr = s.name in symbols) {
217 			if (auto p = cast(Poison) *sPtr) {
218 				import source.exception;
219 				throw new CompileException(s.location, "Poisoned");
220 			}
221 
222 			import source.exception;
223 			throw new CompileException(s.location, "Already defined");
224 		}
225 
226 		symbols[s.name] = s;
227 	}
228 
229 	void addOverloadableSymbol(Symbol s) {
230 		if (auto sPtr = s.name in symbols) {
231 			if (auto os = cast(OverloadSet) *sPtr) {
232 				if (os.isPoisoned) {
233 					import source.exception;
234 					throw new CompileException(s.location, "Poisoned");
235 				}
236 
237 				if (ST == ScopeType.Nested && os.isResolved) {
238 					*sPtr = os = os.clone();
239 				}
240 
241 				os.set ~= s;
242 				return;
243 			}
244 		}
245 
246 		addSymbol(new OverloadSet(s.location, s.name, [s]));
247 	}
248 
249 	void addConditionalSymbol(Symbol s, ConditionalBranch[] cdBranches) in {
250 		assert(cdBranches.length > 0, "No conditional branches supplied");
251 	} do {
252 		auto entry = ConditionalEntry(s, cdBranches);
253 		if (auto csPtr = s.name in symbols) {
254 			if (auto cs = cast(ConditionalSet) *csPtr) {
255 				cs.set ~= entry;
256 				return;
257 			}
258 
259 			import source.exception;
260 			throw new CompileException(s.location, "Already defined");
261 		}
262 
263 		symbols[s.name] = new ConditionalSet(s.location, s.name, [entry]);
264 		hasConditional = true;
265 	}
266 
267 	void setPoisoningMode() in {
268 		assert(isPoisoning == false, "poisoning mode is already on.");
269 	} do {
270 		isPoisoning = true;
271 	}
272 
273 	void clearPoisoningMode() in {
274 		assert(isPoisoning == true, "poisoning mode is not on.");
275 	} do {
276 		// XXX: Consider not removing tags on OverloadSet.
277 		// That would allow to not pass over the AA most of the time.
278 		foreach (n; symbols.keys) {
279 			auto s = symbols[n];
280 
281 			// Mark overload set as non poisoned.
282 			// XXX: Why ?!??
283 			if (auto os = cast(OverloadSet) s) {
284 				os.isPoisoned = false;
285 				continue;
286 			}
287 
288 			// Remove poisons.
289 			// XXX: Why ?!!??!
290 			if (isPoisoned && cast(Poison) s) {
291 				symbols.remove(n);
292 				continue;
293 			}
294 
295 			// If we have no conditionals, no need to check for them.
296 			if (!hasConditional) {
297 				continue;
298 			}
299 
300 			// Replace conditional entrie by whatever they resolve to.
301 			if (auto cs = cast(ConditionalSet) s) {
302 				if (cs.set.length) {
303 					import source.exception;
304 					throw new CompileException(cs.set[0].entry.location,
305 					                           "Not resolved");
306 				}
307 
308 				assert(
309 					cs.set.length == 0,
310 					"Conditional symbols remains when clearing poisoning mode.");
311 				if (cs.selected) {
312 					symbols[n] = cs.selected;
313 				} else {
314 					symbols.remove(n);
315 				}
316 			}
317 		}
318 
319 		isPoisoning = false;
320 		isPoisoned = false;
321 		hasConditional = false;
322 	}
323 
324 	// XXX: Use of smarter data structure can probably improve things here :D
325 	import d.ast.conditional : StaticIfDeclaration;
326 	void resolveConditional(StaticIfDeclaration sif, bool branch) in {
327 		assert(isPoisoning,
328 		       "You must be in poisoning mode when resolving static ifs.");
329 	} do {
330 		foreach (s; symbols.values) {
331 			if (auto cs = cast(ConditionalSet) s) {
332 				ConditionalEntry[] newSet;
333 				foreach (ce; cs.set) {
334 					// This is not the symbol we are interested in, move on.
335 					if (ce.cdBranches[0].sif !is sif) {
336 						newSet ~= ce;
337 						continue;
338 					}
339 
340 					// If this is not the right branch, forget.
341 					if (ce.cdBranches[0].branch != branch) {
342 						continue;
343 					}
344 
345 					// The top level static if is resolved, drop.
346 					ce.cdBranches = ce.cdBranches[1 .. $];
347 
348 					// There are nested static ifs, put back in the set.
349 					if (ce.cdBranches.length) {
350 						newSet ~= ce;
351 						continue;
352 					}
353 
354 					// FIXME: Check if it is an overloadable symbol.
355 					assert(cs.selected is null, "overload ? bug ?");
356 
357 					// We have a new symbol, select it.
358 					if (cs.isPoisoned) {
359 						import source.exception;
360 						throw new CompileException(s.location, "Poisoned");
361 					}
362 
363 					cs.selected = ce.entry;
364 				}
365 
366 				cs.set = newSet;
367 			}
368 		}
369 	}
370 }
371 
372 final:
373 
374 /**
375  * A scope associate identifier with declarations.
376  */
377 class NestedScope : Scope {
378 	// TODO: Remove the module field, which can be access from fun.
379 	Function fun;
380 
381 	mixin ScopeImpl!(ScopeType.Nested);
382 
383 	this(Function fun) {
384 		this.fun = fun;
385 		this.parentScope = fun;
386 	}
387 
388 	this(NestedScope parentScope) {
389 		this.fun = parentScope.fun;
390 		this.parentScope = parentScope;
391 	}
392 
393 	this(Scope s) {
394 		if (auto n = cast(NestedScope) s) {
395 			this(n);
396 		} else if (auto f = cast(Function) s) {
397 			this(f);
398 		} else {
399 			assert(0, "Parent scope must be a function or a nested scope");
400 		}
401 	}
402 
403 	Module getModule() {
404 		return fun.getModule();
405 	}
406 }
407 
408 // XXX: Can't be private because mixin templates are a glorious hack.
409 // private:
410 class Poison : Symbol {
411 	this(Location location, Name name) {
412 		super(location, name);
413 	}
414 }
415 
416 struct ConditionalEntry {
417 	Symbol entry;
418 	ConditionalBranch[] cdBranches;
419 }
420 
421 class ConditionalSet : Symbol {
422 	ConditionalEntry[] set;
423 
424 	Symbol selected;
425 
426 	this(Location location, Name name, ConditionalEntry[] set) {
427 		super(location, name);
428 		this.set = set;
429 	}
430 }