1 /// Copyright: Copyright (c) 2017-2019 Andrey Penechko.
2 /// License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
3 /// Authors: Andrey Penechko.
4 
5 /// We rely on the fact that nodes are allocated sequentially,
6 /// which allows us to simply copy a range on slots and fix indices,
7 /// in order to create template instance.
8 module vox.fe.ast.decl.template_;
9 
10 import vox.all;
11 
12 @(AstType.decl_template)
13 struct TemplateDeclNode
14 {
15 	mixin AstNodeData!(AstType.decl_template);
16 	/// For template name register
17 	ScopeIndex parentScope;
18 	/// Template parameters
19 	AstNodes parameters;
20 	/// Templated AST node (currently function or struct)
21 	AstIndex body;
22 	/// Points to the first index that needs to be copied
23 	AstIndex body_start;
24 	/// Points to the next index after body data
25 	AstIndex after_body;
26 	/// Template id. Same as underlying entity.
27 	Identifier id;
28 	/// Number of parameters before variadic parameter
29 	/// Is equal to parameters.length when no variadic is present
30 	ushort numParamsBeforeVariadic;
31 	/// Cached template instances
32 	Array!TemplateInstance instances;
33 
34 	bool hasVariadic() { return numParamsBeforeVariadic < parameters.length; };
35 }
36 
37 struct TemplateInstance
38 {
39 	/// Template arguments of this instance
40 	AstNodes args;
41 	/// AST node created for instantiated template. Copy of TemplateDeclNode.body
42 	AstIndex entity;
43 }
44 
45 void print_template(TemplateDeclNode* node, ref AstPrintState state)
46 {
47 	state.print("TEMPLATE ", state.context.idString(node.id));
48 	print_ast(node.parameters, state);
49 	print_ast(node.body, state);
50 	foreach (ref TemplateInstance inst; node.instances)
51 	{
52 		state.print("INSTANCE: ", state.context.idString(inst.entity.get_node_id(state.context)));
53 		print_ast(inst.entity, state);
54 	}
55 }
56 
57 void post_clone_template(TemplateDeclNode* node, ref CloneState state)
58 {
59 	state.fixScope(node.parentScope);
60 	state.fixAstNodes(node.parameters);
61 	state.fixAstIndex(node.body);
62 
63 	// TemplateDeclNode.after_body can be == to CloneState.cloned_to
64 	// Fix it manually
65 	// And it doesn't need node post clone code called
66 	node.body_start.storageIndex += state.offset;
67 	node.after_body.storageIndex += state.offset;
68 }
69 
70 void name_register_self_template(AstIndex nodeIndex, TemplateDeclNode* node, ref NameRegisterState state)
71 {
72 	node.state = AstNodeState.name_register_nested;
73 	node.parentScope.insert_scope(node.id, nodeIndex, state.context);
74 	node.state = AstNodeState.type_check_done;
75 }
76 
77 
78 enum TemplateParamDeclFlags : ushort
79 {
80 	isVariadic = AstFlags.userFlag << 0,
81 }
82 
83 @(AstType.decl_template_param)
84 struct TemplateParamDeclNode
85 {
86 	mixin AstNodeData!(AstType.decl_template_param);
87 	Identifier id;
88 	ushort index; // index in the list of template parameters
89 
90 	bool isVariadic() { return cast(bool)(flags & TemplateParamDeclFlags.isVariadic); }
91 }
92 
93 void print_template_param(TemplateParamDeclNode* node, ref AstPrintState state)
94 {
95 	state.print("TEMPLATE PARAM ", state.context.idString(node.id), node.isVariadic ? "..." : null);
96 }
97 
98 
99 struct CloneState
100 {
101 	CompilationContext* context;
102 
103 	// We need to add this offset to all indices inside copied slots that point into copied slots
104 	uint offset;
105 
106 	/// Points to original nodes
107 	AstIndex cloned_from;
108 	AstIndex cloned_to;
109 
110 	/// We want to redirect all references from `template_parent_scope` to `instance_scope`
111 	ScopeIndex template_parent_scope;
112 	/// Scope where template was instantiated. Contains template arguments
113 	ScopeIndex instance_scope;
114 
115 	void fixAstIndex(ref AstIndex nodeIndex)
116 	{
117 		// null is automatically out of bounds
118 		if (nodeIndex.storageIndex >= cloned_from.storageIndex && nodeIndex.storageIndex < cloned_to.storageIndex)
119 		{
120 			nodeIndex.storageIndex += offset;
121 			post_clone(nodeIndex, this);
122 		}
123 	}
124 
125 	void fixAstNodes(ref AstNodes nodes)
126 	{
127 		nodes = nodes.dup(context.arrayArena);
128 		foreach(ref AstIndex index; nodes)
129 			fixAstIndex(index);
130 	}
131 
132 	void fixScope(ref ScopeIndex _scope)
133 	{
134 		// null is automatically out of bounds
135 		if (_scope.storageIndex >= cloned_from.storageIndex && _scope.storageIndex < cloned_to.storageIndex)
136 		{
137 			//writefln("fix %s -> %s", _scope, AstIndex(_scope.storageIndex + offset));
138 			_scope.storageIndex += offset;
139 
140 			Scope* s = _scope.get_scope(context);
141 			post_clone_scope(s, this);
142 		}
143 		else if (_scope == template_parent_scope)
144 		{
145 			assert(_scope.isDefined);
146 			// redirect to this scope for instance argument resolution
147 			//writefln("fix %s -> %s", _scope, instance_scope);
148 			_scope = instance_scope;
149 		}
150 	}
151 }
152 
153 AstIndex get_template_instance(AstIndex templateIndex, TokenIndex start, AstNodes args, ref TypeCheckState state)
154 {
155 	CompilationContext* c = state.context;
156 	auto templ = templateIndex.get!TemplateDeclNode(c);
157 
158 	++c.numTemplateInstanceLookups;
159 	auto numParams = templ.parameters.length;
160 	auto numArgs = args.length;
161 
162 	AstIndex errNumArgs() {
163 		c.error(start,
164 			"Wrong number of template arguments (%s), must be %s",
165 			numArgs,
166 			numParams);
167 		return CommonAstNodes.node_error;
168 	}
169 
170 	void checkArg(size_t index, AstIndex arg)
171 	{
172 		if (!arg.isType(c))
173 		{
174 			// will be lifted in the future
175 			c.error(arg.loc(c),
176 				"Template argument %s, must be a type, not %s", index+1,
177 				arg.astType(c));
178 		}
179 	}
180 
181 	// Verify arguments. For now only types are supported
182 	foreach(size_t i, AstIndex arg; args) {
183 		checkArg(i, arg);
184 	}
185 
186 	bool hasVariadic = templ.numParamsBeforeVariadic < numParams;
187 	AstNodes variadicTypes;
188 	if (hasVariadic) {
189 		// handle variadic parameter
190 		if (numArgs < numParams && numParams - numArgs > 1) return errNumArgs;
191 
192 		foreach(size_t i; templ.numParamsBeforeVariadic..numArgs) {
193 			variadicTypes.put(c.arrayArena, args[i]);
194 		}
195 	} else if (numArgs != numParams) {
196 		return errNumArgs;
197 	}
198 
199 	// Check if there is existing instance
200 	instance_loop:
201 	foreach(ref TemplateInstance instance; templ.instances)
202 	{
203 		// templates with variadics can have different number of instance arguments
204 		if (instance.args.length != numArgs) continue;
205 
206 		foreach(size_t i, AstIndex arg; instance.args)
207 		{
208 			if (!same_type(arg, args[i], c)) continue instance_loop;
209 		}
210 
211 		// Found match, reuse instance
212 		return instance.entity;
213 	}
214 
215 	// No matching instances found
216 	// Create new instance
217 
218 	++c.numTemplateInstantiations;
219 
220 	// Create scope for arguments
221 	ScopeIndex instance_scope = c.appendScope;
222 	Scope* newScope = c.getAstScope(instance_scope);
223 	newScope.parentScope = templ.parentScope;
224 	newScope.debugName = "template instance";
225 	newScope.kind = newScope.parentScope.get_scope(c).kind;
226 
227 	// Register template instance arguments
228 	foreach(size_t i; 0..templ.numParamsBeforeVariadic)
229 	{
230 		AstIndex paramIndex = templ.parameters[i];
231 		auto param = paramIndex.get!TemplateParamDeclNode(c);
232 		newScope.insert(param.id, args[i], c);
233 	}
234 
235 	// register variadic (must be single node)
236 	if (hasVariadic)
237 	{
238 		AstIndex paramIndex = templ.parameters[templ.numParamsBeforeVariadic];
239 		auto param = paramIndex.get!TemplateParamDeclNode(c);
240 
241 		// Create array of variadic types
242 		auto arrayIndex = c.appendAst!AliasArrayDeclNode(param.loc, variadicTypes);
243 		auto arrayNode = arrayIndex.get!AliasArrayDeclNode(c);
244 
245 		newScope.insert(param.id, arrayIndex, c);
246 	}
247 
248 	// Clone template body and apply fixes
249 	CloneState cloneState = clone_node(templ.body_start, templ.after_body, instance_scope, c);
250 	AstIndex instance = templ.body;
251 	cloneState.fixAstIndex(instance);
252 
253 	// Node may need to know if is a result of template instantiation
254 	instance.flags(c) |= AstFlags.isTemplateInstance;
255 
256 	// Create identifier for instance
257 	set_instance_id(instance, args, c);
258 
259 	// Cache instance
260 	templ.instances.put(c.arrayArena, TemplateInstance(args, instance));
261 
262 	// Type check instance
263 	// Must be registered before type check to prevent infinite recursion in case of recursive templates
264 	require_type_check(instance, c);
265 
266 	return instance;
267 }
268 
269 void set_instance_id(AstIndex instance_index, AstNodes instance_args, CompilationContext* c)
270 {
271 	Identifier* id = &instance_index.get_node_id(c);
272 	TextSink* sink = &c.tempBuf;
273 	sink.clear;
274 	sink.put(c.idString(*id));
275 	sink.put("[");
276 	foreach(size_t i, AstIndex arg; instance_args)
277 	{
278 		if (i > 0) sink.put(", ");
279 		print_node_name(*sink, arg, c);
280 	}
281 	sink.put("]");
282 	const(char)[] idString = sink.data.data;
283 	*id = c.idMap.getOrReg(c, idString);
284 }
285 
286 /// Perform copiying of AST subtree and index fixing
287 /// node_start..after_node is the range of slots to be copied
288 /// instance_scope is the scope created around AST subtree copy
289 /// All nodes need to fixed via CloneState through indices pointing inside cloned tree
290 CloneState clone_node(AstIndex node_start, AstIndex after_node, ScopeIndex instance_scope, CompilationContext* c)
291 {
292 	c.assertf(after_node.storageIndex > node_start.storageIndex, "%s > %s", node_start, after_node);
293 	AstIndex slots_start = AstIndex(c.astBuffer.uintLength);
294 	c.assertf(slots_start.storageIndex > node_start.storageIndex, "%s > %s", slots_start, node_start);
295 
296 	uint num_slots_to_copy = after_node.storageIndex - node_start.storageIndex;
297 	// allocate space at the end
298 	uint[] slots = c.astBuffer.voidPut(num_slots_to_copy);
299 	// copy slots
300 	slots[] = c.astBuffer.bufPtr[node_start.storageIndex..after_node.storageIndex];
301 
302 	CloneState state = {
303 		context : c,
304 		offset : slots_start.storageIndex - node_start.storageIndex,
305 		cloned_from : node_start,
306 		cloned_to : after_node,
307 		template_parent_scope : instance_scope.get_scope(c).parentScope,
308 		instance_scope : instance_scope,
309 	};
310 
311 	return state;
312 }
313 
314 /// Applies offset to indices pointing into copied area
315 /// Happens before name register
316 void post_clone(AstIndex nodeIndex, ref CloneState state)
317 {
318 	CompilationContext* c = state.context;
319 
320 	AstNode* node = c.getAstNode(nodeIndex);
321 
322 	if (node.hasAttributes) {
323 		post_clone_attributes(node.attributeInfo, state);
324 	}
325 
326 	final switch(node.astType) with(AstType)
327 	{
328 		case error: c.internal_error(node.loc, "Visiting error node");
329 		case abstract_node: c.internal_error(node.loc, "Visiting abstract node");
330 
331 		case decl_alias: post_clone_alias(cast(AliasDeclNode*)node, state); break;
332 		case decl_alias_array: break;
333 		case decl_builtin: break;
334 		case decl_builtin_attribute: break;
335 		case decl_module: assert(false);
336 		case decl_package: assert(false);
337 		case decl_import: post_clone_import(cast(ImportDeclNode*)node, state); break;
338 		case decl_function: post_clone_func(cast(FunctionDeclNode*)node, state); break;
339 		case decl_var: post_clone_var(cast(VariableDeclNode*)node, state); break;
340 		case decl_struct: post_clone_struct(cast(StructDeclNode*)node, state); break;
341 		case decl_enum: post_clone_enum(cast(EnumDeclaration*)node, state); break;
342 		case decl_enum_member: post_clone_enum_member(cast(EnumMemberDecl*)node, state); break;
343 		case decl_static_assert: post_clone_static_assert(cast(StaticAssertDeclNode*)node, state); break;
344 		case decl_static_foreach: post_clone_static_foreach(cast(StaticForeachDeclNode*)node, state); break;
345 		case decl_static_if: post_clone_static_if(cast(StaticIfDeclNode*)node, state); break;
346 		case decl_static_version: post_clone_static_version(cast(StaticVersionDeclNode*)node, state); break;
347 		case decl_template: post_clone_template(cast(TemplateDeclNode*)node, state); break;
348 		case decl_template_param: break;
349 
350 		case stmt_block: post_clone_block(cast(BlockStmtNode*)node, state); break;
351 		case stmt_if: post_clone_if(cast(IfStmtNode*)node, state); break;
352 		case stmt_while: post_clone_while(cast(WhileStmtNode*)node, state); break;
353 		case stmt_do_while: post_clone_do(cast(DoWhileStmtNode*)node, state); break;
354 		case stmt_for: post_clone_for(cast(ForStmtNode*)node, state); break;
355 		case stmt_switch: post_clone_switch(cast(SwitchStmtNode*)node, state); break;
356 		case stmt_return: post_clone_return(cast(ReturnStmtNode*)node, state); break;
357 		case stmt_break: break;
358 		case stmt_continue: break;
359 
360 		case expr_name_use: post_clone_name_use(cast(NameUseExprNode*)node, state); break;
361 		case expr_member: post_clone_member(cast(MemberExprNode*)node, state); break;
362 		case expr_bin_op: post_clone_binary_op(cast(BinaryExprNode*)node, state); break;
363 		case expr_un_op: post_clone_unary_op(cast(UnaryExprNode*)node, state); break;
364 		case expr_call: post_clone_call(cast(CallExprNode*)node, state); break;
365 		case expr_named_argument: post_clone_named_argument(cast(NamedArgumenExprNode*)node, state); break;
366 		case expr_index: post_clone_index(cast(IndexExprNode*)node, state); break;
367 		case expr_slice: post_clone_expr_slice(cast(SliceExprNode*)node, state); break;
368 		case expr_type_conv: post_clone_type_conv(cast(TypeConvExprNode*)node, state); break;
369 
370 		case literal_int: break;
371 		case literal_float: break;
372 		case literal_string: break;
373 		case literal_null: break;
374 		case literal_bool: break;
375 		case literal_array: break;
376 		case literal_special: break;
377 
378 		case type_basic: break;
379 		case type_func_sig: post_clone_func_sig(cast(FunctionSignatureNode*)node, state); break;
380 		case type_ptr: post_clone_ptr(cast(PtrTypeNode*)node, state); break;
381 		case type_static_array: post_clone_static_array(cast(StaticArrayTypeNode*)node, state); break;
382 		case type_slice: post_clone_slice(cast(SliceTypeNode*)node, state); break;
383 	}
384 }