1 /**
2  * This file is part of DCD, a development tool for the D programming language.
3  * Copyright (C) 2014 Brian Schott
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 module dsymbol.conversion.second;
20 
21 import dsymbol.semantic;
22 import dsymbol.string_interning;
23 import dsymbol.symbol;
24 import dsymbol.scope_;
25 import dsymbol.builtin.names;
26 import dsymbol.builtin.symbols;
27 import dsymbol.type_lookup;
28 import dsymbol.deferred;
29 import dsymbol.import_;
30 import dsymbol.modulecache;
31 import containers.unrolledlist;
32 import std.experimental.allocator;
33 import std.experimental.allocator.mallocator;
34 import std.experimental.logger;
35 import dparse.ast;
36 import dparse.lexer;
37 
38 void secondPass(SemanticSymbol* currentSymbol, Scope* moduleScope, ref ModuleCache cache)
39 {
40 	with (CompletionKind) final switch (currentSymbol.acSymbol.kind)
41 	{
42 	case className:
43 	case interfaceName:
44 		resolveInheritance(currentSymbol.acSymbol, currentSymbol.typeLookups,
45 			moduleScope, cache);
46 		break;
47 	case withSymbol:
48 	case variableName:
49 	case memberVariableName:
50 	case functionName:
51 	case aliasName:
52 		// type may not be null in the case of a renamed import
53 		if (currentSymbol.acSymbol.type is null)
54 		{
55 			resolveType(currentSymbol.acSymbol, currentSymbol.typeLookups,
56 				moduleScope, cache);
57 		}
58 		break;
59 	case importSymbol:
60 		if (currentSymbol.acSymbol.type is null)
61 			resolveImport(currentSymbol.acSymbol, currentSymbol.typeLookups, cache);
62 		break;
63 	case structName:
64 	case unionName:
65 	case enumName:
66 	case keyword:
67 	case enumMember:
68 	case packageName:
69 	case moduleName:
70 	case dummy:
71 	case templateName:
72 	case mixinTemplateName:
73 		break;
74 	}
75 
76 	foreach (child; currentSymbol.children)
77 		secondPass(child, moduleScope, cache);
78 
79 	// Alias this and mixin templates are resolved after child nodes are
80 	// resolved so that the correct symbol information will be available.
81 	with (CompletionKind) switch (currentSymbol.acSymbol.kind)
82 	{
83 	case className:
84 	case interfaceName:
85 	case structName:
86 	case unionName:
87 		resolveAliasThis(currentSymbol.acSymbol, currentSymbol.typeLookups, cache);
88 		resolveMixinTemplates(currentSymbol.acSymbol, currentSymbol.typeLookups,
89 			moduleScope, cache);
90 		break;
91 	default:
92 		break;
93 	}
94 }
95 
96 void resolveImport(DSymbol* acSymbol, ref UnrolledList!(TypeLookup*, Mallocator, false) typeLookups,
97 	ref ModuleCache cache)
98 in
99 {
100 	assert (acSymbol.kind == CompletionKind.importSymbol);
101 }
102 body
103 {
104 	if (acSymbol.qualifier == SymbolQualifier.selectiveImport)
105 	{
106 		assert(acSymbol.symbolFile !is null);
107 		DSymbol* moduleSymbol = cache.cacheModule(acSymbol.symbolFile);
108 		if (moduleSymbol is null)
109 		{
110 		tryAgain:
111 			DeferredSymbol* deferred = Mallocator.instance.make!DeferredSymbol(acSymbol);
112 			deferred.typeLookups.insert(typeLookups[]);
113 			// Get rid of the old references to the lookups, this new deferred
114 			// symbol owns them now
115 			typeLookups.clear();
116 			cache.deferredSymbols.insert(deferred);
117 		}
118 		else
119 		{
120 			immutable size_t breadcrumbCount = typeLookups.front.breadcrumbs.length;
121 			assert(breadcrumbCount <= 2 && breadcrumbCount > 0, "Malformed selective import");
122 
123 			istring symbolName = typeLookups.front.breadcrumbs.front;
124 			DSymbol* selected = moduleSymbol.getFirstPartNamed(symbolName);
125 			if (acSymbol is null)
126 				goto tryAgain;
127 			acSymbol.type = selected;
128 			acSymbol.ownType = false;
129 
130 			// count of 1 means selective import
131 			// count of 2 means a renamed selective import
132 			if (breadcrumbCount == 2)
133 			{
134 				acSymbol.kind = CompletionKind.aliasName;
135 				acSymbol.symbolFile = acSymbol.altFile;
136 			}
137 		}
138 	}
139 	else
140 	{
141 		assert(acSymbol.symbolFile !is null);
142 		DSymbol* moduleSymbol = cache.cacheModule(acSymbol.symbolFile);
143 		if (moduleSymbol is null)
144 		{
145 			DeferredSymbol* deferred = Mallocator.instance.make!DeferredSymbol(acSymbol);
146 			cache.deferredSymbols.insert(deferred);
147 		}
148 		else
149 		{
150 			acSymbol.type = moduleSymbol;
151 			acSymbol.ownType = false;
152 		}
153 	}
154 }
155 
156 void resolveTypeFromType(DSymbol* symbol, TypeLookup* lookup, Scope* moduleScope,
157 	ref ModuleCache cache, UnrolledList!(DSymbol*, Mallocator, false)* imports)
158 in
159 {
160 	if (imports !is null)
161 		foreach (i; imports.opSlice())
162 			assert(i.kind == CompletionKind.importSymbol);
163 }
164 body
165 {
166 	// The left-most suffix
167 	DSymbol* suffix;
168 	// The right-most suffix
169 	DSymbol* lastSuffix;
170 
171 	// Create symbols for the type suffixes such as array and
172 	// associative array
173 	while (!lookup.breadcrumbs.empty)
174 	{
175 		auto back = lookup.breadcrumbs.back;
176 		immutable bool isArr = back == ARRAY_SYMBOL_NAME;
177 		immutable bool isAssoc = back == ASSOC_ARRAY_SYMBOL_NAME;
178 		immutable bool isFunction = back == FUNCTION_SYMBOL_NAME;
179 		if (back == POINTER_SYMBOL_NAME)
180 		{
181 			lastSuffix.isPointer = true;
182 			lookup.breadcrumbs.popBack();
183 			continue;
184 		}
185 		if (!isArr && !isAssoc && !isFunction)
186 			break;
187 		immutable qualifier = isAssoc ? SymbolQualifier.assocArray :
188 			(isFunction ? SymbolQualifier.func : SymbolQualifier.array);
189 		lastSuffix = cache.symbolAllocator.make!DSymbol(back, CompletionKind.dummy, lastSuffix);
190 		lastSuffix.qualifier = qualifier;
191 		lastSuffix.ownType = true;
192 		if (isFunction)
193 		{
194 			lookup.breadcrumbs.popBack();
195 			lastSuffix.callTip = lookup.breadcrumbs.back();
196 		}
197 		else
198 			lastSuffix.addChildren(isArr ? arraySymbols[] : assocArraySymbols[], false);
199 
200 		if (suffix is null)
201 			suffix = lastSuffix;
202 		lookup.breadcrumbs.popBack();
203 	}
204 
205 	UnrolledList!(DSymbol*, Mallocator, false) remainingImports;
206 
207 	DSymbol* currentSymbol;
208 
209 	void getSymbolFromImports(UnrolledList!(DSymbol*, Mallocator, false)* importList, istring name)
210 	{
211 		foreach (im; importList.opSlice())
212 		{
213 			assert(im.symbolFile !is null);
214 			// Try to find a cached version of the module
215 			DSymbol* moduleSymbol = cache.getModuleSymbol(im.symbolFile);
216 			// If the module has not been cached yet, store it in the
217 			// remaining imports list
218 			if (moduleSymbol is null)
219 			{
220 				remainingImports.insert(im);
221 				continue;
222 			}
223 			// Try to get the symbol from the imported module
224 			currentSymbol = moduleSymbol.getFirstPartNamed(name);
225 			if (currentSymbol is null)
226 				continue;
227 		}
228 	}
229 
230 	// Follow all the names and try to resolve them
231 	size_t i = 0;
232 	foreach (part; lookup.breadcrumbs[])
233 	{
234 		if (i == 0)
235 		{
236 			if (moduleScope is null)
237 				getSymbolFromImports(imports, part);
238 			else
239 			{
240 				auto symbols = moduleScope.getSymbolsByNameAndCursor(part, symbol.location);
241 				if (symbols.length > 0)
242 					currentSymbol = symbols[0];
243 				else
244 					return;
245 			}
246 		}
247 		else
248 		{
249 			if (currentSymbol.kind == CompletionKind.aliasName)
250 				currentSymbol = currentSymbol.type;
251 			if (currentSymbol is null)
252 				return;
253 			if (currentSymbol.kind == CompletionKind.moduleName && currentSymbol.type !is null)
254 				currentSymbol = currentSymbol.type;
255 			if (currentSymbol is null)
256 				return;
257 			if (currentSymbol.kind == CompletionKind.importSymbol)
258 				currentSymbol = currentSymbol.type;
259 			if (currentSymbol is null)
260 				return;
261 			currentSymbol = currentSymbol.getFirstPartNamed(part);
262 		}
263 		++i;
264 		if (currentSymbol is null)
265 			return;
266 	}
267 
268 	if (lastSuffix !is null)
269 	{
270 		assert(suffix !is null);
271 		suffix.type = currentSymbol;
272 		suffix.ownType = false;
273 		symbol.type = lastSuffix;
274 		symbol.ownType = true;
275 		if (currentSymbol is null && !remainingImports.empty)
276 		{
277 //			info("Deferring type resolution for ", symbol.name);
278 			auto deferred = Mallocator.instance.make!DeferredSymbol(suffix);
279 			// TODO: The scope has ownership of the import information
280 			deferred.imports.insert(remainingImports[]);
281 			deferred.typeLookups.insert(lookup);
282 			cache.deferredSymbols.insert(deferred);
283 		}
284 	}
285 	else if (currentSymbol !is null)
286 	{
287 		symbol.type = currentSymbol;
288 		symbol.ownType = false;
289 	}
290 	else if (!remainingImports.empty)
291 	{
292 		auto deferred = Mallocator.instance.make!DeferredSymbol(symbol);
293 //		info("Deferring type resolution for ", symbol.name);
294 		// TODO: The scope has ownership of the import information
295 		deferred.imports.insert(remainingImports[]);
296 		deferred.typeLookups.insert(lookup);
297 		cache.deferredSymbols.insert(deferred);
298 	}
299 }
300 
301 private:
302 
303 void resolveInheritance(DSymbol* symbol, ref UnrolledList!(TypeLookup*, Mallocator, false) typeLookups,
304 	Scope* moduleScope, ref ModuleCache cache)
305 {
306 	import std.algorithm : filter;
307 
308 	outer: foreach (TypeLookup* lookup; typeLookups[])
309 	{
310 		if (lookup.kind != TypeLookupKind.inherit)
311 			continue;
312 		DSymbol* baseClass;
313 		assert(lookup.breadcrumbs.length > 0);
314 
315 		// TODO: Delayed type lookup
316 		auto symbolScope = moduleScope.getScopeByCursor(symbol.location);
317 		auto symbols = moduleScope.getSymbolsByNameAndCursor(lookup.breadcrumbs.front,
318 			symbol.location);
319 		if (symbols.length == 0)
320 			continue;
321 
322 		baseClass = symbols[0];
323 		lookup.breadcrumbs.popFront();
324 		foreach (part; lookup.breadcrumbs[])
325 		{
326 			symbols = baseClass.getPartsByName(part);
327 			if (symbols.length == 0)
328 				continue outer;
329 			baseClass = symbols[0];
330 		}
331 
332 		DSymbol* imp = cache.symbolAllocator.make!DSymbol(IMPORT_SYMBOL_NAME,
333 			CompletionKind.importSymbol, baseClass);
334 		symbol.addChild(imp, true);
335 		symbolScope.addSymbol(imp, false);
336 		if (baseClass.kind == CompletionKind.className)
337 		{
338 			auto s = cache.symbolAllocator.make!DSymbol(SUPER_SYMBOL_NAME,
339 				CompletionKind.variableName, baseClass);
340 			symbolScope.addSymbol(s, true);
341 		}
342 	}
343 }
344 
345 void resolveAliasThis(DSymbol* symbol,
346 	ref UnrolledList!(TypeLookup*, Mallocator, false) typeLookups, ref ModuleCache cache)
347 {
348 	import std.algorithm : filter;
349 
350 	foreach (aliasThis; typeLookups[].filter!(a => a.kind == TypeLookupKind.aliasThis))
351 	{
352 		assert(aliasThis.breadcrumbs.length > 0);
353 		auto parts = symbol.getPartsByName(aliasThis.breadcrumbs.front);
354 		if (parts.length == 0 || parts[0].type is null)
355 			continue;
356 		DSymbol* s = cache.symbolAllocator.make!DSymbol(IMPORT_SYMBOL_NAME,
357 			CompletionKind.importSymbol, parts[0].type);
358 		symbol.addChild(s, true);
359 	}
360 }
361 
362 void resolveMixinTemplates(DSymbol* symbol,
363 	ref UnrolledList!(TypeLookup*, Mallocator, false) typeLookups, Scope* moduleScope, ref ModuleCache cache)
364 {
365 	import std.algorithm : filter;
366 
367 	foreach (mix; typeLookups[].filter!(a => a.kind == TypeLookupKind.mixinTemplate))
368 	{
369 		assert(mix.breadcrumbs.length > 0);
370 		auto symbols = moduleScope.getSymbolsByNameAndCursor(mix.breadcrumbs.front,
371 			symbol.location);
372 		if (symbols.length == 0)
373 			continue;
374 		auto currentSymbol = symbols[0];
375 		mix.breadcrumbs.popFront();
376 		foreach (m; mix.breadcrumbs[])
377 		{
378 			auto s = currentSymbol.getPartsByName(m);
379 			if (s.length == 0)
380 			{
381 				currentSymbol = null;
382 				break;
383 			}
384 			else
385 				currentSymbol = s[0];
386 		}
387 		if (currentSymbol !is null)
388 		{
389 			auto i = cache.symbolAllocator.make!DSymbol(IMPORT_SYMBOL_NAME,
390 				CompletionKind.importSymbol, currentSymbol);
391 			i.ownType = false;
392 			symbol.addChild(i, true);
393 		}
394 	}
395 }
396 
397 void resolveType(DSymbol* symbol, ref UnrolledList!(TypeLookup*, Mallocator, false) typeLookups,
398 	Scope* moduleScope, ref ModuleCache cache)
399 {
400 	if (typeLookups.length == 0)
401 		return;
402 	assert(typeLookups.length == 1);
403 	auto lookup = typeLookups.front;
404 	if (lookup.kind == TypeLookupKind.varOrFunType)
405 		resolveTypeFromType(symbol, lookup, moduleScope, cache, null);
406 	else if (lookup.kind == TypeLookupKind.initializer)
407 		resolveTypeFromInitializer(symbol, lookup, moduleScope, cache);
408 	else
409 		assert(false, "How did this happen?");
410 }
411 
412 
413 void resolveTypeFromInitializer(DSymbol* symbol, TypeLookup* lookup,
414 	Scope* moduleScope, ref ModuleCache cache)
415 {
416 	if (lookup.breadcrumbs.length == 0)
417 		return;
418 	DSymbol* currentSymbol = null;
419 	size_t i = 0;
420 
421 	auto crumbs = lookup.breadcrumbs[];
422 	foreach (crumb; crumbs)
423 	{
424 		if (i == 0)
425 		{
426 			currentSymbol = moduleScope.getFirstSymbolByNameAndCursor(
427 				symbolNameToTypeName(crumb), symbol.location);
428 
429 			// solves auto arrays
430 			if (crumb == ARRAY_SYMBOL_NAME)
431 			{
432 				auto nestedArr = crumbs.save();
433 				auto a = nestedArr.front();
434 
435 				DSymbol* suffix;
436 				DSymbol* lastSuffix;
437 
438 				// process the flags set in ArrayInitializer visit
439 				while (true)
440 				{
441 					lastSuffix = cache.symbolAllocator.make!(DSymbol)(a, CompletionKind.dummy, lastSuffix);
442 					lastSuffix.qualifier = SymbolQualifier.array;
443 
444 					if (suffix is null)
445 						suffix = lastSuffix;
446 
447 					nestedArr.popFront();
448 					if (nestedArr.empty())
449 						break;
450 					a = nestedArr.front();
451 					if (a != ARRAY_SYMBOL_NAME)
452 						break;
453 				}
454 
455 				// last crumb should be the element type
456 				DSymbol* elemType;
457 				if (!nestedArr.empty)
458 				{
459 					suffix.addChildren(arraySymbols[], false);
460 					elemType = moduleScope.getFirstSymbolByNameAndCursor(
461 						symbolNameToTypeName(a), symbol.location);
462 				}
463 
464 				// put the elem type to the back of the *arr* chain
465 				if (suffix !is null && elemType)
466 				{
467 					suffix.type = elemType;
468 					suffix.ownType = false;
469 					symbol.type = lastSuffix;
470 					symbol.ownType = true;
471 				}
472 			}
473 			if (currentSymbol is null)
474 				return;
475 		}
476 		else if (crumb == ARRAY_SYMBOL_NAME)
477 		{
478 			typeSwap(currentSymbol);
479 			if (currentSymbol is null)
480 				return;
481 
482 			// Index expressions can be an array index or an AA index
483 			if (currentSymbol.qualifier == SymbolQualifier.array
484 					|| currentSymbol.qualifier == SymbolQualifier.assocArray
485 					|| currentSymbol.kind == CompletionKind.aliasName)
486 			{
487 				if (currentSymbol.type !is null)
488 					currentSymbol = currentSymbol.type;
489 				else
490 					return;
491 			}
492 			else
493 			{
494 				auto opIndex = currentSymbol.getFirstPartNamed(internString("opIndex"));
495 				if (opIndex !is null)
496 					currentSymbol = opIndex.type;
497 				else
498 					return;
499 			}
500 		}
501 		else if (crumb == "foreach")
502 		{
503 			typeSwap(currentSymbol);
504 			if (currentSymbol is null)
505 				return;
506 			if (currentSymbol.qualifier == SymbolQualifier.array
507 					|| currentSymbol.qualifier == SymbolQualifier.assocArray)
508 			{
509 				currentSymbol = currentSymbol.type;
510 				break;
511 			}
512 			auto front = currentSymbol.getFirstPartNamed(internString("front"));
513 			if (front !is null)
514 			{
515 				currentSymbol = front.type;
516 				break;
517 			}
518 			auto opApply = currentSymbol.getFirstPartNamed(internString("opApply"));
519 			if (opApply !is null)
520 			{
521 				currentSymbol = opApply.type;
522 				break;
523 			}
524 		}
525 		else
526 		{
527 			typeSwap(currentSymbol);
528 			if (currentSymbol is null )
529 				return;
530 			currentSymbol = currentSymbol.getFirstPartNamed(crumb);
531 		}
532 		++i;
533 		if (currentSymbol is null)
534 			return;
535 	}
536 	typeSwap(currentSymbol);
537 	symbol.type = currentSymbol;
538 	symbol.ownType = false;
539 }
540 
541 void typeSwap(ref DSymbol* currentSymbol)
542 {
543 	while (currentSymbol !is null && currentSymbol.type !is currentSymbol
544 			&& (currentSymbol.kind == CompletionKind.variableName
545 			|| currentSymbol.kind == CompletionKind.importSymbol
546 			|| currentSymbol.kind == CompletionKind.withSymbol
547 			|| currentSymbol.kind == CompletionKind.aliasName))
548 		currentSymbol = currentSymbol.type;
549 }