Merge pull request #126 from birth-software/polymorphic-type-sketch
First polymorphic type implementation
This commit is contained in:
commit
2343a1ff4e
@ -3222,6 +3222,7 @@ pub const Type = union(enum) {
|
||||
void,
|
||||
noreturn,
|
||||
type,
|
||||
polymorphic: *Debug.Declaration.Global,
|
||||
@"struct": Struct.Index,
|
||||
function: Function.Prototype.Index,
|
||||
integer: Type.Integer,
|
||||
@ -3837,10 +3838,6 @@ pub const Struct = struct {
|
||||
|
||||
pub const Kind = union(enum) {
|
||||
@"struct": Struct.Descriptor,
|
||||
@"union": struct {
|
||||
scope: Debug.Scope.Global,
|
||||
fields: UnpinnedArray(Struct.Field.Index) = .{},
|
||||
},
|
||||
error_union: struct {
|
||||
@"error": Type.Index,
|
||||
type: Type.Index,
|
||||
@ -3864,11 +3861,21 @@ pub const Struct = struct {
|
||||
};
|
||||
pub const Options = struct {
|
||||
sliceable: ?Sliceable = null,
|
||||
polymorphism_kind: PolymorphismKind = .none,
|
||||
pub const Id = enum {
|
||||
sliceable,
|
||||
};
|
||||
};
|
||||
|
||||
pub const PolymorphismKind = union(enum) {
|
||||
none,
|
||||
declaration: []const Type.Index,
|
||||
implementation: struct {
|
||||
types: []const Type.Index,
|
||||
original: Type.Index,
|
||||
},
|
||||
};
|
||||
|
||||
pub const Sliceable = struct {
|
||||
pointer: u32,
|
||||
length: u32,
|
||||
@ -4509,7 +4516,7 @@ pub const Builder = struct {
|
||||
// TODO: depends? .right is not always the right choice
|
||||
const v = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, argument_node_index, .right);
|
||||
|
||||
const cast_id: Instruction.Cast.Id = switch (type_expect) {
|
||||
switch (type_expect) {
|
||||
.type => |type_index| {
|
||||
const cast_id = try builder.resolveCast(unit, context, type_index, v);
|
||||
switch (cast_id) {
|
||||
@ -4534,9 +4541,7 @@ pub const Builder = struct {
|
||||
}
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
};
|
||||
_ = cast_id; // autofix
|
||||
|
||||
}
|
||||
},
|
||||
.size => {
|
||||
assert(argument_node_list.len == 1);
|
||||
@ -6512,12 +6517,127 @@ pub const Builder = struct {
|
||||
break :blk unit.all_errors;
|
||||
}
|
||||
},
|
||||
// This is a data structures with parameters
|
||||
.call => b: {
|
||||
const parameterized_type_index = try builder.resolveType(unit, context, node.left);
|
||||
const parameter_nodes = unit.getNodeList(node.right);
|
||||
var parameter_types = UnpinnedArray(Type.Index){};
|
||||
|
||||
for (parameter_nodes) |parameter_node_index| {
|
||||
const parameter_node = unit.getNode(parameter_node_index);
|
||||
switch (parameter_node.id) {
|
||||
.unsigned_integer_type => try parameter_types.append(context.my_allocator, try builder.resolveType(unit, context, parameter_node_index)),
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
}
|
||||
|
||||
const instantiated_type_index = try builder.instantiate_polymorphic_type(unit, context, parameterized_type_index, parameter_types.slice(), parameterized_type_index);
|
||||
break :b instantiated_type_index;
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn instantiate_polymorphic_type(builder: *Builder, unit: *Unit, context: *const Context, type_index: Type.Index, parameter_types: []const Type.Index, original_type_index: Type.Index) !Type.Index {
|
||||
const parameterized_type = unit.types.get(type_index);
|
||||
const original_type = unit.types.get(original_type_index);
|
||||
|
||||
switch (parameterized_type.*) {
|
||||
.@"struct" => |struct_index| switch (unit.structs.get(struct_index).kind) {
|
||||
.@"struct" => |*struct_type| {
|
||||
switch (struct_type.options.polymorphism_kind) {
|
||||
.declaration => |declaration_types| {
|
||||
if (declaration_types.len != parameter_types.len) @panic("Argument count mismatch");
|
||||
|
||||
// var s = struct_type.*;
|
||||
var fields = try UnpinnedArray(Struct.Field.Index).initialize_with_capacity(context.my_allocator, struct_type.fields.length);
|
||||
var is_polymorphic = false;
|
||||
for (struct_type.fields.slice()) |field_index| {
|
||||
const field = unit.struct_fields.get(field_index);
|
||||
const new_type_index = try builder.instantiate_polymorphic_type(unit, context, field.type, parameter_types, original_type_index);
|
||||
is_polymorphic = is_polymorphic or new_type_index == field.type;
|
||||
|
||||
if (field.type != new_type_index) {
|
||||
var f = field.*;
|
||||
f.type = new_type_index;
|
||||
const new_field_index = try unit.struct_fields.append(context.my_allocator, f);
|
||||
fields.append_with_capacity(new_field_index);
|
||||
} else {
|
||||
fields.append_with_capacity(field_index);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_polymorphic) {
|
||||
var s = struct_type.*;
|
||||
s.fields = fields;
|
||||
s.options.polymorphism_kind = .{
|
||||
.implementation = .{
|
||||
.types = parameter_types,
|
||||
.original = type_index,
|
||||
},
|
||||
};
|
||||
const si = try unit.structs.append(context.my_allocator, .{
|
||||
.kind = .{
|
||||
.@"struct" = s,
|
||||
},
|
||||
});
|
||||
const sti = try unit.types.append(context.my_allocator, .{
|
||||
.@"struct" = si,
|
||||
});
|
||||
return sti;
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
},
|
||||
.pointer => |pointer| {
|
||||
const new_type_index = try builder.instantiate_polymorphic_type(unit, context, pointer.type, parameter_types, original_type_index);
|
||||
if (new_type_index != pointer.type) {
|
||||
var p = pointer;
|
||||
p.type = new_type_index;
|
||||
return try unit.getPointerType(context, p);
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
},
|
||||
.polymorphic => |polymorphic_declaration| {
|
||||
assert(polymorphic_declaration.initial_value.type == type_index);
|
||||
const name_hash = polymorphic_declaration.declaration.name;
|
||||
switch (original_type.*) {
|
||||
.@"struct" => |struct_index| switch (unit.structs.get(struct_index).kind) {
|
||||
.@"struct" => |*struct_type| {
|
||||
switch (struct_type.options.polymorphism_kind) {
|
||||
.declaration => |declaration_types| {
|
||||
for (declaration_types, parameter_types) |declaration_type_index, parameter_type_index| {
|
||||
if (declaration_type_index == type_index) {
|
||||
return parameter_type_index;
|
||||
}
|
||||
}
|
||||
|
||||
unreachable;
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
unreachable;
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
_ = name_hash; // autofix
|
||||
unreachable;
|
||||
},
|
||||
.integer => return type_index,
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_builtin_declaration(builder: *Builder, unit: *Unit, context: *const Context, name: []const u8) !*Debug.Declaration.Global {
|
||||
const std_file_index = try builder.resolveImportStringLiteral(unit, context, Type.Expect{ .type = .type }, "std");
|
||||
const std_file = unit.files.get(std_file_index);
|
||||
@ -7502,11 +7622,39 @@ pub const Builder = struct {
|
||||
.@"struct" => b: {
|
||||
assert(container_node.id == .struct_type);
|
||||
|
||||
var struct_options = Struct.Options{};
|
||||
const struct_index = try unit.structs.append(context.my_allocator, .{
|
||||
.kind = .{
|
||||
.@"struct" = .{
|
||||
.scope = .{
|
||||
.scope = .{
|
||||
.kind = switch (builder.current_scope.kind) {
|
||||
.file => .file_container,
|
||||
else => .container,
|
||||
},
|
||||
.line = token_debug_info.line,
|
||||
.column = token_debug_info.column,
|
||||
.level = builder.current_scope.level + 1,
|
||||
.local = false,
|
||||
.file = builder.current_file,
|
||||
},
|
||||
},
|
||||
.options = .{},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const type_index = try unit.types.append(context.my_allocator, .{
|
||||
.@"struct" = struct_index,
|
||||
});
|
||||
|
||||
const struct_type = unit.structs.get(struct_index);
|
||||
const struct_options = & struct_type.kind.@"struct".options;
|
||||
|
||||
if (container_node.right != .null) {
|
||||
const struct_option_nodes = unit.getNodeList(container_node.right);
|
||||
var struct_options_value = false;
|
||||
var parameter_types = UnpinnedArray(Type.Index){};
|
||||
|
||||
for (struct_option_nodes) |struct_option_node_index| {
|
||||
const struct_option_node = unit.getNode(struct_option_node_index);
|
||||
switch (struct_option_node.id) {
|
||||
@ -7538,37 +7686,48 @@ pub const Builder = struct {
|
||||
}
|
||||
}
|
||||
},
|
||||
.comptime_expression => {
|
||||
assert(struct_option_node.left != .null);
|
||||
assert(struct_option_node.right == .null);
|
||||
const declaration_token_debug_info = builder.getTokenDebugInfo(unit, struct_option_node.token);
|
||||
const left = unit.getNode(struct_option_node.left);
|
||||
assert(left.id == .identifier);
|
||||
const identifier = unit.getExpectedTokenBytes(left.token, .identifier);
|
||||
const hash = try unit.processIdentifier(context, identifier);
|
||||
const polymorphic_type_index = try unit.types.append(context.my_allocator, .{
|
||||
.polymorphic = undefined,
|
||||
});
|
||||
const global_declaration_index = try unit.global_declarations.append(context.my_allocator, .{
|
||||
.declaration = .{
|
||||
.scope = &struct_type.kind.@"struct".scope.scope,
|
||||
.name = hash,
|
||||
.type = .type,
|
||||
.line = declaration_token_debug_info.line,
|
||||
.column = declaration_token_debug_info.column,
|
||||
.mutability = .@"const",
|
||||
.kind = .global,
|
||||
},
|
||||
.initial_value = .{
|
||||
.type = polymorphic_type_index,
|
||||
},
|
||||
.type_node_index = .null,
|
||||
.attributes = .{},
|
||||
});
|
||||
const global_declaration = unit.global_declarations.get(global_declaration_index);
|
||||
try struct_type.kind.@"struct".scope.scope.declarations.put_no_clobber(context.my_allocator, hash, &global_declaration.declaration);
|
||||
const polymorphic_type = unit.types.get(polymorphic_type_index);
|
||||
polymorphic_type.polymorphic = global_declaration;
|
||||
try parameter_types.append(context.my_allocator, polymorphic_type_index);
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
}
|
||||
|
||||
struct_type.kind.@"struct".options.polymorphism_kind = .{
|
||||
.declaration = parameter_types.slice(),
|
||||
};
|
||||
}
|
||||
|
||||
const struct_index = try unit.structs.append(context.my_allocator, .{
|
||||
.kind = .{
|
||||
.@"struct" = .{
|
||||
.scope = .{
|
||||
.scope = .{
|
||||
.kind = switch (builder.current_scope.kind) {
|
||||
.file => .file_container,
|
||||
else => .container,
|
||||
},
|
||||
.line = token_debug_info.line,
|
||||
.column = token_debug_info.column,
|
||||
.level = builder.current_scope.level + 1,
|
||||
.local = false,
|
||||
.file = builder.current_file,
|
||||
},
|
||||
},
|
||||
.options = struct_options,
|
||||
},
|
||||
},
|
||||
});
|
||||
const struct_type = unit.structs.get(struct_index);
|
||||
|
||||
const type_index = try unit.types.append(context.my_allocator, .{
|
||||
.@"struct" = struct_index,
|
||||
});
|
||||
|
||||
// Save file type
|
||||
switch (builder.current_scope.kind) {
|
||||
.file => {
|
||||
@ -8681,6 +8840,26 @@ pub const Builder = struct {
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
},
|
||||
.cast => {
|
||||
assert(argument_node_list.len == 1);
|
||||
switch (type_expect) {
|
||||
.type => |type_index| {
|
||||
const value = try builder.resolveComptimeValue(unit, context, Type.Expect.none, .{}, argument_node_list[0], null, .right);
|
||||
const ty = unit.types.get(type_index);
|
||||
switch (ty.*) {
|
||||
.pointer => |_| switch (value) {
|
||||
.@"comptime_int" => |ct_int| switch (ct_int.value) {
|
||||
0 => return .null_pointer,
|
||||
else => unreachable,
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
},
|
||||
@ -10988,6 +11167,15 @@ pub const Builder = struct {
|
||||
else => unreachable,
|
||||
}, unreachable),
|
||||
},
|
||||
.empty_container_literal_guess => {
|
||||
assert(node.left != .null);
|
||||
assert(node.right != .null);
|
||||
const container_type = try builder.resolveType(unit, context, node.left);
|
||||
const node_list = unit.getNodeList(node.right);
|
||||
assert(node_list.len == 0);
|
||||
const result = try builder.resolveContainerLiteral(unit, context, node_list, container_type);
|
||||
return result;
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
};
|
||||
|
||||
@ -15832,6 +16020,13 @@ pub const Unit = struct {
|
||||
return type_index;
|
||||
}
|
||||
}
|
||||
|
||||
fn getPolymorphicTypes(unit: *Unit, type_index: Type.Index) []const Type.Index {
|
||||
const ty = unit.types.get(type_index);
|
||||
return switch (ty.*) {
|
||||
else => |t| @panic(@tagName(t)),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const FixedKeyword = enum {
|
||||
@ -15854,7 +16049,6 @@ pub const FixedKeyword = enum {
|
||||
@"else",
|
||||
@"struct",
|
||||
@"enum",
|
||||
@"union",
|
||||
null,
|
||||
@"align",
|
||||
@"for",
|
||||
|
@ -196,6 +196,7 @@ pub const Node = struct {
|
||||
payload,
|
||||
catch_payload,
|
||||
bitfield_type,
|
||||
comptime_expression,
|
||||
};
|
||||
};
|
||||
|
||||
@ -1210,6 +1211,7 @@ const Analyzer = struct {
|
||||
}
|
||||
|
||||
fn primaryExpression(analyzer: *Analyzer) !Node.Index {
|
||||
const token = analyzer.token_i;
|
||||
const result = switch (analyzer.peekToken()) {
|
||||
.identifier => switch (analyzer.peekTokenAhead(1)) {
|
||||
// TODO: tags
|
||||
@ -1244,7 +1246,6 @@ const Analyzer = struct {
|
||||
.fixed_keyword_return => try analyzer.addNode(.{
|
||||
.id = .@"return",
|
||||
.token = blk: {
|
||||
const token = analyzer.token_i;
|
||||
analyzer.consumeToken();
|
||||
break :blk token;
|
||||
},
|
||||
@ -1256,6 +1257,16 @@ const Analyzer = struct {
|
||||
.operator_left_brace => try analyzer.block(),
|
||||
.fixed_keyword_if => try analyzer.ifExpression(),
|
||||
.fixed_keyword_bitfield => try analyzer.processContainerType(.fixed_keyword_bitfield),
|
||||
.operator_dollar => blk: {
|
||||
analyzer.consumeToken();
|
||||
const t = try analyzer.typeExpression();
|
||||
break :blk try analyzer.addNode(.{
|
||||
.id = .comptime_expression,
|
||||
.token = token,
|
||||
.left = t,
|
||||
.right = .null,
|
||||
});
|
||||
},
|
||||
else => |id| @panic(@tagName(id)),
|
||||
};
|
||||
|
||||
@ -1653,7 +1664,7 @@ const Analyzer = struct {
|
||||
const initialization: InitializationType = current_initialization orelse switch (type_node) {
|
||||
.null => .empty_literal,
|
||||
else => switch (analyzer.nodes.get(type_node).id) {
|
||||
.identifier => .empty_container_literal_guess,
|
||||
.identifier, .call => .empty_container_literal_guess,
|
||||
.array_type => .empty_array_literal,
|
||||
else => |t| @panic(@tagName(t)),
|
||||
},
|
||||
|
1
lib/std/data_structures.nat
Normal file
1
lib/std/data_structures.nat
Normal file
@ -0,0 +1 @@
|
||||
const PinnedArray = #import("data_structures/pinned_array.nat").PinnedArray;
|
5
lib/std/data_structures/pinned_array.nat
Normal file
5
lib/std/data_structures/pinned_array.nat
Normal file
@ -0,0 +1,5 @@
|
||||
const PinnedArray = struct(.{ .sliceable = true }, $T) {
|
||||
pointer: [&]T = #cast(0),
|
||||
length: u32 = 0,
|
||||
capacity: u32 = 0,
|
||||
};
|
@ -5,17 +5,22 @@ comptime {
|
||||
test {
|
||||
_ = build;
|
||||
_ = builtin;
|
||||
_ = data_structures;
|
||||
_ = os;
|
||||
_ = start;
|
||||
}
|
||||
|
||||
const build = #import("build.nat");
|
||||
const builtin = #import("builtin.nat");
|
||||
const os = #import("os.nat");
|
||||
const c = #import("c.nat");
|
||||
const start = #import("start.nat");
|
||||
const testing = #import("testing.nat");
|
||||
|
||||
const data_structures = #import("data_structures.nat");
|
||||
|
||||
const c = #import("c.nat");
|
||||
const os = #import("os.nat");
|
||||
|
||||
const start = #import("start.nat");
|
||||
|
||||
const assert = fn(ok: bool) void {
|
||||
if (!ok) {
|
||||
unreachable;
|
||||
|
6
test/standalone/pinned_array/main.nat
Normal file
6
test/standalone/pinned_array/main.nat
Normal file
@ -0,0 +1,6 @@
|
||||
const std = #import("std");
|
||||
const PinnedArray = std.data_structures.PinnedArray;
|
||||
|
||||
const main = fn () *!void {
|
||||
var pinned_array = PinnedArray(u32){};
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user