optionals

This commit is contained in:
agra
2026-02-22 22:16:30 +02:00
parent d3e574eae5
commit 1cc67f9b5a
17 changed files with 1952 additions and 32 deletions

View File

@@ -23,6 +23,7 @@ pub const Type = union(enum) {
vector_type: VectorTypeInfo,
function_type: FunctionTypeInfo,
any_type,
optional_type: OptionalTypeInfo,
meta_type: MetaTypeInfo,
tuple_type: TupleTypeInfo,
@@ -53,6 +54,10 @@ pub const Type = union(enum) {
length: u32,
};
pub const OptionalTypeInfo = struct {
child_name: []const u8,
};
pub const MetaTypeInfo = struct {
name: []const u8,
};
@@ -90,6 +95,7 @@ pub const Type = union(enum) {
}
return info.return_type.eql(o.return_type.*);
},
.optional_type => |info| std.mem.eql(u8, info.child_name, other.optional_type.child_name),
.meta_type => |info| std.mem.eql(u8, info.name, other.meta_type.name),
.tuple_type => |info| {
const o = other.tuple_type;
@@ -141,6 +147,7 @@ pub const Type = union(enum) {
if (std.mem.eql(u8, name, "f64")) return .f64;
return null;
},
'?' => if (name.len >= 2) .{ .optional_type = .{ .child_name = name[1..] } } else null,
'A' => if (std.mem.eql(u8, name, "Any")) .any_type else null,
'v' => if (std.mem.eql(u8, name, "void")) .void_type else null,
'[' => {
@@ -212,6 +219,20 @@ pub const Type = union(enum) {
};
}
pub fn isOptional(self: Type) bool {
return switch (self) {
.optional_type => true,
else => false,
};
}
pub fn optionalChild(self: Type) ?[]const u8 {
return switch (self) {
.optional_type => |info| info.child_name,
else => null,
};
}
pub fn isAny(self: Type) bool {
return switch (self) {
.any_type => true,
@@ -382,6 +403,30 @@ pub const Type = union(enum) {
return true;
}
// T → ?T: any type implicitly wraps into its optional
if (target.isOptional()) {
const child_name = target.optional_type.child_name;
// null → ?T
if (self.isPointer() and std.mem.eql(u8, self.pointer_type.pointee_name, "void")) return true;
// ?T → ?U when T → U
if (self.isOptional()) {
const self_child = fromName(self.optional_type.child_name) orelse return false;
const target_child = fromName(child_name) orelse return false;
return self_child.isImplicitlyConvertibleTo(target_child);
}
// T → ?T: check if self matches the child type
if (fromName(child_name)) |child_type| {
return self.eql(child_type) or self.isImplicitlyConvertibleTo(child_type);
}
// Non-primitive child (struct/enum name): compare by name
return switch (self) {
.struct_type => |n| std.mem.eql(u8, n, child_name),
.enum_type => |n| std.mem.eql(u8, n, child_name),
.union_type => |n| std.mem.eql(u8, n, child_name),
else => false,
};
}
const src_float = self.isFloat();
const dst_float = target.isFloat();
const src_int = self.isInt();
@@ -461,6 +506,7 @@ pub const Type = union(enum) {
}
return try buf.toOwnedSlice(allocator);
},
.optional_type => |info| return fmtAlloc(allocator, "?{s}", .{info.child_name}),
.meta_type => |info| info.name,
.tuple_type => |info| {
var buf = std.ArrayList(u8).empty;
@@ -531,6 +577,9 @@ pub const Type = union(enum) {
return Type.s(capped);
}
// Optional types: widen inner types
if (a.isOptional() and b.isOptional()) return a;
// Pointer types: both are pointers → return first (all are opaque ptr at LLVM level)
if ((a.isPointer() or a.isManyPointer()) and (b.isPointer() or b.isManyPointer())) return a;