authorchloe caruso <git@paperclover.net> 2024-07-23 14:18:52-07:00
committerchloe caruso <git@paperclover.net> 2024-07-23 14:18:52-07:00
log645e8bd1a355759251056f95184ce6d3e8c77fdc
tree2387c3c2dd6b90a397b36a840a4890c461c272a2
parent57d2129baeabbf799abbe58a0c90d035b3134d52

fix(sema): separate `hasRuntimeRepresentation` into two functions


4 files changed, 69 insertions(+), 8 deletions(-)

CHANGELOG.md+3
...@@ -1,5 +1,8 @@...@@ -1,5 +1,8 @@
1## 0.1.51## 0.1.5
22
3- feat: clarify "defined runtime representation" and "can be represented at runtime"
4- fix(sema): fix passing structs as function params
5
3## 0.1.46## 0.1.4
47
5- feat: implement basic struct types (#42)8- feat: implement basic struct types (#42)
src/core/InternPool.zig+29-3
...@@ -395,7 +395,9 @@ pub const Type = enum(u32) {...@@ -395,7 +395,9 @@ pub const Type = enum(u32) {
395 return ip.valueToKey(ty.value()).array_type;395 return ip.valueToKey(ty.value()).array_type;
396 }396 }
397397
398 pub fn hasRuntimeRepresentation(ty: Type, ip: *const InternPool) bool {398 /// If it is possible to represent this type at runtime. Some types do not
399 /// have a defined representation, such as structs (to allow field renaming, unwrapping).
400 pub fn hasPossibleRepresentation(ty: Type, ip: *const InternPool) bool {
399 return switch (ty) {401 return switch (ty) {
400 .bool,402 .bool,
401 .int32,403 .int32,
...@@ -411,10 +413,34 @@ pub const Type = enum(u32) {...@@ -411,10 +413,34 @@ pub const Type = enum(u32) {
411 else => switch (ty.tag(ip)) {413 else => switch (ty.tag(ip)) {
412 .simple_type => unreachable, // all simple types are already printed414 .simple_type => unreachable, // all simple types are already printed
413 .struct_type,415 .struct_type,
414 => false,
415 .function_type,416 .function_type,
416 => true,417 => true,
417 .array_type => ty.toArrayType(ip).?.item_type.hasRuntimeRepresentation(ip),418 .array_type => ty.toArrayType(ip).?.item_type.hasPossibleRepresentation(ip),
419 },
420 };
421 }
422
423 /// A defined representation is a specific strategy for lowering values in
424 /// codegen that the developer is allowed to rely on.
425 pub fn hasDefinedRepresentation(ty: Type, ip: *const InternPool) bool {
426 return switch (ty) {
427 .bool,
428 .int32,
429 .float,
430 .string,
431 .@"opaque",
432 => true,
433 .void,
434 .numeric_literal,
435 .type,
436 .noreturn,
437 => false,
438 else => switch (ty.tag(ip)) {
439 .simple_type => unreachable, // all simple types are already printed
440 .struct_type,
441 .function_type,
442 => false,
443 .array_type => ty.toArrayType(ip).?.item_type.hasDefinedRepresentation(ip),
418 },444 },
419 };445 };
420 }446 }
src/core/Report.zig+12
...@@ -214,6 +214,18 @@ pub const Err = union(enum) {...@@ -214,6 +214,18 @@ pub const Err = union(enum) {
214 });214 });
215 }215 }
216 },216 },
217 type_no_defined_representation: struct {
218 src: LazyNode,
219 type: Type,
220
221 fn build(data: @This(), ctx: *MsgContext) !void {
222 const node = try data.src.get(ctx);
223 try ctx.msgNode(node, "type \"{}\" does not have a defined representation", .{
224 data.type.displayName(ctx.ip()),
225 });
226 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", .{});
227 }
228 },
217 param_comptime_only: struct {229 param_comptime_only: struct {
218 src: LazyNode,230 src: LazyNode,
219 type: Type,231 type: Type,
src/core/Sema.zig+25-5
...@@ -207,7 +207,7 @@ pub fn analyzeFunctionDeclaration(s: *Sema) Error!Value {...@@ -207,7 +207,7 @@ pub fn analyzeFunctionDeclaration(s: *Sema) Error!Value {
207 for (fn_decl.params.slice(&s.code), 0..) |param, i| {207 for (fn_decl.params.slice(&s.code), 0..) |param, i| {
208 const src = s.inst_map.start.op(operandArgument(@intCast(i)));208 const src = s.inst_map.start.op(operandArgument(@intCast(i)));
209 const ty = try s.resolveType(src, param);209 const ty = try s.resolveType(src, param);
210 if (!ty.hasRuntimeRepresentation(s.ip)) {210 if (!ty.hasPossibleRepresentation(s.ip)) {
211 return s.fail(.param_comptime_only, .{211 return s.fail(.param_comptime_only, .{
212 .src = s.lazyNode(src),212 .src = s.lazyNode(src),
213 .type = ty,213 .type = ty,
...@@ -546,7 +546,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {...@@ -546,7 +546,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
546 }546 }
547 } else {547 } else {
548 for (call.args.slice(&s.code), 0..) |arg, i| {548 for (call.args.slice(&s.code), 0..) |arg, i| {
549 const ref = try s.ensureValueRuntime(549 const ref = try s.ensureValueDefinedRepresentation(
550 in.op(operandArgument(@intCast(i))),550 in.op(operandArgument(@intCast(i))),
551 try s.resolveRef(arg),551 try s.resolveRef(arg),
552 );552 );
...@@ -574,7 +574,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {...@@ -574,7 +574,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
574 const scratch = try s.arena.alloc(Air.Inst.Ref, call.args.len);574 const scratch = try s.arena.alloc(Air.Inst.Ref, call.args.len);
575 defer s.arena.free(scratch);575 defer s.arena.free(scratch);
576 for (call.args.slice(&s.code), 0..) |arg, i| {576 for (call.args.slice(&s.code), 0..) |arg, i| {
577 const ref = try s.ensureValueRuntime(577 const ref = try s.ensureValueDefinedRepresentation(
578 in.op(operandArgument(@intCast(i))),578 in.op(operandArgument(@intCast(i))),
579 try s.resolveRef(arg),579 try s.resolveRef(arg),
580 );580 );
...@@ -1213,7 +1213,7 @@ fn ensureValueRuntime(s: *Sema, src: InstOp, ref: Air.Inst.Ref) Error!Air.Inst.R...@@ -1213,7 +1213,7 @@ fn ensureValueRuntime(s: *Sema, src: InstOp, ref: Air.Inst.Ref) Error!Air.Inst.R
1213 .string,1213 .string,
1214 .void,1214 .void,
1215 => {},1215 => {},
1216 _ => if (!ty.hasRuntimeRepresentation(s.ip)) {1216 _ => if (!ty.hasPossibleRepresentation(s.ip)) {
1217 return s.fail(.runtime_incompatible_type, .{1217 return s.fail(.runtime_incompatible_type, .{
1218 .src = s.lazyNode(src),1218 .src = s.lazyNode(src),
1219 .type = ty,1219 .type = ty,
...@@ -1221,11 +1221,31 @@ fn ensureValueRuntime(s: *Sema, src: InstOp, ref: Air.Inst.Ref) Error!Air.Inst.R...@@ -1221,11 +1221,31 @@ fn ensureValueRuntime(s: *Sema, src: InstOp, ref: Air.Inst.Ref) Error!Air.Inst.R
1221 },1221 },
1222 }1222 }
1223 },1223 },
1224 else => {},1224 .index => {
1225 const ty = s.typeOfRef(ref);
1226 if (!ty.hasPossibleRepresentation(s.ip)) {
1227 return s.fail(.runtime_incompatible_type, .{
1228 .src = s.lazyNode(src),
1229 .type = ty,
1230 });
1231 }
1232 },
1225 }1233 }
1226 return ref;1234 return ref;
1227}1235}
12281236
1237fn ensureValueDefinedRepresentation(s: *Sema, src: InstOp, ref: Air.Inst.Ref) Error!Air.Inst.Ref {
1238 const runtime = try s.ensureValueRuntime(src, ref);
1239 const ty = s.typeOfRef(runtime);
1240 if (!ty.hasDefinedRepresentation(s.ip)) {
1241 return s.fail(.type_no_defined_representation, .{
1242 .src = s.lazyNode(src),
1243 .type = ty,
1244 });
1245 }
1246 return runtime;
1247}
1248
1229fn coerceArrayValue(s: *Sema, src: InstOp, value: Value, item_type: Type) !Value {1249fn coerceArrayValue(s: *Sema, src: InstOp, value: Value, item_type: Type) !Value {
1230 const array: InternPool.Tag.ArrayValue, var trail = value.valueData(s.ip, .array_value).getTrail(s.ip);1250 const array: InternPool.Tag.ArrayValue, var trail = value.valueData(s.ip, .array_value).getTrail(s.ip);
12311251