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 module vox.fe.ast.type.func_sig;
5 
6 import vox.all;
7 
8 enum FuncSignatureFlags : ushort
9 {
10 	// Set if at least one of return or parameter types is meta type
11 	isCtfeOnly = AstFlags.userFlag << 0,
12 	// Function has parameter with expanded type
13 	hasExpandedParam = AstFlags.userFlag << 1,
14 	attachedToFunctionWithBody = AstFlags.userFlag << 2,
15 }
16 
17 @(AstType.type_func_sig)
18 struct FunctionSignatureNode {
19 	mixin AstNodeData!(AstType.type_func_sig, AstFlags.isType, AstNodeState.name_register_self_done);
20 	AstIndex returnType;
21 	// parameters are owned by the function declaration or
22 	// if it is part of function type literal then there is no owner
23 	AstNodes parameters; // array of var declarations
24 	CallConvention callConvention; // Is set in the parser
25 	ubyte numDefaultArgs;
26 	/// Number of parameters before variadic parameter
27 	/// Is equal to parameters.length when no variadic is present
28 	ushort numParamsBeforeVariadic;
29 
30 	private IrIndex irType; /// Index of function type
31 	TypeNode* typeNode() return { return cast(TypeNode*)&this; }
32 
33 	IrIndex getIrType(CompilationContext* c) {
34 		gen_ir_type_func_sig(&this, c); // calculate if needed
35 		return irType;
36 	}
37 
38 	bool isCtfeOnly() { return cast(bool)(flags & FuncSignatureFlags.isCtfeOnly); }
39 	bool hasExpandedParam() { return cast(bool)(flags & FuncSignatureFlags.hasExpandedParam); }
40 	bool attachedToFunctionWithBody() { return cast(bool)(flags & FuncSignatureFlags.attachedToFunctionWithBody); }
41 	bool isExternal() { return hasAttributes && attributeInfo.isExternal; }
42 }
43 
44 void print_func_sig(FunctionSignatureNode* node, ref AstPrintState state)
45 {
46 	state.print("TYPE ", node.typeNode.printer(state.context), node.isCtfeOnly ? " #ctfe" : null);
47 }
48 
49 void post_clone_func_sig(FunctionSignatureNode* node, ref CloneState state)
50 {
51 	state.fixAstIndex(node.returnType);
52 	state.fixAstNodes(node.parameters);
53 }
54 
55 // only occurs when signature is a part of function declaration
56 void name_register_nested_func_sig(FunctionSignatureNode* node, ref NameRegisterState state) {
57 	node.state = AstNodeState.name_register_nested;
58 	require_name_register(node.parameters, state);
59 	node.state = AstNodeState.name_register_nested_done;
60 	if (node.hasExpandedParam) expandVariadicParam(node, state.context);
61 }
62 
63 void name_resolve_func_sig(FunctionSignatureNode* node, ref NameResolveState state)
64 {
65 	node.state = AstNodeState.name_resolve;
66 	require_name_resolve(node.returnType, state);
67 	require_name_resolve(node.parameters, state);
68 	node.state = AstNodeState.name_resolve_done;
69 }
70 
71 // This happend when we are name resolving function signature inside template instance,
72 // so we already have final values for variadic template argument
73 void expandVariadicParam(FunctionSignatureNode* node, CompilationContext* c)
74 {
75 	uint variadicIndex = node.numParamsBeforeVariadic;
76 	auto param = node.parameters[variadicIndex].get!VariableDeclNode(c);
77 	// we are still in name register pass, but we need to get to the alias array
78 	require_name_resolve(param.type, c);
79 
80 	AstNode* typeArray = param.type.get_effective_node(c).get_node(c);
81 	if (typeArray.astType != AstType.decl_alias_array)
82 	{
83 		if (typeArray.isError) return;
84 		if (typeArray.astType == AstType.type_slice) {
85 			c.unrecoverable_error(param.loc,
86 				"Variadic arrays are not yet implemented",
87 				typeArray.astType);
88 		}
89 		c.unrecoverable_error(param.loc,
90 			"Expand operator must be applied to variadic template argument, not %s",
91 			typeArray.astType);
92 	}
93 
94 	auto types = typeArray.as!AliasArrayDeclNode(c);
95 
96 	uint numVariadicParams = types.items.length;
97 	node.parameters.replaceAt(c.arrayArena, variadicIndex, 1, types.items[]);
98 
99 	AstNodes vars;
100 	vars.reserve(c.arrayArena, numVariadicParams);
101 
102 	foreach(size_t i, AstIndex type; types.items)
103 	{
104 		string originalId = c.idString(param.id);
105 		Identifier paramId = c.idMap.getOrRegFormatted(c, "__%s_%s", originalId, i);
106 		AstIndex newParamIndex = c.appendAst!VariableDeclNode(param.loc, ScopeIndex.init, type, AstIndex.init, paramId, cast(ushort)(param.scopeIndex + i));
107 		auto newParam = newParamIndex.get!VariableDeclNode(c);
108 		newParam.flags |= VariableFlags.isParameter | VariableFlags.isAnonymous;
109 		newParam.state = AstNodeState.name_resolve_done;
110 		node.parameters[variadicIndex + i] = newParamIndex;
111 		vars.put(c.arrayArena, newParamIndex);
112 	}
113 
114 	// update indices of other params
115 	foreach(size_t i; variadicIndex + numVariadicParams..node.parameters.length)
116 	{
117 		auto rtParam = node.parameters[i].get!VariableDeclNode(c);
118 		rtParam.scopeIndex = cast(ushort)(rtParam.scopeIndex + numVariadicParams - 1);
119 	}
120 
121 	// rewrite variadic parameter as array literal in-place
122 	static assert(VariableDeclNode.sizeof >= AliasArrayDeclNode.sizeof,
123 		"VariableDeclNode.sizeof < AliasArrayDeclNode.sizeof");
124 	auto arrayNode = cast(AliasArrayDeclNode*)param;
125 	*arrayNode = AliasArrayDeclNode(param.loc, vars);
126 }
127 
128 // Parameters consist of 4 groups:
129 // 1) 0+, non-variadic, non-default
130 // 2) 0+, variadic
131 // 3) 0+, non-variadic, non-default
132 // 4) 0+, non-variadic, default
133 void type_check_func_sig(FunctionSignatureNode* node, ref TypeCheckState state)
134 {
135 	CompilationContext* c = state.context;
136 
137 	node.state = AstNodeState.type_check;
138 
139 	// ir_header
140 	gen_ir_header_func_sig(node, c);
141 
142 	// Process @extern attribute
143 	process_externs(node, c);
144 
145 	require_type_check(node.returnType, state);
146 	check_is_type(node.returnType, c);
147 
148 	TypeNode* returnType = node.returnType.get_type(c);
149 	if (returnType.isOpaqueStruct(c)) {
150 		c.error(node.loc,
151 			"function cannot return opaque type `%s`",
152 			returnType.printer(c));
153 	}
154 
155 	require_type_check(node.parameters, state);
156 
157 	if (caclIsCtfeOnly(node, c)) node.flags |= FuncSignatureFlags.isCtfeOnly;
158 
159 	node.state = AstNodeState.type_check_done;
160 }
161 
162 // Validate @extern attributes, bind externals and update calling conventions
163 // depends on ir_header
164 void process_externs(FunctionSignatureNode* node, CompilationContext* c)
165 {
166 	// Check that function `isExternal` matches `isExternal` of signature
167 	if (!node.attachedToFunctionWithBody && !node.isExternal) {
168 		// allow until @extern(module) is implemented
169 		// TODO: we don't know atm if we are attached to function without body or it is free function type
170 	}
171 
172 	if (!node.isExternal) return; // skip if no extern properties are bound to this node
173 
174 	AstIndex lastExternAttrib;
175 
176 	foreach(AstIndex attrib; node.attributeInfo.attributes)
177 	{
178 		auto attribNode = attrib.get_node(c);
179 
180 		if (attribNode.astType != AstType.decl_builtin_attribute) continue;
181 
182 		void onExternAttrib() {
183 			// check for duplicates
184 			if (lastExternAttrib.isDefined) {
185 				// check if previous @extern attribute is broadcasted
186 				// NOTE: all broadcasted attributes are located before direct attributes
187 				//       so we can detect multiple directly applied @extern attributes by detecting 2 consecutive @extern attributes
188 				if (!attribNode.as!BuiltinAttribNode(c).isBroadcasted)
189 				{
190 					if (lastExternAttrib.get!BuiltinAttribNode(c).isBroadcasted) {
191 						// allow duplicates in cases when only one @extern attribute is directly applied
192 						// and others are broadcast applied (like `@extern(...):` and `@extern(...){...}`)
193 					} else {
194 						c.error(attribNode.loc, "Duplicate @extern attribute");
195 					}
196 				}
197 			}
198 			lastExternAttrib = attrib; // save last @extern attribute
199 		}
200 
201 		final switch(cast(BuiltinAttribSubType)attribNode.subType) {
202 			case BuiltinAttribSubType.extern_syscall:
203 				onExternAttrib();
204 				uint syscall_number = attribNode.as!BuiltinAttribNode(c).data;
205 
206 				if (syscall_number > ushort.max) {
207 					c.error(attribNode.loc, "Max supported syscall number is 64k, got %s", syscall_number);
208 					return;
209 				}
210 
211 				if (c.targetOs != TargetOs.linux) {
212 					c.error(attribNode.loc, "@extern(syscall) attribute is only implemented on linux");
213 					return;
214 				}
215 				break;
216 
217 			case BuiltinAttribSubType.extern_module:
218 				onExternAttrib();
219 				break;
220 		}
221 	}
222 
223 	// Apply the last @extern attribute
224 	auto attribNode = lastExternAttrib.get_node(c);
225 	final switch(cast(BuiltinAttribSubType)attribNode.subType) {
226 		case BuiltinAttribSubType.extern_syscall:
227 			uint syscall_number = attribNode.as!BuiltinAttribNode(c).data;
228 			auto funcType = &c.types.get!IrTypeFunction(node.irType);
229 			funcType.callConv = CallConvention.sysv64_syscall;
230 			funcType.syscallNumber = cast(ushort)syscall_number;
231 			break;
232 
233 		case BuiltinAttribSubType.extern_module:
234 			// TODO: lookup by the name in external host module, or create import entry for dll symbols
235 			break;
236 	}
237 
238 	if (node.attachedToFunctionWithBody) {
239 		if (attribNode.as!BuiltinAttribNode(c).isBroadcasted) {
240 			// allow broadcasted @extern attribute on function with body
241 		} else {
242 			c.error(node.loc, "External function cannot have a body");
243 		}
244 	}
245 }
246 
247 private bool caclIsCtfeOnly(FunctionSignatureNode* node, CompilationContext* c) {
248 	if (node.returnType.get_node_type(c).isMetaType(c)) return true;
249 	foreach (AstIndex param; node.parameters) {
250 		if (param.get_node_type(c).isMetaType(c)) return true;
251 	}
252 	return false;
253 }
254 
255 bool same_type_func_sig(FunctionSignatureNode* t1, FunctionSignatureNode* t2, CompilationContext* c)
256 {
257 	if (!same_type(t1.returnType, t2.returnType, c)) return false;
258 	if (t1.parameters.length != t2.parameters.length) return false;
259 	foreach (i, AstIndex paramA; t1.parameters)
260 	{
261 		AstIndex paramB = t2.parameters[i];
262 		if (!same_type(paramA.get_node_type(c), paramB.get_node_type(c), c)) return false;
263 	}
264 	return true;
265 }
266 
267 TypeConvResKind type_conv_func_sig(FunctionSignatureNode* node, AstIndex typeBIndex, ref AstIndex expr, CompilationContext* c)
268 {
269 	if (typeBIndex.get_type(c).isAlias) return TypeConvResKind.ii_i;
270 	return TypeConvResKind.fail;
271 }
272 
273 void gen_ir_header_func_sig(FunctionSignatureNode* node, CompilationContext* c)
274 {
275 	final switch(node.getPropertyState(NodeProperty.ir_header)) {
276 		case PropertyState.not_calculated: break;
277 		case PropertyState.calculating: c.circular_dependency;
278 		case PropertyState.calculated: return;
279 	}
280 
281 	c.begin_node_property_calculation(node, NodeProperty.ir_header);
282 	scope(exit) c.end_node_property_calculation(node, NodeProperty.ir_header);
283 
284 	uint numResults = node.returnType.isTypeVoid ? 0 : 1;
285 	node.irType = c.types.appendFuncSignature(numResults, node.parameters.length, node.callConvention);
286 }
287 
288 // depends on ir_header
289 IrIndex gen_ir_type_func_sig(FunctionSignatureNode* node, CompilationContext* c)
290 	out(res; res.isTypeFunction, "Not a function type")
291 {
292 	final switch(node.getPropertyState(NodeProperty.ir_body)) {
293 		case PropertyState.not_calculated: break;
294 		case PropertyState.calculating: c.circular_dependency;
295 		case PropertyState.calculated: return node.irType;
296 	}
297 
298 	// dependencies
299 	gen_ir_header_func_sig(node, c);
300 
301 	c.begin_node_property_calculation(node, NodeProperty.ir_body);
302 	scope(exit) c.end_node_property_calculation(node, NodeProperty.ir_body);
303 
304 
305 	auto funcType = &c.types.get!IrTypeFunction(node.irType);
306 
307 	if (funcType.numResults == 1) {
308 		IrIndex returnType = node.returnType.gen_ir_type(c);
309 		funcType.resultTypes[0] = returnType;
310 	}
311 
312 	IrIndex[] parameterTypes = funcType.parameterTypes;
313 	foreach(i, AstIndex parameter; node.parameters) {
314 		parameterTypes[i] = parameter.get_node_type(c).gen_ir_type(c);
315 	}
316 
317 	return node.irType;
318 }