optionals
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user