authorgravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-07-23 14:18:52-07:00
committergravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-07-23 14:18:52-07:00
logd75fca14692f14f7bdc5720aa5d86d682ac2f12d
tree2387c3c2dd6b90a397b36a840a4890c461c272a2
parentd6a1a0803f6a9ecace8ff0c23d4ad54f65670bde
signature Commit is signed but in an unrecognized format.

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


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

CHANGELOG.md+3
......@@ -1,5 +1,8 @@
11## 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
36## 0.1.4
47
58- feat: implement basic struct types (#42)
src/core/InternPool.zig+29-3
......@@ -395,7 +395,9 @@ pub const Type = enum(u32) {
395395 return ip.valueToKey(ty.value()).array_type;
396396 }
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 {
399401 return switch (ty) {
400402 .bool,
401403 .int32,
......@@ -411,10 +413,34 @@ pub const Type = enum(u32) {
411413 else => switch (ty.tag(ip)) {
412414 .simple_type => unreachable, // all simple types are already printed
413415 .struct_type,
414 => false,
415416 .function_type,
416417 => 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),
418444 },
419445 };
420446 }
src/core/Report.zig+12
......@@ -214,6 +214,18 @@ pub const Err = union(enum) {
214214 });
215215 }
216216 },
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 },
217229 param_comptime_only: struct {
218230 src: LazyNode,
219231 type: Type,
src/core/Sema.zig+25-5
......@@ -207,7 +207,7 @@ pub fn analyzeFunctionDeclaration(s: *Sema) Error!Value {
207207 for (fn_decl.params.slice(&s.code), 0..) |param, i| {
208208 const src = s.inst_map.start.op(operandArgument(@intCast(i)));
209209 const ty = try s.resolveType(src, param);
210 if (!ty.hasRuntimeRepresentation(s.ip)) {
210 if (!ty.hasPossibleRepresentation(s.ip)) {
211211 return s.fail(.param_comptime_only, .{
212212 .src = s.lazyNode(src),
213213 .type = ty,
......@@ -546,7 +546,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
546546 }
547547 } else {
548548 for (call.args.slice(&s.code), 0..) |arg, i| {
549 const ref = try s.ensureValueRuntime(
549 const ref = try s.ensureValueDefinedRepresentation(
550550 in.op(operandArgument(@intCast(i))),
551551 try s.resolveRef(arg),
552552 );
......@@ -574,7 +574,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
574574 const scratch = try s.arena.alloc(Air.Inst.Ref, call.args.len);
575575 defer s.arena.free(scratch);
576576 for (call.args.slice(&s.code), 0..) |arg, i| {
577 const ref = try s.ensureValueRuntime(
577 const ref = try s.ensureValueDefinedRepresentation(
578578 in.op(operandArgument(@intCast(i))),
579579 try s.resolveRef(arg),
580580 );
......@@ -1213,7 +1213,7 @@ fn ensureValueRuntime(s: *Sema, src: InstOp, ref: Air.Inst.Ref) Error!Air.Inst.R
12131213 .string,
12141214 .void,
12151215 => {},
1216 _ => if (!ty.hasRuntimeRepresentation(s.ip)) {
1216 _ => if (!ty.hasPossibleRepresentation(s.ip)) {
12171217 return s.fail(.runtime_incompatible_type, .{
12181218 .src = s.lazyNode(src),
12191219 .type = ty,
......@@ -1221,11 +1221,31 @@ fn ensureValueRuntime(s: *Sema, src: InstOp, ref: Air.Inst.Ref) Error!Air.Inst.R
12211221 },
12221222 }
12231223 },
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 },
12251233 }
12261234 return ref;
12271235}
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
12291249fn coerceArrayValue(s: *Sema, src: InstOp, value: Value, item_type: Type) !Value {
12301250 const array: InternPool.Tag.ArrayValue, var trail = value.valueData(s.ip, .array_value).getTrail(s.ip);
12311251