diff --git a/src/compiler.pr b/src/compiler.pr index 50547a42..ace7a2d3 100644 --- a/src/compiler.pr +++ b/src/compiler.pr @@ -4081,6 +4081,7 @@ type Member = struct { index: int tpe: &typechecking::Type is_embed: bool + value: &Value } // This list needs to be reversed to find the actual indices @@ -4113,11 +4114,24 @@ def resolve_member(vec: &Vector(Member), tpe: &typechecking::Type, name: Str) -> } } } + for var field in @tpe.const_fields { + if field.name == name { + let member = [ + value = field.value + ] !Member + vec.push(member) + return true + } + } + return false } def walk_MemberAccess_struct(node: &parser::Node, tpe: &typechecking::Type, member: &Member, value: Value, state: &State) -> Value { let loc = make_location(node, state) + if member.value { + return @member.value + } var member_type = member.tpe if member.tpe.kind == typechecking::TypeKind::BOX { @@ -9532,20 +9546,38 @@ def generate_vtable_function(function: &Function, tpe: &typechecking::Type, stat state.ret(NO_VALUE) } else { // Getter - var deref = state.extract_value(pointer(type_entry.tpe.tpe), reference, [1]) - var value = state.load(type_entry.tpe.tpe, deref) let name = function.unmangled - var findex: size_t = 0 - var ftpe: &typechecking::Type - for var field in @type_entry.tpe.tpe.fields { - if field.name == name { - findex = field.index - ftpe = field.tpe + + var const_field: typechecking::StructMember + var is_const = false + let const_fields = type_entry.tpe.tpe.const_fields + if const_fields { + for var field in @const_fields { + if field.name == name { + is_const = true + const_field = field + break + } } } - value = state.extract_value(ftpe, value, [findex !int]) - state.ret(value) + if is_const { + state.ret(@const_field.value) + } else { + var deref = state.extract_value(pointer(type_entry.tpe.tpe), reference, [1]) + var value = state.load(type_entry.tpe.tpe, deref) + var findex: size_t = 0 + var ftpe: &typechecking::Type + for var field in @type_entry.tpe.tpe.fields { + if field.name == name { + findex = field.index + ftpe = field.tpe + } + } + + value = state.extract_value(ftpe, value, [findex !int]) + state.ret(value) + } } } } diff --git a/src/parser.pr b/src/parser.pr index 0f1ef5f2..ec02c3f3 100644 --- a/src/parser.pr +++ b/src/parser.pr @@ -319,6 +319,8 @@ export type NodeIdDeclStruct = struct { is_bitfield: bool is_embed: bool bit_size: size_t + is_const: bool + value: &Node } export type NodeEnumT = struct { @@ -827,6 +829,7 @@ export def offset(node: &Node, changes: &[server::TextDocumentChangeEvent]) { case NodeKind::ID_DECL_STRUCT offset(node.value.id_decl_struct.ident, changes) offset(node.value.id_decl_struct.tpe, changes) + offset(node.value.id_decl_struct.value, changes) case NodeKind::ID_DECL_ENUM offset(node.value.id_decl_enum.ident, changes) offset(node.value.id_decl_enum.value, changes) @@ -961,6 +964,7 @@ export def clear(node: &Node) { case NodeKind::ID_DECL_STRUCT clear(node.value.id_decl_struct.ident) clear(node.value.id_decl_struct.tpe) + clear(node.value.id_decl_struct.value) case NodeKind::ID_DECL_ENUM clear(node.value.id_decl_enum.ident) clear(node.value.id_decl_enum.value) @@ -1156,6 +1160,8 @@ export def find(node: &Node, line: int, column: int) -> &Node { if n2 { return n2 } n2 = find(node.value.id_decl_struct.tpe, line, column) if n2 { return n2 } + n2 = find(node.value.id_decl_struct.value, line, column) + if n2 { return n2 } case NodeKind::ID_DECL_ENUM var n2 = find(node.value.id_decl_enum.ident, line, column) if n2 { return n2 } @@ -1380,6 +1386,7 @@ export def deep_copy_node(node: &Node, clear_svalue: bool = true) -> &Node { case NodeKind::ID_DECL_STRUCT copy.value.id_decl_struct.ident = deep_copy_node(node.value.id_decl_struct.ident, clear_svalue) copy.value.id_decl_struct.tpe = deep_copy_node(node.value.id_decl_struct.tpe, clear_svalue) + copy.value.id_decl_struct.value = deep_copy_node(node.value.id_decl_struct.value, clear_svalue) case NodeKind::ID_DECL_ENUM copy.value.id_decl_enum.ident = deep_copy_node(node.value.id_decl_enum.ident, clear_svalue) copy.value.id_decl_enum.value = deep_copy_node(node.value.id_decl_enum.value, clear_svalue) @@ -1957,13 +1964,23 @@ def parse_id_decl_struct(parse_state: &ParseState) -> &Node { token = peek(parse_state) var is_embed = false + var is_const = false var ident: &Node = null var tpe: &Node = null + var value: &Node = null if token.tpe == lexer::TokenType::COLON { pop(parse_state) skip_newline(parse_state) tpe = expect_type(parse_state) + } else if token.tpe == lexer::TokenType::K_CONST { + pop(parse_state) + is_const = true + ident = expect_identifier(parse_state) + expect(parse_state, lexer::TokenType::COLON, "Expected ':'") + tpe = expect_type(parse_state) + expect(parse_state, lexer::TokenType::OP_ASSIGN, "Expected '='") + value = expect_expression(parse_state) } else { if token.tpe == lexer::TokenType::OP_BAND { is_embed = true @@ -1992,9 +2009,11 @@ def parse_id_decl_struct(parse_state: &ParseState) -> &Node { tpe = tpe, is_bitfield = is_bitfield, is_embed = is_embed, - bit_size = bit_size + bit_size = bit_size, + is_const = is_const, + value = value ] !NodeIdDeclStruct - node._hash = combine_hashes(node.kind !uint64, is_bitfield !uint64, bit_size, hash(ident), hash(tpe)) + node._hash = combine_hashes(node.kind !uint64, is_bitfield !uint64, bit_size, is_embed !uint64, hash(ident), hash(tpe), hash(value)) } parse_t_term(parse_state) diff --git a/src/serialize.pr b/src/serialize.pr index 0e599ba1..b3f797cd 100644 --- a/src/serialize.pr +++ b/src/serialize.pr @@ -533,7 +533,7 @@ def deserialize_type(deserialize: &Deserialize, fp: File, tpe: &typechecking::Ty field_types.push(type_member) tpe.field_types = field_types } else { - typechecking::make_struct_type(fields, tpe) + typechecking::make_struct_type(fields, null, tpe) } tpe.size = size tpe.align = align diff --git a/src/typechecking.pr b/src/typechecking.pr index bafa520f..c2b291bf 100644 --- a/src/typechecking.pr +++ b/src/typechecking.pr @@ -84,7 +84,10 @@ export type StructMember = struct { is_bitfield: bool bit_size: size_t bit_offset: size_t + is_embed: bool + is_const: bool + value: &compiler::Value } export type StructuralTypeMember = struct { @@ -142,6 +145,8 @@ export type Type = struct { packed: bool // Fields for struct, array of StructMember fields: &[StructMember] + // Constants + const_fields: &[StructMember] // Vector of TypeMember field_types: &Vector(TypeMember) // Function and Tuple @@ -1285,6 +1290,11 @@ def has_function(entry: &TypeEntry, intf: &Type, mb: StructuralTypeMember, modul for var field in @tpe.fields { if field.name == mb.name and equals(mb.return_t(0), field.tpe) { return true } } + if tpe.const_fields { + for var field in @tpe.const_fields { + if field.name == mb.name and equals(mb.return_t(0), field.tpe) { return true } + } + } } } @@ -2242,7 +2252,7 @@ export def make_stub_type(ident: &parser::Node, state: &State, prefix: bool = tr return tpe } -export def make_union_type(fields: &[StructMember], current_type: &Type = null) -> &Type { +export def make_union_type(fields: &[StructMember], const_fields: &[StructMember], current_type: &Type = null) -> &Type { let tpe = make_type_raw(TypeKind::UNION) if not current_type else current_type let field_types = vector::make(TypeMember) @@ -2271,6 +2281,7 @@ export def make_union_type(fields: &[StructMember], current_type: &Type = null) tpe.size = size tpe.align = align tpe.fields = fields + tpe.const_fields = const_fields if const_fields else allocate_ref(StructMember, 0) tpe.field_types = field_types return tpe @@ -2291,7 +2302,7 @@ export def make_tuple_type(types: &Vector(&Type)) -> &Type { return tpe } -export def make_struct_type(fields: &[StructMember], current_type: &Type = null) -> &Type { +export def make_struct_type(fields: &[StructMember], const_fields: &[StructMember] = null, current_type: &Type = null) -> &Type { let struct_tpe = make_type_raw(TypeKind::STRUCT) if not current_type else current_type let field_types = vector::make(TypeMember) @@ -2388,6 +2399,7 @@ export def make_struct_type(fields: &[StructMember], current_type: &Type = null) struct_tpe.size = offset struct_tpe.align = align struct_tpe.fields = fields + struct_tpe.const_fields = const_fields if const_fields else allocate_ref(StructMember, 0) struct_tpe.field_types = field_types return struct_tpe @@ -2646,7 +2658,9 @@ export def do_type_lookup(node: &parser::Node, state: &State, current_type: &Typ } else if node.kind == parser::NodeKind::STRUCT_T or node.kind == parser::NodeKind::UNION_T { let length = vector::length(node.value.body) - let fields = allocate_ref(StructMember, length) + let fields = vector::make(StructMember) + let const_fields = vector::make(StructMember) + for var i in 0..length { let field = node.value.body(i) if not field { continue } @@ -2658,6 +2672,8 @@ export def do_type_lookup(node: &parser::Node, state: &State, current_type: &Typ var is_bitfield = false var is_embed = field.value.id_decl_struct.is_embed var bit_size = 0 !size_t + var is_const = field.value.id_decl_struct.is_const + var value: &compiler::Value var field_type: &Type = null if not is_embed and current_type and current_type.kind != TypeKind::STUB { field_type = current_type.fields(i).tpe } @@ -2675,26 +2691,35 @@ export def do_type_lookup(node: &parser::Node, state: &State, current_type: &Typ is_bitfield = true bit_size = field.value.id_decl_struct.bit_size } + if is_const { + // Load value for const + value = consteval::expr(field.value.id_decl_struct.value, state) + } } } else if (@field).kind == parser::NodeKind::STRUCT_T or (@field).kind == parser::NodeKind::UNION_T { field_tpe = type_lookup(field, state, field_type, false, cache) } - fields(i) = [ + let member = [ node = field, line = line, name = name, tpe = field_tpe, is_bitfield = is_bitfield, is_embed = is_embed, - bit_size = bit_size + bit_size = bit_size, + is_const = is_const, + value = value ] !StructMember + + if is_const { const_fields.push(member) } + else { fields.push(member) } } - var tpe = (make_struct_type(fields, current_type) + var tpe = (make_struct_type(fields.to_array(), const_fields.to_array(), current_type) if node.kind == parser::NodeKind::STRUCT_T - else make_union_type(fields, current_type)) + else make_union_type(fields.to_array(), const_fields.to_array(), current_type)) tpe.line = node.loc.line tpe.type_name = make_unique_name("", state) @@ -3081,7 +3106,7 @@ export def do_type_lookup(node: &parser::Node, state: &State, current_type: &Typ tpe.size = (ceil(offset / biggest_type.align !double) * biggest_type.align) !int tpe.align = util::lcm(align !int, biggest_type.align !int) - tpe._tpe = make_union_type(fields, tpe._tpe) + tpe._tpe = make_union_type(fields, null, tpe._tpe) tpe.variants = set::make(variants) tpe.line = node.loc.line @@ -5608,9 +5633,8 @@ export def lookup_struct_member(member: StructMember, resolved: &SSet = null) { } } -def resolve_member(fields: &[StructMember], name: String) -> &Type { - for var i in 0..fields.size { - let member = fields(i) +def resolve_member(tpe: &Type, name: String) -> &Type { + for var member in @tpe.fields { if member.name { if member.name == name { lookup_struct_member(member) @@ -5621,10 +5645,15 @@ def resolve_member(fields: &[StructMember], name: String) -> &Type { if member.is_embed and is_ref(member.tpe) { tpe = member.tpe.tpe } - let member = resolve_member(tpe.fields, name) + let member = resolve_member(tpe, name) if member { return member } } } + for var member in @tpe.const_fields { + if member.name == name { + return member.tpe + } + } return null } @@ -5684,7 +5713,7 @@ def walk_MemberAccess_aggregate(node: &parser::Node, ucs: bool, state: &State) - if (@tpe).kind == TypeKind::STRUCT or (@tpe).kind == TypeKind::UNION { let name = last_ident_to_str(right) - var rtpe = resolve_member((@tpe).fields, name) + var rtpe = resolve_member(tpe, name) if not rtpe { if ucs { if walk_MemberAccess_ucs(node, state) { return true }