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.decl.struct_;
5 
6 import vox.all;
7 
8 enum TypeFlags : ushort
9 {
10 	type_size_mask      = 1 << 0 | 1 << 1, // used for reading value
11 	size_not_calculated = 0 << 1,          // used for setting flags
12 	size_is_calculating = 1 << 1,          // used for setting flags
13 	size_is_calculated  = 2 << 1,          // used for setting flags
14 
15 	userFlag            = 1 << 2,
16 }
17 
18 enum StructFlags : ushort
19 {
20 	isOpaque   = TypeFlags.userFlag << 0,
21 	// Set if struct contains meta type member variables or methods
22 	isCtfeOnly = TypeFlags.userFlag << 1,
23 	isUnion    = TypeFlags.userFlag << 2,
24 }
25 
26 @(AstType.decl_struct)
27 struct StructDeclNode {
28 	mixin ScopeDeclNodeData!(AstType.decl_struct, AstFlags.isType);
29 	ScopeIndex parentScope;
30 	ScopeIndex memberScope; // null if no body
31 	Identifier id;
32 	IrIndex irType;
33 	IrIndex defaultVal;
34 
35 	this(TokenIndex loc, ScopeIndex parentScope, ScopeIndex memberScope, Identifier id)
36 	{
37 		this.loc = loc;
38 		this.astType = AstType.decl_struct;
39 		this.flags = AstFlags.isType;
40 		this.parentScope = parentScope;
41 		this.memberScope = memberScope;
42 		this.id = id;
43 		setPropertyState(NodeProperty.type, PropertyState.calculated);
44 	}
45 
46 	TypeNode* typeNode() return { return cast(TypeNode*)&this; }
47 	bool isOpaque() { return cast(bool)(flags & StructFlags.isOpaque); }
48 	bool isCtfeOnly() { return cast(bool)(flags & StructFlags.isCtfeOnly); }
49 	bool isUnion() { return cast(bool)(flags & StructFlags.isUnion); }
50 	string structOrUnionString() { return isUnion ? "union" : "struct"; }
51 	SizeAndAlignment sizealign(CompilationContext* c) {
52 		gen_ir_type_struct(&this, c);
53 		IrTypeStruct* structType = &c.types.get!IrTypeStruct(irType);
54 		return structType.sizealign;
55 	}
56 }
57 
58 struct StructDynMemberIterator
59 {
60 	StructDeclNode* node;
61 	CompilationContext* c;
62 
63 	int opApply(scope int delegate(uint index, ref AstIndex member) dg)
64 	{
65 		uint memberIndex;
66 		foreach(ref AstIndex decl; node.declarations)
67 		{
68 			if (!isDynamicStructMember(decl, c)) continue;
69 			if (auto res = dg(memberIndex, decl)) return res;
70 			++memberIndex;
71 		}
72 		return 0;
73 	}
74 }
75 
76 bool isDynamicStructMember(AstIndex decl, CompilationContext* c) {
77 	AstNode* member = decl.get_node(c);
78 	if (member.astType != AstType.decl_var) return false;
79 	VariableDeclNode* memberVar = member.as!VariableDeclNode(c);
80 	if (!memberVar.isMember) return false;
81 	return true;
82 }
83 
84 void print_struct(StructDeclNode* node, ref AstPrintState state)
85 {
86 	state.print(node.isUnion ? "UNION " : "STRUCT ", state.context.idString(node.id), node.isCtfeOnly ? " #ctfe" : null);
87 	print_ast(node.declarations, state);
88 }
89 
90 void post_clone_struct(StructDeclNode* node, ref CloneState state)
91 {
92 	state.fixScope(node.parentScope);
93 	state.fixScope(node.memberScope);
94 	state.fixAstNodes(node.declarations);
95 }
96 
97 void name_register_self_struct(AstIndex nodeIndex, StructDeclNode* node, ref NameRegisterState state) {
98 	node.state = AstNodeState.name_register_self;
99 	node.parentScope.insert_scope(node.id, nodeIndex, state.context);
100 	node.state = AstNodeState.name_register_self_done;
101 }
102 
103 void name_register_nested_struct(AstIndex nodeIndex, StructDeclNode* node, ref NameRegisterState state) {
104 	node.state = AstNodeState.name_register_nested;
105 	require_name_register(node.declarations, state);
106 	node.state = AstNodeState.name_register_nested_done;
107 }
108 
109 void name_resolve_struct(StructDeclNode* node, ref NameResolveState state) {
110 	node.state = AstNodeState.name_resolve;
111 	require_name_resolve(node.declarations, state);
112 	node.state = AstNodeState.name_resolve_done;
113 }
114 
115 void type_check_struct(StructDeclNode* node, ref TypeCheckState state)
116 {
117 	node.state = AstNodeState.type_check;
118 	require_type_check(node.declarations, state);
119 	gen_ir_type_struct(node, state.context);
120 	node.state = AstNodeState.type_check_done;
121 }
122 
123 TypeConvResKind type_conv_struct(StructDeclNode* node, AstIndex typeBIndex, ref AstIndex expr, CompilationContext* c)
124 {
125 	TypeNode* typeB = typeBIndex.get_type(c);
126 
127 	switch(typeB.astType) with(AstType)
128 	{
129 		case decl_enum:
130 			if (c.getAstNodeIndex(node) == typeB.as_enum.memberType.get_node_type(c))
131 				return TypeConvResKind.no_e;
132 			goto default;
133 		default: return TypeConvResKind.fail;
134 	}
135 }
136 
137 IrIndex gen_init_value_struct(StructDeclNode* node, CompilationContext* c)
138 {
139 	if (node.defaultVal.isDefined) return node.defaultVal;
140 
141 	IrIndex structType = node.gen_ir_type_struct(c);
142 	uint numStructMembers = c.types.get!IrTypeStruct(structType).numMembers;
143 	uint numArgSlots = node.isUnion ? 2 : numStructMembers;
144 	IrIndex[] args = c.allocateTempArray!IrIndex(numArgSlots);
145 	scope(exit) c.freeTempArray(args);
146 
147 	bool allZeroes = true;
148 	foreach(uint memberIndex, AstIndex member; StructDynMemberIterator(node, c))
149 	{
150 		VariableDeclNode* memberVar = member.get!VariableDeclNode(c);
151 		IrIndex memberValue = memberVar.gen_init_value_var(c);
152 		if (!memberValue.isConstantZero) allZeroes = false;
153 
154 		if (node.isUnion) {
155 			// only initialize first member in default struct initializer
156 			args[0] = c.constants.addZeroConstant(structType); // member index
157 			args[1] = memberValue; // value
158 			break;
159 		}
160 
161 		args[memberIndex] = memberValue;
162 	}
163 	if (allZeroes)
164 		node.defaultVal = c.constants.addZeroConstant(structType);
165 	else
166 		node.defaultVal = c.constants.addAggrecateConstant(structType, args);
167 
168 	return node.defaultVal;
169 }
170 
171 void gen_ir_header_struct(StructDeclNode* node, CompilationContext* c)
172 {
173 	final switch(node.getPropertyState(NodeProperty.ir_header)) {
174 		case PropertyState.not_calculated: break;
175 		case PropertyState.calculating: c.circular_dependency;
176 		case PropertyState.calculated: return;
177 	}
178 
179 	c.begin_node_property_calculation(node, NodeProperty.ir_header);
180 	scope(exit) c.end_node_property_calculation(node, NodeProperty.ir_header);
181 
182 	uint numFields = 0;
183 	foreach(uint memberIndex, AstIndex member; StructDynMemberIterator(node, c)) {
184 		++numFields;
185 	}
186 
187 	node.irType = c.types.appendStruct(numFields, node.isUnion);
188 }
189 
190 
191 IrIndex gen_ir_type_struct(StructDeclNode* node, CompilationContext* c, AllowHeaderOnly allow_header_only = AllowHeaderOnly.no)
192 	out(res; res.isTypeStruct, "Not a struct type")
193 {
194 	final switch(node.getPropertyState(NodeProperty.ir_body)) {
195 		case PropertyState.not_calculated: break;
196 		case PropertyState.calculating:
197 			if (allow_header_only) return node.irType;
198 			c.circular_dependency;
199 		case PropertyState.calculated: return node.irType;
200 	}
201 
202 	// dependencies
203 	gen_ir_header_struct(node, c);
204 
205 	c.begin_node_property_calculation(node, NodeProperty.ir_body);
206 	scope(exit) c.end_node_property_calculation(node, NodeProperty.ir_body);
207 
208 
209 	IrTypeStruct* structType = &c.types.get!IrTypeStruct(node.irType);
210 	IrTypeStructMember[] members = structType.members;
211 
212 	uint memberIndex;
213 	uint memberOffset;
214 	uint maxMemberSize;
215 	ubyte maxAlignmentPower = 0;
216 
217 	foreach(AstIndex memberAstIndex; node.declarations)
218 	{
219 		AstNode* member = c.getAstNode(memberAstIndex);
220 		if (member.astType == AstType.decl_var)
221 		{
222 			if (!member.isMember) continue; // skip static members
223 			auto var = member.as!(VariableDeclNode)(c);
224 			require_type_check(var.type, c, IsNested.no);
225 			IrIndex type = var.type.gen_ir_type(c);
226 			SizeAndAlignment memberInfo = c.types.typeSizeAndAlignment(type);
227 			maxAlignmentPower = max(maxAlignmentPower, memberInfo.alignmentPower);
228 			memberOffset = alignValue!uint(memberOffset, 1 << memberInfo.alignmentPower);
229 			if (node.isUnion)
230 				members[memberIndex++] = IrTypeStructMember(type, 0);
231 			else
232 				members[memberIndex++] = IrTypeStructMember(type, memberOffset);
233 			memberOffset += memberInfo.size;
234 			maxMemberSize = max(maxMemberSize, memberInfo.size);
235 
236 			if (var.type.isMetaType(c)) {
237 				node.flags |= StructFlags.isCtfeOnly;
238 			}
239 		} else if (member.astType == AstType.decl_function) {
240 			if (member.as!(FunctionDeclNode)(c).isCtfeOnly(c)) {
241 				node.flags |= StructFlags.isCtfeOnly;
242 			}
243 		}
244 	}
245 
246 	memberOffset = alignValue!uint(memberOffset, 1 << maxAlignmentPower);
247 
248 	if (node.isUnion)
249 		structType.sizealign = SizeAndAlignment(maxMemberSize, maxAlignmentPower);
250 	else
251 		structType.sizealign = SizeAndAlignment(memberOffset, maxAlignmentPower);
252 
253 	return node.irType;
254 }