4 files changed, 69 insertions(+), 8 deletions(-)
CHANGELOG.md+3| ... | ... | @@ -1,5 +1,8 @@ |
| 1 | 1 | ## 0.1.5 |
| 2 | 2 | |
| 3 | - feat: clarify "defined runtime representation" and "can be represented at runtime" | |
| 4 | - fix(sema): fix passing structs as function params | |
| 5 | ||
| 3 | 6 | ## 0.1.4 |
| 4 | 7 | |
| 5 | 8 | - feat: implement basic struct types (#42) |
src/core/InternPool.zig+29-3| ... | ... | @@ -395,7 +395,9 @@ pub const Type = enum(u32) { |
| 395 | 395 | return ip.valueToKey(ty.value()).array_type; |
| 396 | 396 | } |
| 397 | 397 | |
| 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 | 401 | return switch (ty) { |
| 400 | 402 | .bool, |
| 401 | 403 | .int32, |
| ... | ... | @@ -411,10 +413,34 @@ pub const Type = enum(u32) { |
| 411 | 413 | else => switch (ty.tag(ip)) { |
| 412 | 414 | .simple_type => unreachable, // all simple types are already printed |
| 413 | 415 | .struct_type, |
| 414 | => false, | |
| 415 | 416 | .function_type, |
| 416 | 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 | 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 | 229 | param_comptime_only: struct { |
| 218 | 230 | src: LazyNode, |
| 219 | 231 | type: Type, |
src/core/Sema.zig+25-5| ... | ... | @@ -207,7 +207,7 @@ pub fn analyzeFunctionDeclaration(s: *Sema) Error!Value { |
| 207 | 207 | for (fn_decl.params.slice(&s.code), 0..) |param, i| { |
| 208 | 208 | const src = s.inst_map.start.op(operandArgument(@intCast(i))); |
| 209 | 209 | const ty = try s.resolveType(src, param); |
| 210 | if (!ty.hasRuntimeRepresentation(s.ip)) { | |
| 210 | if (!ty.hasPossibleRepresentation(s.ip)) { | |
| 211 | 211 | return s.fail(.param_comptime_only, .{ |
| 212 | 212 | .src = s.lazyNode(src), |
| 213 | 213 | .type = ty, |
| ... | ... | @@ -546,7 +546,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 546 | 546 | } |
| 547 | 547 | } else { |
| 548 | 548 | for (call.args.slice(&s.code), 0..) |arg, i| { |
| 549 | const ref = try s.ensureValueRuntime( | |
| 549 | const ref = try s.ensureValueDefinedRepresentation( | |
| 550 | 550 | in.op(operandArgument(@intCast(i))), |
| 551 | 551 | try s.resolveRef(arg), |
| 552 | 552 | ); |
| ... | ... | @@ -574,7 +574,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 574 | 574 | const scratch = try s.arena.alloc(Air.Inst.Ref, call.args.len); |
| 575 | 575 | defer s.arena.free(scratch); |
| 576 | 576 | for (call.args.slice(&s.code), 0..) |arg, i| { |
| 577 | const ref = try s.ensureValueRuntime( | |
| 577 | const ref = try s.ensureValueDefinedRepresentation( | |
| 578 | 578 | in.op(operandArgument(@intCast(i))), |
| 579 | 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 | 1213 | .string, |
| 1214 | 1214 | .void, |
| 1215 | 1215 | => {}, |
| 1216 | _ => if (!ty.hasRuntimeRepresentation(s.ip)) { | |
| 1216 | _ => if (!ty.hasPossibleRepresentation(s.ip)) { | |
| 1217 | 1217 | return s.fail(.runtime_incompatible_type, .{ |
| 1218 | 1218 | .src = s.lazyNode(src), |
| 1219 | 1219 | .type = ty, |
| ... | ... | @@ -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 | 1234 | return ref; |
| 1227 | 1235 | } |
| 1228 | 1236 | |
| 1237 | fn 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 | ||
| 1229 | 1249 | fn coerceArrayValue(s: *Sema, src: InstOp, value: Value, item_type: Type) !Value { |
| 1230 | 1250 | const array: InternPool.Tag.ArrayValue, var trail = value.valueData(s.ip, .array_value).getTrail(s.ip); |
| 1231 | 1251 |