1 /// Copyright: Copyright (c) 2020 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.attribute;
5 
6 import vox.all;
7 
8 /// If AstNode has any attributes attached at parse time then AttributeInfo is allocated
9 /// in memory before such AstNode and node receives AstFlags.hasAttributes flag.
10 /// All broadcasted nodes are located before direct attributes.
11 struct AttributeInfo
12 {
13 	AstNodes attributes;
14 	uint flags;
15 
16 	// Returns pointer to the node this struct is attached to
17 	AstNode* node() return {
18 		return cast(AstNode*)(cast(void*)(&this) + AttributeInfo.sizeof);
19 	}
20 
21 	bool isExternal() { return cast(bool)(flags & AttribInfoFlags.isExternal); }
22 }
23 
24 void print_attributes(AttributeInfo* attribs, ref AstPrintState state) {
25 	print_ast(attribs.attributes, state);
26 }
27 
28 void post_clone_attributes(AttributeInfo* attribs, ref CloneState state) {
29 	state.fixAstNodes(attribs.attributes);
30 }
31 
32 void name_register_nested_attributes(AttributeInfo* attribs, ref NameRegisterState state) {
33 	require_name_register(attribs.attributes, state);
34 }
35 
36 void name_resolve_attributes(AttributeInfo* attribs, ref NameResolveState state) {
37 	require_name_resolve(attribs.attributes, state);
38 }
39 
40 void type_check_attributes(AttributeInfo* attribs, ref TypeCheckState state) {
41 	require_type_check(attribs.attributes, state);
42 }
43 
44 enum AttribInfoFlags : uint {
45 	isExternal = 1 <<  0, // set if AttributeInfo contains some @extern attribute
46 }
47 
48 enum BuiltinFlagAttrib : ushort {
49 	isStatic = 1 <<  0, // @static
50 }
51 
52 enum BuiltinAttribSubType : ubyte {
53 	extern_syscall,
54 	extern_module
55 }
56 
57 immutable uint[BuiltinAttribSubType.max+1] builtinAttribFlags = [
58 	AttribInfoFlags.isExternal, // extern_syscall
59 	AttribInfoFlags.isExternal, // extern_module
60 ];
61 
62 uint calcAttribFlags(AstIndex attrib, CompilationContext* c) {
63 	auto attribNode = attrib.get_node(c);
64 	if (attribNode.astType != AstType.decl_builtin_attribute) return 0;
65 	return builtinAttribFlags[attribNode.subType];
66 }
67 
68 enum AnyAttributeFlags : ushort
69 {
70 	// Set if attribute was applied to multiple nodes at once via `@attr{}` or `@attr:` syntax
71 	// If not set, attribute was applied directly
72 	isBroadcasted = AstFlags.userFlag << 0,
73 }
74 
75 @(AstType.decl_builtin_attribute)
76 struct BuiltinAttribNode
77 {
78 	mixin AstNodeData!(AstType.decl_builtin_attribute, 0, AstNodeState.type_check_done);
79 	uint data;
80 
81 	bool isBroadcasted() { return cast(bool)(flags & AnyAttributeFlags.isBroadcasted); }
82 
83 	this(TokenIndex loc, BuiltinAttribSubType subType, uint data)
84 	{
85 		this.loc = loc;
86 		this.astType = AstType.decl_builtin_attribute;
87 		this.flags = 0;
88 		if (subType == BuiltinAttribSubType.extern_syscall)
89 			this.state = AstNodeState.name_resolve_done;
90 		else
91 			this.state = AstNodeState.type_check_done;
92 		this.subType = subType;
93 		this.data = data;
94 	}
95 }
96 
97 void print_builtin_attribute(BuiltinAttribNode* node, ref AstPrintState state)
98 {
99 	final switch(cast(BuiltinAttribSubType)node.subType) {
100 		case BuiltinAttribSubType.extern_syscall:
101 			state.print("ATTRIB @extern(syscall, ", node.data, ")", AttributeFlagPrinter(node.flags));
102 			break;
103 		case BuiltinAttribSubType.extern_module:
104 			state.print("ATTRIB @extern(module, ", state.context.idString(Identifier(node.data)), ")", AttributeFlagPrinter(node.flags));
105 			break;
106 	}
107 }
108 
109 struct AttributeFlagPrinter {
110 	ushort flags;
111 	void toString(scope void delegate(const(char)[]) sink) {
112 		if (flags == 0) return;
113 		if (flags & AnyAttributeFlags.isBroadcasted) sink(" /broadcasted");
114 	}
115 }
116 
117 void type_check_builtin_attribute(BuiltinAttribNode* node, ref TypeCheckState state)
118 {
119 	final switch(node.getPropertyState(NodeProperty.type_check)) {
120 		case PropertyState.not_calculated: break;
121 		case PropertyState.calculating: state.context.circular_dependency;
122 		case PropertyState.calculated: return;
123 	}
124 
125 	state.context.begin_node_property_calculation(node, NodeProperty.type_check);
126 	scope(exit) state.context.end_node_property_calculation(node, NodeProperty.type_check);
127 
128 	if (node.subType == BuiltinAttribSubType.extern_syscall && node.isBroadcasted)
129 	{
130 		// forbid broadcasting @extern(syscall) as it makes no sense, since it carries data, which should be different for each function
131 		state.context.error(node.loc, "Broadcasting @extern(syscall) attribute is forbidden");
132 	}
133 }