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 }