From d75fca14692f14f7bdc5720aa5d86d682ac2f12d Mon Sep 17 00:00:00 2001 From: dave caruso Date: Tue, 23 Jul 2024 14:18:52 -0700 Subject: [PATCH] fix(sema): separate `hasRuntimeRepresentation` into two functions --- CHANGELOG.md | 3 +++ src/core/InternPool.zig | 32 +++++++++++++++++++++++++++++--- src/core/Report.zig | 12 ++++++++++++ src/core/Sema.zig | 30 +++++++++++++++++++++++++----- 4 files changed, 69 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d839acacc037994a584756874aaeb0b6a4883fcd..69fc35eace17a56e0bbf99eff1626fbb96ce9cf0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ## 0.1.5 +- feat: clarify "defined runtime representation" and "can be represented at runtime" +- fix(sema): fix passing structs as function params + ## 0.1.4 - feat: implement basic struct types (#42) diff --git a/src/core/InternPool.zig b/src/core/InternPool.zig index fd5f15febebc8024746fe6462c059af8958b30f6..a4160990fffaf692b393c63017e29c0b5ec8c048 100644 --- a/src/core/InternPool.zig +++ b/src/core/InternPool.zig @@ -395,7 +395,9 @@ pub const Type = enum(u32) { return ip.valueToKey(ty.value()).array_type; } - pub fn hasRuntimeRepresentation(ty: Type, ip: *const InternPool) bool { + /// If it is possible to represent this type at runtime. Some types do not + /// have a defined representation, such as structs (to allow field renaming, unwrapping). + pub fn hasPossibleRepresentation(ty: Type, ip: *const InternPool) bool { return switch (ty) { .bool, .int32, @@ -411,10 +413,34 @@ pub const Type = enum(u32) { else => switch (ty.tag(ip)) { .simple_type => unreachable, // all simple types are already printed .struct_type, - => false, .function_type, => true, - .array_type => ty.toArrayType(ip).?.item_type.hasRuntimeRepresentation(ip), + .array_type => ty.toArrayType(ip).?.item_type.hasPossibleRepresentation(ip), + }, + }; + } + + /// A defined representation is a specific strategy for lowering values in + /// codegen that the developer is allowed to rely on. + pub fn hasDefinedRepresentation(ty: Type, ip: *const InternPool) bool { + return switch (ty) { + .bool, + .int32, + .float, + .string, + .@"opaque", + => true, + .void, + .numeric_literal, + .type, + .noreturn, + => false, + else => switch (ty.tag(ip)) { + .simple_type => unreachable, // all simple types are already printed + .struct_type, + .function_type, + => false, + .array_type => ty.toArrayType(ip).?.item_type.hasDefinedRepresentation(ip), }, }; } diff --git a/src/core/Report.zig b/src/core/Report.zig index cac8f4db1023d84c544d5969dba22e81ae2e0b25..c58c32f0234448ed1f24c073a6de01e0c17bd1a8 100644 --- a/src/core/Report.zig +++ b/src/core/Report.zig @@ -214,6 +214,18 @@ pub const Err = union(enum) { }); } }, + type_no_defined_representation: struct { + src: LazyNode, + type: Type, + + fn build(data: @This(), ctx: *MsgContext) !void { + const node = try data.src.get(ctx); + try ctx.msgNode(node, "type \"{}\" does not have a defined representation", .{ + data.type.displayName(ctx.ip()), + }); + try ctx.msgAlone("TODO: clarify why for the user's specific case; structs and functions are not guaranteed to hold the same field and parameter count+types. this allows future optimization passes to be smarter", .{}); + } + }, param_comptime_only: struct { src: LazyNode, type: Type, diff --git a/src/core/Sema.zig b/src/core/Sema.zig index ba2455adb3d259529e70c416a0815b7198086ee6..d54ba755c8d44f1c23211e7d2cb63e1d2263f644 100644 --- a/src/core/Sema.zig +++ b/src/core/Sema.zig @@ -207,7 +207,7 @@ pub fn analyzeFunctionDeclaration(s: *Sema) Error!Value { for (fn_decl.params.slice(&s.code), 0..) |param, i| { const src = s.inst_map.start.op(operandArgument(@intCast(i))); const ty = try s.resolveType(src, param); - if (!ty.hasRuntimeRepresentation(s.ip)) { + if (!ty.hasPossibleRepresentation(s.ip)) { return s.fail(.param_comptime_only, .{ .src = s.lazyNode(src), .type = ty, @@ -546,7 +546,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { } } else { for (call.args.slice(&s.code), 0..) |arg, i| { - const ref = try s.ensureValueRuntime( + const ref = try s.ensureValueDefinedRepresentation( in.op(operandArgument(@intCast(i))), try s.resolveRef(arg), ); @@ -574,7 +574,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { const scratch = try s.arena.alloc(Air.Inst.Ref, call.args.len); defer s.arena.free(scratch); for (call.args.slice(&s.code), 0..) |arg, i| { - const ref = try s.ensureValueRuntime( + const ref = try s.ensureValueDefinedRepresentation( in.op(operandArgument(@intCast(i))), try s.resolveRef(arg), ); @@ -1213,7 +1213,7 @@ fn ensureValueRuntime(s: *Sema, src: InstOp, ref: Air.Inst.Ref) Error!Air.Inst.R .string, .void, => {}, - _ => if (!ty.hasRuntimeRepresentation(s.ip)) { + _ => if (!ty.hasPossibleRepresentation(s.ip)) { return s.fail(.runtime_incompatible_type, .{ .src = s.lazyNode(src), .type = ty, @@ -1221,11 +1221,31 @@ fn ensureValueRuntime(s: *Sema, src: InstOp, ref: Air.Inst.Ref) Error!Air.Inst.R }, } }, - else => {}, + .index => { + const ty = s.typeOfRef(ref); + if (!ty.hasPossibleRepresentation(s.ip)) { + return s.fail(.runtime_incompatible_type, .{ + .src = s.lazyNode(src), + .type = ty, + }); + } + }, } return ref; } +fn ensureValueDefinedRepresentation(s: *Sema, src: InstOp, ref: Air.Inst.Ref) Error!Air.Inst.Ref { + const runtime = try s.ensureValueRuntime(src, ref); + const ty = s.typeOfRef(runtime); + if (!ty.hasDefinedRepresentation(s.ip)) { + return s.fail(.type_no_defined_representation, .{ + .src = s.lazyNode(src), + .type = ty, + }); + } + return runtime; +} + fn coerceArrayValue(s: *Sema, src: InstOp, value: Value, item_type: Type) !Value { const array: InternPool.Tag.ArrayValue, var trail = value.valueData(s.ip, .array_value).getTrail(s.ip); -- 2.54.0