c interop type foundation
This commit is contained in:
+603
-73
@@ -1,94 +1,619 @@
|
||||
package types
|
||||
|
||||
import "../target"
|
||||
import "core:fmt"
|
||||
import "core:mem"
|
||||
|
||||
Numeric_Category :: enum {
|
||||
// Type is a compact ID. Builtin scalar types occupy the stable low range;
|
||||
// recursive and nominal types are interned in Store starting at DYNAMIC_START.
|
||||
Type :: distinct u32
|
||||
|
||||
INVALID :: Type(0)
|
||||
VOID :: Type(1)
|
||||
INT :: Type(2)
|
||||
|
||||
I8 :: Type(3)
|
||||
I16 :: Type(4)
|
||||
I32 :: Type(5)
|
||||
I64 :: Type(6)
|
||||
U8 :: Type(7)
|
||||
U16 :: Type(8)
|
||||
U32 :: Type(9)
|
||||
U64 :: Type(10)
|
||||
ISIZE :: Type(11)
|
||||
USIZE :: Type(12)
|
||||
F32 :: Type(13)
|
||||
F64 :: Type(14)
|
||||
|
||||
C_CHAR :: Type(15)
|
||||
C_SCHAR :: Type(16)
|
||||
C_UCHAR :: Type(17)
|
||||
C_SHORT :: Type(18)
|
||||
C_USHORT :: Type(19)
|
||||
C_INT :: Type(20)
|
||||
C_UINT :: Type(21)
|
||||
C_LONG :: Type(22)
|
||||
C_ULONG :: Type(23)
|
||||
C_LONGLONG :: Type(24)
|
||||
C_ULONGLONG :: Type(25)
|
||||
C_FLOAT :: Type(26)
|
||||
C_DOUBLE :: Type(27)
|
||||
C_LONGDOUBLE :: Type(28)
|
||||
|
||||
DYNAMIC_START :: Type(64)
|
||||
|
||||
Numeric_Category :: enum u8 {
|
||||
None,
|
||||
Signed_Integer,
|
||||
Unsigned_Integer,
|
||||
Float,
|
||||
}
|
||||
|
||||
Kind :: enum {
|
||||
Kind :: enum u8 {
|
||||
Invalid,
|
||||
Void,
|
||||
Int_Constraint,
|
||||
Concrete,
|
||||
Scalar,
|
||||
Array,
|
||||
Pointer,
|
||||
Slice,
|
||||
Optional,
|
||||
Named,
|
||||
Struct,
|
||||
}
|
||||
|
||||
Type :: struct {
|
||||
kind: Kind,
|
||||
category: Numeric_Category,
|
||||
bits: int,
|
||||
Node :: struct {
|
||||
kind: Kind,
|
||||
child: Type,
|
||||
count: u64,
|
||||
sentinel: u64,
|
||||
field_start: u32,
|
||||
field_count: u32,
|
||||
pkg: u32,
|
||||
name: u32,
|
||||
qualifier: u32,
|
||||
file: u32,
|
||||
mutable: bool,
|
||||
many: bool,
|
||||
has_sentinel: bool,
|
||||
inferred_count: bool,
|
||||
c_layout: bool,
|
||||
opaque: bool,
|
||||
declared: bool,
|
||||
}
|
||||
|
||||
INVALID :: Type {
|
||||
kind = .Invalid,
|
||||
Field :: struct {
|
||||
name: u32,
|
||||
type: Type,
|
||||
}
|
||||
VOID :: Type {
|
||||
kind = .Void,
|
||||
|
||||
Store :: struct {
|
||||
nodes: [dynamic]Node,
|
||||
fields: [dynamic]Field,
|
||||
selected: target.Target,
|
||||
allocator: mem.Allocator,
|
||||
}
|
||||
INT :: Type {
|
||||
kind = .Int_Constraint,
|
||||
category = .Signed_Integer,
|
||||
|
||||
init_store :: proc(allocator := context.allocator) -> Store {
|
||||
store: Store
|
||||
store.nodes.allocator = allocator
|
||||
store.fields.allocator = allocator
|
||||
store.selected = target.DEFAULT
|
||||
store.allocator = allocator
|
||||
return store
|
||||
}
|
||||
I8 :: Type {
|
||||
kind = .Concrete,
|
||||
category = .Signed_Integer,
|
||||
bits = 8,
|
||||
|
||||
destroy_store :: proc(store: ^Store) {
|
||||
delete(store.nodes)
|
||||
delete(store.fields)
|
||||
}
|
||||
I16 :: Type {
|
||||
kind = .Concrete,
|
||||
category = .Signed_Integer,
|
||||
bits = 16,
|
||||
|
||||
clone_store :: proc(source: ^Store, allocator := context.allocator) -> Store {
|
||||
store := init_store(allocator)
|
||||
append(&store.nodes, ..source.nodes[:])
|
||||
append(&store.fields, ..source.fields[:])
|
||||
store.selected = source.selected
|
||||
return store
|
||||
}
|
||||
I32 :: Type {
|
||||
kind = .Concrete,
|
||||
category = .Signed_Integer,
|
||||
bits = 32,
|
||||
|
||||
intern :: proc(store: ^Store, candidate: Node) -> Type {
|
||||
if candidate.kind != .Struct && candidate.kind != .Named {
|
||||
for existing, index in store.nodes {
|
||||
if existing == candidate {
|
||||
return DYNAMIC_START+Type(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
id := DYNAMIC_START+Type(len(store.nodes))
|
||||
append(&store.nodes, candidate)
|
||||
return id
|
||||
}
|
||||
I64 :: Type {
|
||||
kind = .Concrete,
|
||||
category = .Signed_Integer,
|
||||
bits = 64,
|
||||
|
||||
named :: proc(store: ^Store, pkg, name: u32, qualifier: u32 = 0, file: u32 = 0xffff_ffff) -> Type {
|
||||
normalized_file := file if qualifier != 0 else u32(0)
|
||||
for existing, index in store.nodes {
|
||||
if (existing.kind == .Named || existing.kind == .Struct) &&
|
||||
existing.pkg == pkg && existing.name == name && existing.qualifier == qualifier &&
|
||||
existing.file == normalized_file {
|
||||
return DYNAMIC_START+Type(index)
|
||||
}
|
||||
}
|
||||
return intern(store, Node{kind=.Named, pkg=pkg, name=name, qualifier=qualifier, file=normalized_file})
|
||||
}
|
||||
|
||||
find_named :: proc(store: ^Store, pkg, name: u32, qualifier: u32 = 0) -> Type {
|
||||
for existing, index in store.nodes {
|
||||
if (existing.kind == .Named || existing.kind == .Struct) &&
|
||||
existing.pkg == pkg && existing.name == name && existing.qualifier == qualifier {
|
||||
return DYNAMIC_START+Type(index)
|
||||
}
|
||||
}
|
||||
return INVALID
|
||||
}
|
||||
|
||||
define_struct :: proc(store: ^Store, id: Type, fields: []Field, c_layout, opaque: bool) -> bool {
|
||||
existing, ok := node(store, id)
|
||||
if !ok || (existing.kind != .Named && existing.kind != .Struct) || existing.declared {
|
||||
return false
|
||||
}
|
||||
index := int(id-DYNAMIC_START)
|
||||
store.nodes[index].kind = .Struct
|
||||
store.nodes[index].c_layout = c_layout
|
||||
store.nodes[index].opaque = opaque
|
||||
store.nodes[index].declared = true
|
||||
store.nodes[index].field_start = u32(len(store.fields))
|
||||
store.nodes[index].field_count = u32(len(fields))
|
||||
append(&store.fields, ..fields)
|
||||
return true
|
||||
}
|
||||
|
||||
fields_for :: proc(store: ^Store, value: Type) -> []Field {
|
||||
item, ok := node(store, value)
|
||||
if !ok || item.kind != .Struct {
|
||||
return nil
|
||||
}
|
||||
start := int(item.field_start)
|
||||
end := start+int(item.field_count)
|
||||
if start < 0 || end > len(store.fields) {
|
||||
return nil
|
||||
}
|
||||
return store.fields[start:end]
|
||||
}
|
||||
|
||||
kind :: proc(value: Type, store: ^Store = nil) -> Kind {
|
||||
switch value {
|
||||
case INVALID:
|
||||
return .Invalid
|
||||
case VOID:
|
||||
return .Void
|
||||
case INT:
|
||||
return .Int_Constraint
|
||||
}
|
||||
if value >= I8 && value <= C_LONGDOUBLE {
|
||||
return .Scalar
|
||||
}
|
||||
if store != nil && value >= DYNAMIC_START {
|
||||
index := int(value-DYNAMIC_START)
|
||||
if index >= 0 && index < len(store.nodes) {
|
||||
return store.nodes[index].kind
|
||||
}
|
||||
}
|
||||
return .Invalid
|
||||
}
|
||||
|
||||
node :: proc(store: ^Store, value: Type) -> (Node, bool) {
|
||||
if store == nil || value < DYNAMIC_START {
|
||||
return {}, false
|
||||
}
|
||||
index := int(value-DYNAMIC_START)
|
||||
if index < 0 || index >= len(store.nodes) {
|
||||
return {}, false
|
||||
}
|
||||
return store.nodes[index], true
|
||||
}
|
||||
|
||||
is_valid :: proc(value: Type) -> bool {
|
||||
return value.kind != .Invalid
|
||||
return value != INVALID
|
||||
}
|
||||
|
||||
is_void :: proc(value: Type) -> bool {
|
||||
return value == VOID
|
||||
}
|
||||
|
||||
is_constraint :: proc(value: Type) -> bool {
|
||||
return value == INT
|
||||
}
|
||||
|
||||
is_c :: proc(value: Type) -> bool {
|
||||
return value >= C_CHAR && value <= C_LONGDOUBLE
|
||||
}
|
||||
|
||||
as_c_primitive :: proc(value: Type) -> (target.C_Primitive, bool) {
|
||||
switch value {
|
||||
case C_CHAR: return .Char, true
|
||||
case C_SCHAR: return .Schar, true
|
||||
case C_UCHAR: return .Uchar, true
|
||||
case C_SHORT: return .Short, true
|
||||
case C_USHORT: return .Ushort, true
|
||||
case C_INT: return .Int, true
|
||||
case C_UINT: return .Uint, true
|
||||
case C_LONG: return .Long, true
|
||||
case C_ULONG: return .Ulong, true
|
||||
case C_LONGLONG: return .Longlong, true
|
||||
case C_ULONGLONG: return .Ulonglong, true
|
||||
case C_FLOAT: return .Float, true
|
||||
case C_DOUBLE: return .Double, true
|
||||
case C_LONGDOUBLE: return .Longdouble, true
|
||||
}
|
||||
return {}, false
|
||||
}
|
||||
|
||||
category :: proc(value: Type, selected := target.DEFAULT) -> Numeric_Category {
|
||||
switch value {
|
||||
case I8, I16, I32, I64, ISIZE:
|
||||
return .Signed_Integer
|
||||
case U8, U16, U32, U64, USIZE:
|
||||
return .Unsigned_Integer
|
||||
case F32, F64:
|
||||
return .Float
|
||||
case:
|
||||
primitive, ok := as_c_primitive(value)
|
||||
if !ok {
|
||||
return .None
|
||||
}
|
||||
switch target.c_primitive_layout(selected, primitive).kind {
|
||||
case .Signed_Integer: return .Signed_Integer
|
||||
case .Unsigned_Integer: return .Unsigned_Integer
|
||||
case .Float: return .Float
|
||||
}
|
||||
}
|
||||
return .None
|
||||
}
|
||||
|
||||
bits :: proc(value: Type, selected := target.DEFAULT) -> int {
|
||||
switch value {
|
||||
case I8, U8:
|
||||
return 8
|
||||
case I16, U16:
|
||||
return 16
|
||||
case I32, U32, F32:
|
||||
return 32
|
||||
case I64, U64, F64:
|
||||
return 64
|
||||
case ISIZE, USIZE:
|
||||
return target.pointer_bits(selected)
|
||||
case:
|
||||
primitive, ok := as_c_primitive(value)
|
||||
return target.c_primitive_layout(selected, primitive).bits if ok else 0
|
||||
}
|
||||
}
|
||||
|
||||
alignment :: proc(value: Type, selected := target.DEFAULT) -> int {
|
||||
if primitive, ok := as_c_primitive(value); ok {
|
||||
return target.c_primitive_layout(selected, primitive).alignment
|
||||
}
|
||||
width := bits(value, selected)/8
|
||||
return min(max(width, 1), 8)
|
||||
}
|
||||
|
||||
representation :: proc(value: Type, selected := target.DEFAULT) -> Type {
|
||||
if value == ISIZE {
|
||||
return I64
|
||||
}
|
||||
if value == USIZE {
|
||||
return U64
|
||||
}
|
||||
primitive, ok := as_c_primitive(value)
|
||||
if !ok {
|
||||
return value
|
||||
}
|
||||
layout := target.c_primitive_layout(selected, primitive)
|
||||
if layout.kind == .Float {
|
||||
return F32 if layout.bits == 32 else F64
|
||||
}
|
||||
if layout.kind == .Signed_Integer {
|
||||
switch layout.bits {
|
||||
case 8: return I8
|
||||
case 16: return I16
|
||||
case 32: return I32
|
||||
case: return I64
|
||||
}
|
||||
}
|
||||
switch layout.bits {
|
||||
case 8: return U8
|
||||
case 16: return U16
|
||||
case 32: return U32
|
||||
case: return U64
|
||||
}
|
||||
}
|
||||
|
||||
is_concrete_scalar :: proc(value: Type) -> bool {
|
||||
return kind(value) == .Scalar
|
||||
}
|
||||
|
||||
is_concrete :: proc(value: Type, store: ^Store = nil) -> bool {
|
||||
value_kind := kind(value, store)
|
||||
if value_kind == .Scalar || value_kind == .Array || value_kind == .Pointer ||
|
||||
value_kind == .Slice || value_kind == .Optional {
|
||||
return true
|
||||
}
|
||||
if value_kind == .Struct {
|
||||
item, ok := node(store, value)
|
||||
return ok && item.declared
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
is_pointer :: proc(value: Type, store: ^Store) -> bool {
|
||||
return kind(value, store) == .Pointer
|
||||
}
|
||||
|
||||
is_array :: proc(value: Type, store: ^Store) -> bool {
|
||||
return kind(value, store) == .Array
|
||||
}
|
||||
|
||||
is_slice :: proc(value: Type, store: ^Store) -> bool {
|
||||
return kind(value, store) == .Slice
|
||||
}
|
||||
|
||||
is_optional :: proc(value: Type, store: ^Store) -> bool {
|
||||
return kind(value, store) == .Optional
|
||||
}
|
||||
|
||||
is_struct :: proc(value: Type, store: ^Store) -> bool {
|
||||
return kind(value, store) == .Struct
|
||||
}
|
||||
|
||||
is_optional_pointer :: proc(value: Type, store: ^Store) -> bool {
|
||||
item, ok := node(store, value)
|
||||
return ok && item.kind == .Optional && is_pointer(item.child, store)
|
||||
}
|
||||
|
||||
is_runtime_value :: proc(value: Type, store: ^Store) -> bool {
|
||||
value_kind := kind(value, store)
|
||||
if value_kind == .Scalar || value_kind == .Pointer {
|
||||
return true
|
||||
}
|
||||
if value_kind == .Slice || value_kind == .Array || value_kind == .Optional {
|
||||
return !contains_c_struct_by_value(value, store)
|
||||
}
|
||||
if value_kind == .Struct {
|
||||
item, ok := node(store, value)
|
||||
return ok && item.declared && !item.opaque && !contains_c_struct_by_value(value, store)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
contains_c_struct_by_value :: proc(value: Type, store: ^Store, depth := 0) -> bool {
|
||||
if depth > 256 {
|
||||
return true
|
||||
}
|
||||
item, ok := node(store, value)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if item.kind == .Pointer {
|
||||
return false
|
||||
}
|
||||
if item.kind == .Struct {
|
||||
if item.c_layout {
|
||||
return true
|
||||
}
|
||||
for field in fields_for(store, value) {
|
||||
if contains_c_struct_by_value(field.type, store, depth+1) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
if item.kind == .Array || item.kind == .Slice || item.kind == .Optional {
|
||||
return contains_c_struct_by_value(item.child, store, depth+1)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
is_c_signature_type :: proc(value: Type, store: ^Store, allow_void := false) -> bool {
|
||||
if allow_void && is_void(value) {
|
||||
return true
|
||||
}
|
||||
return is_concrete_scalar(value) || is_pointer(value, store) || is_optional_pointer(value, store)
|
||||
}
|
||||
|
||||
child_type :: proc(value: Type, store: ^Store) -> Type {
|
||||
item, ok := node(store, value)
|
||||
return item.child if ok else INVALID
|
||||
}
|
||||
|
||||
logical_count :: proc(value: Type, store: ^Store) -> u64 {
|
||||
item, ok := node(store, value)
|
||||
return item.count if ok else 0
|
||||
}
|
||||
|
||||
physical_count :: proc(value: Type, store: ^Store) -> u64 {
|
||||
item, ok := node(store, value)
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
return item.count + (u64(1) if item.has_sentinel else u64(0))
|
||||
}
|
||||
|
||||
is_mutable :: proc(value: Type, store: ^Store) -> bool {
|
||||
item, ok := node(store, value)
|
||||
return ok && item.mutable
|
||||
}
|
||||
|
||||
is_many_pointer :: proc(value: Type, store: ^Store) -> bool {
|
||||
item, ok := node(store, value)
|
||||
return ok && item.kind == .Pointer && item.many
|
||||
}
|
||||
|
||||
is_c_struct :: proc(value: Type, store: ^Store) -> bool {
|
||||
item, ok := node(store, value)
|
||||
return ok && item.kind == .Struct && item.c_layout
|
||||
}
|
||||
|
||||
pointer :: proc(store: ^Store, child: Type, mutable, many: bool) -> Type {
|
||||
return intern(store, Node{kind=.Pointer, child=child, mutable=mutable, many=many})
|
||||
}
|
||||
|
||||
slice :: proc(store: ^Store, child: Type, mutable: bool, has_sentinel := false, sentinel: u64 = 0) -> Type {
|
||||
return intern(store, Node{
|
||||
kind=.Slice,
|
||||
child=child,
|
||||
mutable=mutable,
|
||||
has_sentinel=has_sentinel,
|
||||
sentinel=sentinel,
|
||||
})
|
||||
}
|
||||
|
||||
array :: proc(
|
||||
store: ^Store,
|
||||
child: Type,
|
||||
count: u64,
|
||||
mutable: bool,
|
||||
has_sentinel := false,
|
||||
sentinel: u64 = 0,
|
||||
) -> Type {
|
||||
return intern(store, Node{
|
||||
kind=.Array,
|
||||
child=child,
|
||||
count=count,
|
||||
mutable=mutable,
|
||||
has_sentinel=has_sentinel,
|
||||
sentinel=sentinel,
|
||||
})
|
||||
}
|
||||
|
||||
optional :: proc(store: ^Store, child: Type) -> Type {
|
||||
return intern(store, Node{kind=.Optional, child=child})
|
||||
}
|
||||
|
||||
with_array_count :: proc(store: ^Store, value: Type, count: u64) -> Type {
|
||||
item, ok := node(store, value)
|
||||
if !ok || item.kind != .Array {
|
||||
return value
|
||||
}
|
||||
item.count = count
|
||||
item.inferred_count = false
|
||||
return intern(store, item)
|
||||
}
|
||||
|
||||
can_weaken_pointer :: proc(from, to: Type, store: ^Store) -> bool {
|
||||
from_node, from_ok := node(store, from)
|
||||
to_node, to_ok := node(store, to)
|
||||
return from_ok && to_ok &&
|
||||
from_node.kind == .Pointer && to_node.kind == .Pointer &&
|
||||
from_node.child == to_node.child && from_node.many == to_node.many &&
|
||||
from_node.mutable && !to_node.mutable
|
||||
}
|
||||
|
||||
is_opaque_struct :: proc(value: Type, store: ^Store) -> bool {
|
||||
item, ok := node(store, value)
|
||||
return ok && item.kind == .Struct && item.opaque
|
||||
}
|
||||
|
||||
size :: proc(value: Type, store: ^Store, selected := target.DEFAULT) -> u64 {
|
||||
#partial switch kind(value, store) {
|
||||
case .Scalar:
|
||||
return u64(bits(value, selected)/8)
|
||||
case .Pointer:
|
||||
return u64(target.pointer_bits(selected)/8)
|
||||
case .Slice:
|
||||
return u64(target.pointer_bits(selected)/8*2)
|
||||
case .Array:
|
||||
item, _ := node(store, value)
|
||||
return physical_count(value, store)*size(item.child, store, selected)
|
||||
case .Optional:
|
||||
item, _ := node(store, value)
|
||||
if is_pointer(item.child, store) {
|
||||
return u64(target.pointer_bits(selected)/8)
|
||||
}
|
||||
child_size := size(item.child, store, selected)
|
||||
child_align := u64(alignment_of(item.child, store, selected))
|
||||
return ((child_size+1+child_align-1)/child_align)*child_align
|
||||
case .Struct:
|
||||
offset: u64
|
||||
max_align: u64 = 1
|
||||
for field in fields_for(store, value) {
|
||||
field_align := u64(alignment_of(field.type, store, selected))
|
||||
offset = (offset+field_align-1)/field_align*field_align
|
||||
offset += size(field.type, store, selected)
|
||||
max_align = max(max_align, field_align)
|
||||
}
|
||||
return (offset+max_align-1)/max_align*max_align
|
||||
case:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
alignment_of :: proc(value: Type, store: ^Store, selected := target.DEFAULT) -> int {
|
||||
#partial switch kind(value, store) {
|
||||
case .Scalar:
|
||||
return alignment(value, selected)
|
||||
case .Pointer, .Slice:
|
||||
return target.pointer_bits(selected)/8
|
||||
case .Array, .Optional:
|
||||
return alignment_of(child_type(value, store), store, selected)
|
||||
case .Struct:
|
||||
result := 1
|
||||
for field in fields_for(store, value) {
|
||||
result = max(result, alignment_of(field.type, store, selected))
|
||||
}
|
||||
return result
|
||||
case:
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
is_concrete_integer :: proc(value: Type) -> bool {
|
||||
return(
|
||||
value.kind == .Concrete &&
|
||||
(value.category == .Signed_Integer || value.category == .Unsigned_Integer) \
|
||||
)
|
||||
category := category(value)
|
||||
return kind(value) == .Scalar &&
|
||||
(category == .Signed_Integer || category == .Unsigned_Integer)
|
||||
}
|
||||
|
||||
is_signed :: proc(value: Type) -> bool {
|
||||
return value.kind == .Concrete && value.category == .Signed_Integer
|
||||
is_float :: proc(value: Type, selected := target.DEFAULT) -> bool {
|
||||
return kind(value) == .Scalar && category(value, selected) == .Float
|
||||
}
|
||||
|
||||
is_signed :: proc(value: Type, selected := target.DEFAULT) -> bool {
|
||||
return kind(value) == .Scalar && category(value, selected) == .Signed_Integer
|
||||
}
|
||||
|
||||
is_unsigned :: proc(value: Type, selected := target.DEFAULT) -> bool {
|
||||
return kind(value) == .Scalar && category(value, selected) == .Unsigned_Integer
|
||||
}
|
||||
|
||||
equal :: proc(a, b: Type) -> bool {
|
||||
return a.kind == b.kind && a.category == b.category && a.bits == b.bits
|
||||
return a == b
|
||||
}
|
||||
|
||||
same_numeric_family :: proc(a, b: Type) -> bool {
|
||||
if category(a) != category(b) {
|
||||
return false
|
||||
}
|
||||
// C primitives are intentionally distinct semantic types. Exact-width
|
||||
// Brolang scalars may widen only to other Brolang scalars.
|
||||
return !is_c(a) && !is_c(b)
|
||||
}
|
||||
|
||||
can_widen :: proc(from, to: Type) -> bool {
|
||||
if equal(from, to) {
|
||||
return true
|
||||
}
|
||||
return(
|
||||
from.kind == .Concrete &&
|
||||
to.kind == .Concrete &&
|
||||
from.category == to.category &&
|
||||
from.bits < to.bits \
|
||||
)
|
||||
return is_concrete_scalar(from) &&
|
||||
is_concrete_scalar(to) &&
|
||||
same_numeric_family(from, to) &&
|
||||
bits(from) < bits(to)
|
||||
}
|
||||
|
||||
widest :: proc(a, b: Type) -> Type {
|
||||
if a.kind != .Concrete || b.kind != .Concrete || a.category != b.category {
|
||||
if equal(a, b) && is_concrete_scalar(a) {
|
||||
return a
|
||||
}
|
||||
if !is_concrete_scalar(a) || !is_concrete_scalar(b) || !same_numeric_family(a, b) {
|
||||
return INVALID
|
||||
}
|
||||
if a.bits >= b.bits {
|
||||
if bits(a) >= bits(b) {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
@@ -108,32 +633,37 @@ smallest_signed_for_literal :: proc(value: i64) -> Type {
|
||||
}
|
||||
|
||||
name :: proc(value: Type) -> string {
|
||||
switch value.kind {
|
||||
case .Invalid:
|
||||
return "<invalid>"
|
||||
case .Void:
|
||||
return "void"
|
||||
case .Int_Constraint:
|
||||
return "int"
|
||||
case .Concrete:
|
||||
switch value.category {
|
||||
case .Signed_Integer:
|
||||
switch value.bits {
|
||||
case 8:
|
||||
return "i8"
|
||||
case 16:
|
||||
return "i16"
|
||||
case 32:
|
||||
return "i32"
|
||||
case 64:
|
||||
return "i64"
|
||||
}
|
||||
case .Unsigned_Integer:
|
||||
return fmt.tprintf("u%d", value.bits)
|
||||
case .Float:
|
||||
return fmt.tprintf("f%d", value.bits)
|
||||
case .None:
|
||||
}
|
||||
switch value {
|
||||
case INVALID: return "<invalid>"
|
||||
case VOID: return "void"
|
||||
case INT: return "int"
|
||||
case I8: return "i8"
|
||||
case I16: return "i16"
|
||||
case I32: return "i32"
|
||||
case I64: return "i64"
|
||||
case U8: return "u8"
|
||||
case U16: return "u16"
|
||||
case U32: return "u32"
|
||||
case U64: return "u64"
|
||||
case ISIZE: return "isize"
|
||||
case USIZE: return "usize"
|
||||
case F32: return "f32"
|
||||
case F64: return "f64"
|
||||
case C_CHAR: return "c_char"
|
||||
case C_SCHAR: return "c_schar"
|
||||
case C_UCHAR: return "c_uchar"
|
||||
case C_SHORT: return "c_short"
|
||||
case C_USHORT: return "c_ushort"
|
||||
case C_INT: return "c_int"
|
||||
case C_UINT: return "c_uint"
|
||||
case C_LONG: return "c_long"
|
||||
case C_ULONG: return "c_ulong"
|
||||
case C_LONGLONG: return "c_longlong"
|
||||
case C_ULONGLONG: return "c_ulonglong"
|
||||
case C_FLOAT: return "c_float"
|
||||
case C_DOUBLE: return "c_double"
|
||||
case C_LONGDOUBLE: return "c_longdouble"
|
||||
case:
|
||||
return fmt.tprintf("<type %d>", value)
|
||||
}
|
||||
return "<invalid>"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user