From 0977e4140768447e57474792ff42d056a8f2e0ed Mon Sep 17 00:00:00 2001 From: Vexu Date: Tue, 18 Aug 2020 10:36:57 +0300 Subject: [PATCH 1/7] stage2: ensure discarded error union payload is void --- src-self-hosted/astgen.zig | 6 +++++- src-self-hosted/zir.zig | 4 ++++ src-self-hosted/zir_sema.zig | 5 +++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig index 3a9c7e6c59..b521e1543a 100644 --- a/src-self-hosted/astgen.zig +++ b/src-self-hosted/astgen.zig @@ -600,7 +600,11 @@ const CondKind = union(enum) { fn thenSubScope(self: CondKind, mod: *Module, then_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope { if (self == .bool) return &then_scope.base; - const payload = payload_node.?.castTag(.PointerPayload).?; + const payload = payload_node.?.castTag(.PointerPayload) orelse { + // condition is error union and payload is not explicitly ignored + _ = try addZIRUnOp(mod, &then_scope.base, src, .ensure_err_payload_void, self.err_union.?); + return &then_scope.base; + }; const is_ptr = payload.ptr_token != null; const ident_node = payload.value_symbol.castTag(.Identifier).?; diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index 276db6d522..ebc491c943 100644 --- a/src-self-hosted/zir.zig +++ b/src-self-hosted/zir.zig @@ -225,6 +225,8 @@ pub const Inst = struct { unwrap_err_safe, /// Same as previous, but without safety checks. Used for orelse, if and while unwrap_err_unsafe, + /// Takes a *E!T and raises a compiler error if T != void + ensure_err_payload_void, pub fn Type(tag: Tag) type { return switch (tag) { @@ -259,6 +261,7 @@ pub const Inst = struct { .unwrap_optional_unsafe, .unwrap_err_safe, .unwrap_err_unsafe, + .ensure_err_payload_void, => UnOp, .add, @@ -398,6 +401,7 @@ pub const Inst = struct { .unwrap_err_safe, .unwrap_err_unsafe, .ptr_type, + .ensure_err_payload_void, => false, .@"break", diff --git a/src-self-hosted/zir_sema.zig b/src-self-hosted/zir_sema.zig index 94c3a19677..d51d0d0f7f 100644 --- a/src-self-hosted/zir_sema.zig +++ b/src-self-hosted/zir_sema.zig @@ -112,6 +112,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .unwrap_optional_unsafe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_unsafe).?, false), .unwrap_err_safe => return analyzeInstUnwrapErr(mod, scope, old_inst.castTag(.unwrap_err_safe).?, true), .unwrap_err_unsafe => return analyzeInstUnwrapErr(mod, scope, old_inst.castTag(.unwrap_err_unsafe).?, false), + .ensure_err_payload_void => return analyzeInstEnsureErrPayloadVoid(mod, scope, old_inst.castTag(.ensure_err_payload_void).?), } } @@ -735,6 +736,10 @@ fn analyzeInstUnwrapErr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, saf return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstUnwrapErr", .{}); } +fn analyzeInstEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst { + return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstEnsureErrPayloadVoid", .{}); +} + fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst { const return_type = try resolveType(mod, scope, fntype.positionals.return_type); From 3eb8f7be1018cef3043bfb56a0e2c3ff323e91d5 Mon Sep 17 00:00:00 2001 From: Vexu Date: Tue, 18 Aug 2020 11:38:56 +0300 Subject: [PATCH 2/7] stage2: astgen bool and/or --- src-self-hosted/astgen.zig | 89 ++++++++++++++++++++++++++++++++++++-- src-self-hosted/zir.zig | 4 ++ 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig index b521e1543a..b8860d7df6 100644 --- a/src-self-hosted/astgen.zig +++ b/src-self-hosted/astgen.zig @@ -100,6 +100,9 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .ArrayCat => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayCat).?, .array_cat), .ArrayMult => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayMult).?, .array_mul), + .BoolAnd => return boolBinOp(mod, scope, rl, node.castTag(.BoolAnd).?), + .BoolOr => return boolBinOp(mod, scope, rl, node.castTag(.BoolOr).?), + .Identifier => return try identifier(mod, scope, rl, node.castTag(.Identifier).?), .Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)), .StringLiteral => return rlWrap(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)), @@ -124,11 +127,10 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?), .Break => return rlWrap(mod, scope, rl, try breakExpr(mod, scope, node.castTag(.Break).?)), .PtrType => return rlWrap(mod, scope, rl, try ptrType(mod, scope, node.castTag(.PtrType).?)), + .GroupedExpression => return expr(mod, scope, rl, node.castTag(.GroupedExpression).?.expr), .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}), .Catch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Catch", .{}), - .BoolAnd => return mod.failNode(scope, node, "TODO implement astgen.expr for .BoolAnd", .{}), - .BoolOr => return mod.failNode(scope, node, "TODO implement astgen.expr for .BoolOr", .{}), .ErrorUnion => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorUnion", .{}), .MergeErrorSets => return mod.failNode(scope, node, "TODO implement astgen.expr for .MergeErrorSets", .{}), .Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}), @@ -159,7 +161,6 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .EnumLiteral => return mod.failNode(scope, node, "TODO implement astgen.expr for .EnumLiteral", .{}), .MultilineStringLiteral => return mod.failNode(scope, node, "TODO implement astgen.expr for .MultilineStringLiteral", .{}), .CharLiteral => return mod.failNode(scope, node, "TODO implement astgen.expr for .CharLiteral", .{}), - .GroupedExpression => return mod.failNode(scope, node, "TODO implement astgen.expr for .GroupedExpression", .{}), .ErrorSetDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorSetDecl", .{}), .ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}), .Comptime => return mod.failNode(scope, node, "TODO implement astgen.expr for .Comptime", .{}), @@ -568,6 +569,88 @@ fn simpleBinOp( return rlWrap(mod, scope, rl, result); } +fn boolBinOp( + mod: *Module, + scope: *Scope, + rl: ResultLoc, + infix_node: *ast.Node.SimpleInfixOp, +) InnerError!*zir.Inst { + const tree = scope.tree(); + const src = tree.token_locs[infix_node.op_token].start; + const bool_type = try addZIRInstConst(mod, scope, src, .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.bool_type), + }); + + var block_scope: Scope.GenZIR = .{ + .parent = scope, + .decl = scope.decl().?, + .arena = scope.arena(), + .instructions = .{}, + }; + defer block_scope.instructions.deinit(mod.gpa); + + const lhs = try expr(mod, scope, .{ .ty = bool_type }, infix_node.lhs); + const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{ + .condition = lhs, + .then_body = undefined, // populated below + .else_body = undefined, // populated below + }, .{}); + + const block = try addZIRInstBlock(mod, scope, src, .{ + .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), + }); + + var rhs_scope: Scope.GenZIR = .{ + .parent = scope, + .decl = block_scope.decl, + .arena = block_scope.arena, + .instructions = .{}, + }; + defer rhs_scope.instructions.deinit(mod.gpa); + + const rhs = try expr(mod, &rhs_scope.base, .{ .ty = bool_type }, infix_node.rhs); + _ = try addZIRInst(mod, &rhs_scope.base, src, zir.Inst.Break, .{ + .block = block, + .operand = rhs, + }, .{}); + + var const_scope: Scope.GenZIR = .{ + .parent = scope, + .decl = block_scope.decl, + .arena = block_scope.arena, + .instructions = .{}, + }; + defer const_scope.instructions.deinit(mod.gpa); + + const is_bool_and = infix_node.base.tag == .BoolAnd; + _ = try addZIRInst(mod, &const_scope.base, src, zir.Inst.Break, .{ + .block = block, + .operand = try addZIRInstConst(mod, &const_scope.base, src, .{ + .ty = Type.initTag(.bool), + .val = if (is_bool_and) Value.initTag(.bool_false) else Value.initTag(.bool_true), + }), + }, .{}); + + if (is_bool_and) { + // if lhs // AND + // break rhs + // else + // break false + condbr.positionals.then_body = .{ .instructions = try rhs_scope.arena.dupe(*zir.Inst, rhs_scope.instructions.items) }; + condbr.positionals.else_body = .{ .instructions = try const_scope.arena.dupe(*zir.Inst, const_scope.instructions.items) }; + } else { + // if lhs // OR + // break true + // else + // break rhs + condbr.positionals.then_body = .{ .instructions = try const_scope.arena.dupe(*zir.Inst, const_scope.instructions.items) }; + condbr.positionals.else_body = .{ .instructions = try rhs_scope.arena.dupe(*zir.Inst, rhs_scope.instructions.items) }; + } + + return rlWrap(mod, scope, rl, &block.base); +} + const CondKind = union(enum) { bool, optional: ?*zir.Inst, diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index ebc491c943..3e8400dca7 100644 --- a/src-self-hosted/zir.zig +++ b/src-self-hosted/zir.zig @@ -1924,6 +1924,10 @@ const EmitZIR = struct { return self.emitUnnamedDecl(&str_inst.base); }, .Void => return self.emitPrimitive(src, .void_value), + .Bool => if (typed_value.val.toBool()) + return self.emitPrimitive(src, .@"true") + else + return self.emitPrimitive(src, .@"false"), else => |t| std.debug.panic("TODO implement emitTypedValue for {}", .{@tagName(t)}), } } From 7c15c9428e9df26c8699f034704796b03cf839b9 Mon Sep 17 00:00:00 2001 From: Vexu Date: Tue, 18 Aug 2020 12:24:23 +0300 Subject: [PATCH 3/7] stage2: array types --- src-self-hosted/Module.zig | 67 +++++++++++++++++- src-self-hosted/astgen.zig | 46 ++++++++++++- src-self-hosted/type.zig | 127 +++++++++++++++++++++++++++++------ src-self-hosted/zir.zig | 20 ++++++ src-self-hosted/zir_sema.zig | 45 ++++++------- 5 files changed, 255 insertions(+), 50 deletions(-) diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 6e33101e76..3c31c7da44 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -2902,7 +2902,7 @@ pub fn floatSub(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: return Value.initPayload(val_payload); } -pub fn singlePtrType(self: *Module, scope: *Scope, src: usize, mutable: bool, elem_ty: Type) error{OutOfMemory}!Type { +pub fn singlePtrType(self: *Module, scope: *Scope, src: usize, mutable: bool, elem_ty: Type) Allocator.Error!Type { const type_payload = try scope.arena().create(Type.Payload.Pointer); type_payload.* = .{ .base = .{ .tag = if (mutable) .single_mut_pointer else .single_const_pointer }, @@ -2911,6 +2911,71 @@ pub fn singlePtrType(self: *Module, scope: *Scope, src: usize, mutable: bool, el return Type.initPayload(&type_payload.base); } +pub fn optionalType(self: *Module, scope: *Scope, child_type: Type) Allocator.Error!Type { + return Type.initPayload(switch (child_type.tag()) { + .single_const_pointer => blk: { + const payload = try scope.arena().create(Type.Payload.Pointer); + payload.* = .{ + .base = .{ .tag = .optional_single_const_pointer }, + .pointee_type = child_type.elemType(), + }; + break :blk &payload.base; + }, + .single_mut_pointer => blk: { + const payload = try scope.arena().create(Type.Payload.Pointer); + payload.* = .{ + .base = .{ .tag = .optional_single_mut_pointer }, + .pointee_type = child_type.elemType(), + }; + break :blk &payload.base; + }, + else => blk: { + const payload = try scope.arena().create(Type.Payload.Optional); + payload.* = .{ + .child_type = child_type, + }; + break :blk &payload.base; + }, + }); +} + +pub fn arrayType(self: *Module, scope: *Scope, len: u64, sentinel: ?Value, elem_type: Type) Allocator.Error!Type { + if (elem_type.eql(Type.initTag(.u8))) { + if (sentinel) |some| { + if (some.eql(Value.initTag(.zero))) { + const payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0); + payload.* = .{ + .len = len, + }; + return Type.initPayload(&payload.base); + } + } else { + const payload = try scope.arena().create(Type.Payload.Array_u8); + payload.* = .{ + .len = len, + }; + return Type.initPayload(&payload.base); + } + } + + if (sentinel) |some| { + const payload = try scope.arena().create(Type.Payload.ArraySentinel); + payload.* = .{ + .len = len, + .sentinel = some, + .elem_type = elem_type, + }; + return Type.initPayload(&payload.base); + } + + const payload = try scope.arena().create(Type.Payload.Array); + payload.* = .{ + .len = len, + .elem_type = elem_type, + }; + return Type.initPayload(&payload.base); +} + pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void { const zir_module = scope.namespace(); const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source"); diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig index b8860d7df6..46ccde10f1 100644 --- a/src-self-hosted/astgen.zig +++ b/src-self-hosted/astgen.zig @@ -128,6 +128,8 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .Break => return rlWrap(mod, scope, rl, try breakExpr(mod, scope, node.castTag(.Break).?)), .PtrType => return rlWrap(mod, scope, rl, try ptrType(mod, scope, node.castTag(.PtrType).?)), .GroupedExpression => return expr(mod, scope, rl, node.castTag(.GroupedExpression).?.expr), + .ArrayType => return rlWrap(mod, scope, rl, try arrayType(mod, scope, node.castTag(.ArrayType).?)), + .ArrayTypeSentinel => return rlWrap(mod, scope, rl, try arrayTypeSentinel(mod, scope, node.castTag(.ArrayTypeSentinel).?)), .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}), .Catch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Catch", .{}), @@ -141,8 +143,6 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .NegationWrap => return mod.failNode(scope, node, "TODO implement astgen.expr for .NegationWrap", .{}), .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}), .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}), - .ArrayType => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayType", .{}), - .ArrayTypeSentinel => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayTypeSentinel", .{}), .SliceType => return mod.failNode(scope, node, "TODO implement astgen.expr for .SliceType", .{}), .Slice => return mod.failNode(scope, node, "TODO implement astgen.expr for .Slice", .{}), .ArrayAccess => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayAccess", .{}), @@ -485,6 +485,48 @@ fn ptrType(mod: *Module, scope: *Scope, node: *ast.Node.PtrType) InnerError!*zir return addZIRInst(mod, scope, src, zir.Inst.PtrType, .{ .child_type = child_type }, kw_args); } +fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst { + const tree = scope.tree(); + const src = tree.token_locs[node.op_token].start; + const meta_type = try addZIRInstConst(mod, scope, src, .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.type_type), + }); + const usize_type = try addZIRInstConst(mod, scope, src, .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.usize_type), + }); + + const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr); + const child_type = try expr(mod, scope, .{ .ty = meta_type }, node.rhs); + + return addZIRBinOp(mod, scope, src, .array_type, len, child_type); +} + +fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSentinel) !*zir.Inst { + const tree = scope.tree(); + const src = tree.token_locs[node.op_token].start; + const meta_type = try addZIRInstConst(mod, scope, src, .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.type_type), + }); + const usize_type = try addZIRInstConst(mod, scope, src, .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.usize_type), + }); + + const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr); + const sentinel_uncasted = try expr(mod, scope, .none, node.sentinel); + const elem_type = try expr(mod, scope, .{ .ty = meta_type }, node.rhs); + const sentinel = try addZIRBinOp(mod, scope, src, .as, elem_type, sentinel_uncasted); + + return addZIRInst(mod, scope, src, zir.Inst.ArrayTypeSentinel, .{ + .len = len, + .sentinel = sentinel, + .elem_type = elem_type, + }, .{}); +} + fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[node.rtoken].start; diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index faba784f90..5329841d4b 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -65,7 +65,7 @@ pub const Type = extern union { .fn_ccc_void_no_args => return .Fn, .function => return .Fn, - .array, .array_u8_sentinel_0 => return .Array, + .array, .array_u8_sentinel_0, .array_u8, .array_sentinel => return .Array, .single_const_pointer => return .Pointer, .single_mut_pointer => return .Pointer, .single_const_pointer_to_comptime_int => return .Pointer, @@ -330,6 +330,7 @@ pub const Type = extern union { => unreachable, .array_u8_sentinel_0 => return self.copyPayloadShallow(allocator, Payload.Array_u8_Sentinel0), + .array_u8 => return self.copyPayloadShallow(allocator, Payload.Array_u8), .array => { const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise); const new_payload = try allocator.create(Payload.Array); @@ -340,6 +341,17 @@ pub const Type = extern union { }; return Type{ .ptr_otherwise = &new_payload.base }; }, + .array_sentinel => { + const payload = @fieldParentPtr(Payload.ArraySentinel, "base", self.ptr_otherwise); + const new_payload = try allocator.create(Payload.ArraySentinel); + new_payload.* = .{ + .base = payload.base, + .len = payload.len, + .sentinel = try payload.sentinel.copy(allocator), + .elem_type = try payload.elem_type.copy(allocator), + }; + return Type{ .ptr_otherwise = &new_payload.base }; + }, .int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned), .int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned), .function => { @@ -445,6 +457,10 @@ pub const Type = extern union { try payload.return_type.format("", .{}, out_stream); }, + .array_u8 => { + const payload = @fieldParentPtr(Payload.Array_u8, "base", ty.ptr_otherwise); + return out_stream.print("[{}]u8", .{payload.len}); + }, .array_u8_sentinel_0 => { const payload = @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", ty.ptr_otherwise); return out_stream.print("[{}:0]u8", .{payload.len}); @@ -455,6 +471,12 @@ pub const Type = extern union { ty = payload.elem_type; continue; }, + .array_sentinel => { + const payload = @fieldParentPtr(Payload.ArraySentinel, "base", ty.ptr_otherwise); + try out_stream.print("[{}:{}]", .{ payload.len, payload.sentinel }); + ty = payload.elem_type; + continue; + }, .single_const_pointer => { const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise); try out_stream.writeAll("*const "); @@ -588,6 +610,8 @@ pub const Type = extern union { => true, // TODO lazy types .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0, + .array_u8 => self.arrayLen() != 0, + .array_sentinel => self.elemType().hasCodeGenBits(), .single_const_pointer => self.elemType().hasCodeGenBits(), .single_mut_pointer => self.elemType().hasCodeGenBits(), .int_signed => self.cast(Payload.IntSigned).?.bits == 0, @@ -616,6 +640,7 @@ pub const Type = extern union { .i8, .bool, .array_u8_sentinel_0, + .array_u8, => return 1, .fn_noreturn_no_args, // represents machine code; not a pointer @@ -659,7 +684,7 @@ pub const Type = extern union { .anyerror => return 2, // TODO revisit this when we have the concept of the error tag type - .array => return self.cast(Payload.Array).?.elem_type.abiAlignment(target), + .array, .array_sentinel => return self.elemType().abiAlignment(target), .int_signed, .int_unsigned => { const bits: u16 = if (self.cast(Payload.IntSigned)) |pl| @@ -717,12 +742,18 @@ pub const Type = extern union { .bool, => return 1, - .array_u8_sentinel_0 => @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", self.ptr_otherwise).len, + .array_u8 => @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", self.ptr_otherwise).len, + .array_u8_sentinel_0 => @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", self.ptr_otherwise).len + 1, .array => { const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise); const elem_size = std.math.max(payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target)); return payload.len * elem_size; }, + .array_sentinel => { + const payload = @fieldParentPtr(Payload.ArraySentinel, "base", self.ptr_otherwise); + const elem_size = std.math.max(payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target)); + return (payload.len + 1) * elem_size; + }, .i16, .u16 => return 2, .i32, .u32 => return 4, .i64, .u64 => return 8, @@ -818,6 +849,8 @@ pub const Type = extern union { .@"null", .@"undefined", .array, + .array_sentinel, + .array_u8, .array_u8_sentinel_0, .const_slice_u8, .fn_noreturn_no_args, @@ -875,6 +908,8 @@ pub const Type = extern union { .@"null", .@"undefined", .array, + .array_sentinel, + .array_u8, .array_u8_sentinel_0, .single_const_pointer, .single_mut_pointer, @@ -931,6 +966,8 @@ pub const Type = extern union { .@"null", .@"undefined", .array, + .array_sentinel, + .array_u8, .array_u8_sentinel_0, .fn_noreturn_no_args, .fn_void_no_args, @@ -988,6 +1025,8 @@ pub const Type = extern union { .@"null", .@"undefined", .array, + .array_sentinel, + .array_u8, .array_u8_sentinel_0, .fn_noreturn_no_args, .fn_void_no_args, @@ -1072,9 +1111,10 @@ pub const Type = extern union { => unreachable, .array => self.cast(Payload.Array).?.elem_type, + .array_sentinel => self.cast(Payload.ArraySentinel).?.elem_type, .single_const_pointer => self.castPointer().?.pointee_type, .single_mut_pointer => self.castPointer().?.pointee_type, - .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8), + .array_u8, .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8), .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int), }; } @@ -1176,6 +1216,8 @@ pub const Type = extern union { => unreachable, .array => self.cast(Payload.Array).?.len, + .array_sentinel => self.cast(Payload.ArraySentinel).?.len, + .array_u8 => self.cast(Payload.Array_u8).?.len, .array_u8_sentinel_0 => self.cast(Payload.Array_u8_Sentinel0).?.len, }; } @@ -1232,7 +1274,8 @@ pub const Type = extern union { .optional_single_const_pointer, => unreachable, - .array => return null, + .array, .array_u8 => return null, + .array_sentinel => return self.cast(Payload.ArraySentinel).?.sentinel, .array_u8_sentinel_0 => return Value.initTag(.zero), }; } @@ -1266,10 +1309,12 @@ pub const Type = extern union { .fn_ccc_void_no_args, .function, .array, + .array_sentinel, + .array_u8, + .array_u8_sentinel_0, .single_const_pointer, .single_mut_pointer, .single_const_pointer_to_comptime_int, - .array_u8_sentinel_0, .const_slice_u8, .int_unsigned, .u8, @@ -1324,10 +1369,12 @@ pub const Type = extern union { .fn_ccc_void_no_args, .function, .array, + .array_sentinel, + .array_u8, + .array_u8_sentinel_0, .single_const_pointer, .single_mut_pointer, .single_const_pointer_to_comptime_int, - .array_u8_sentinel_0, .const_slice_u8, .int_signed, .i8, @@ -1382,10 +1429,12 @@ pub const Type = extern union { .fn_ccc_void_no_args, .function, .array, + .array_sentinel, + .array_u8, + .array_u8_sentinel_0, .single_const_pointer, .single_mut_pointer, .single_const_pointer_to_comptime_int, - .array_u8_sentinel_0, .const_slice_u8, .optional, .optional_single_mut_pointer, @@ -1438,10 +1487,12 @@ pub const Type = extern union { .fn_ccc_void_no_args, .function, .array, + .array_sentinel, + .array_u8, + .array_u8_sentinel_0, .single_const_pointer, .single_mut_pointer, .single_const_pointer_to_comptime_int, - .array_u8_sentinel_0, .const_slice_u8, .int_unsigned, .int_signed, @@ -1523,10 +1574,12 @@ pub const Type = extern union { .@"null", .@"undefined", .array, + .array_sentinel, + .array_u8, + .array_u8_sentinel_0, .single_const_pointer, .single_mut_pointer, .single_const_pointer_to_comptime_int, - .array_u8_sentinel_0, .const_slice_u8, .u8, .i8, @@ -1584,10 +1637,12 @@ pub const Type = extern union { .@"null", .@"undefined", .array, + .array_sentinel, + .array_u8, + .array_u8_sentinel_0, .single_const_pointer, .single_mut_pointer, .single_const_pointer_to_comptime_int, - .array_u8_sentinel_0, .const_slice_u8, .u8, .i8, @@ -1644,10 +1699,12 @@ pub const Type = extern union { .@"null", .@"undefined", .array, + .array_sentinel, + .array_u8, + .array_u8_sentinel_0, .single_const_pointer, .single_mut_pointer, .single_const_pointer_to_comptime_int, - .array_u8_sentinel_0, .const_slice_u8, .u8, .i8, @@ -1704,10 +1761,12 @@ pub const Type = extern union { .@"null", .@"undefined", .array, + .array_sentinel, + .array_u8, + .array_u8_sentinel_0, .single_const_pointer, .single_mut_pointer, .single_const_pointer_to_comptime_int, - .array_u8_sentinel_0, .const_slice_u8, .u8, .i8, @@ -1761,10 +1820,12 @@ pub const Type = extern union { .@"null", .@"undefined", .array, + .array_sentinel, + .array_u8, + .array_u8_sentinel_0, .single_const_pointer, .single_mut_pointer, .single_const_pointer_to_comptime_int, - .array_u8_sentinel_0, .const_slice_u8, .u8, .i8, @@ -1818,10 +1879,12 @@ pub const Type = extern union { .@"null", .@"undefined", .array, + .array_sentinel, + .array_u8, + .array_u8_sentinel_0, .single_const_pointer, .single_mut_pointer, .single_const_pointer_to_comptime_int, - .array_u8_sentinel_0, .const_slice_u8, .u8, .i8, @@ -1895,10 +1958,12 @@ pub const Type = extern union { .fn_ccc_void_no_args, .function, .array, + .array_sentinel, + .array_u8, + .array_u8_sentinel_0, .single_const_pointer, .single_mut_pointer, .single_const_pointer_to_comptime_int, - .array_u8_sentinel_0, .const_slice_u8, .optional, .optional_single_mut_pointer, @@ -1944,6 +2009,7 @@ pub const Type = extern union { .fn_ccc_void_no_args, .function, .single_const_pointer_to_comptime_int, + .array_sentinel, .array_u8_sentinel_0, .const_slice_u8, .c_void, @@ -1971,11 +2037,10 @@ pub const Type = extern union { return null; } }, - .array => { - const array = ty.cast(Payload.Array).?; - if (array.len == 0) + .array, .array_u8 => { + if (ty.arrayLen() == 0) return Value.initTag(.empty_array); - ty = array.elem_type; + ty = ty.elemType(); continue; }, .single_const_pointer, .single_mut_pointer => { @@ -2022,7 +2087,6 @@ pub const Type = extern union { .fn_ccc_void_no_args, .function, .single_const_pointer_to_comptime_int, - .array_u8_sentinel_0, .const_slice_u8, .c_void, .void, @@ -2032,6 +2096,9 @@ pub const Type = extern union { .int_unsigned, .int_signed, .array, + .array_sentinel, + .array_u8, + .array_u8_sentinel_0, .single_const_pointer, .single_mut_pointer, .optional, @@ -2090,8 +2157,10 @@ pub const Type = extern union { const_slice_u8, // See last_no_payload_tag below. // After this, the tag requires a payload. + array_u8, array_u8_sentinel_0, array, + array_sentinel, single_const_pointer, single_mut_pointer, int_signed, @@ -2114,11 +2183,25 @@ pub const Type = extern union { len: u64, }; + pub const Array_u8 = struct { + base: Payload = Payload{ .tag = .array_u8 }, + + len: u64, + }; + pub const Array = struct { base: Payload = Payload{ .tag = .array }, - elem_type: Type, len: u64, + elem_type: Type, + }; + + pub const ArraySentinel = struct { + base: Payload = Payload{ .tag = .array_sentinel }, + + len: u64, + sentinel: Value, + elem_type: Type, }; pub const Pointer = struct { diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index 3e8400dca7..a38c1b8373 100644 --- a/src-self-hosted/zir.zig +++ b/src-self-hosted/zir.zig @@ -47,6 +47,10 @@ pub const Inst = struct { array_cat, /// Array multiplication `a ** b` array_mul, + /// Create an array type + array_type, + /// Create an array type with sentinel + array_type_sentinel, /// Function parameter value. These must be first in a function's main block, /// in respective order with the parameters. arg, @@ -268,6 +272,7 @@ pub const Inst = struct { .addwrap, .array_cat, .array_mul, + .array_type, .bitand, .bitor, .div, @@ -294,6 +299,7 @@ pub const Inst = struct { => BinOp, .arg => Arg, + .array_type_sentinel => ArrayTypeSentinel, .block => Block, .@"break" => Break, .breakvoid => BreakVoid, @@ -333,6 +339,8 @@ pub const Inst = struct { .alloc_inferred, .array_cat, .array_mul, + .array_type, + .array_type_sentinel, .arg, .as, .@"asm", @@ -849,6 +857,18 @@ pub const Inst = struct { sentinel: ?*Inst = null, }, }; + + pub const ArrayTypeSentinel = struct { + pub const base_tag = Tag.array_type_sentinel; + base: Inst, + + positionals: struct { + len: *Inst, + sentinel: *Inst, + elem_type: *Inst, + }, + kw_args: struct {}, + }; }; pub const ErrorMsg = struct { diff --git a/src-self-hosted/zir_sema.zig b/src-self-hosted/zir_sema.zig index d51d0d0f7f..593330f782 100644 --- a/src-self-hosted/zir_sema.zig +++ b/src-self-hosted/zir_sema.zig @@ -113,6 +113,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .unwrap_err_safe => return analyzeInstUnwrapErr(mod, scope, old_inst.castTag(.unwrap_err_safe).?, true), .unwrap_err_unsafe => return analyzeInstUnwrapErr(mod, scope, old_inst.castTag(.unwrap_err_unsafe).?, false), .ensure_err_payload_void => return analyzeInstEnsureErrPayloadVoid(mod, scope, old_inst.castTag(.ensure_err_payload_void).?), + .array_type => return analyzeInstArrayType(mod, scope, old_inst.castTag(.array_type).?), + .array_type_sentinel => return analyzeInstArrayTypeSentinel(mod, scope, old_inst.castTag(.array_type_sentinel).?), } } @@ -676,31 +678,24 @@ fn analyzeInstIntType(mod: *Module, scope: *Scope, inttype: *zir.Inst.IntType) I fn analyzeInstOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp) InnerError!*Inst { const child_type = try resolveType(mod, scope, optional.positionals.operand); - return mod.constType(scope, optional.base.src, Type.initPayload(switch (child_type.tag()) { - .single_const_pointer => blk: { - const payload = try scope.arena().create(Type.Payload.Pointer); - payload.* = .{ - .base = .{ .tag = .optional_single_const_pointer }, - .pointee_type = child_type.elemType(), - }; - break :blk &payload.base; - }, - .single_mut_pointer => blk: { - const payload = try scope.arena().create(Type.Payload.Pointer); - payload.* = .{ - .base = .{ .tag = .optional_single_mut_pointer }, - .pointee_type = child_type.elemType(), - }; - break :blk &payload.base; - }, - else => blk: { - const payload = try scope.arena().create(Type.Payload.Optional); - payload.* = .{ - .child_type = child_type, - }; - break :blk &payload.base; - }, - })); + return mod.constType(scope, optional.base.src, try mod.optionalType(scope, child_type)); +} + +fn analyzeInstArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) InnerError!*Inst { + // TODO these should be lazily evaluated + const len = try resolveInstConst(mod, scope, array.positionals.lhs); + const elem_type = try resolveType(mod, scope, array.positionals.rhs); + + return mod.constType(scope, array.base.src, try mod.arrayType(scope, len.val.toUnsignedInt(), null, elem_type)); +} + +fn analyzeInstArrayTypeSentinel(mod: *Module, scope: *Scope, array: *zir.Inst.ArrayTypeSentinel) InnerError!*Inst { + // TODO these should be lazily evaluated + const len = try resolveInstConst(mod, scope, array.positionals.len); + const sentinel = try resolveInstConst(mod, scope, array.positionals.sentinel); + const elem_type = try resolveType(mod, scope, array.positionals.elem_type); + + return mod.constType(scope, array.base.src, try mod.arrayType(scope, len.val.toUnsignedInt(), sentinel.val, elem_type)); } fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst { From 31d8efc6b32a6b60b7ab292ac50e0f6c02bc140e Mon Sep 17 00:00:00 2001 From: Vexu Date: Tue, 18 Aug 2020 13:57:09 +0300 Subject: [PATCH 4/7] stage2: validate param and variable types --- src-self-hosted/type.zig | 39 ++++++++++++++++++++++++++++++++++++ src-self-hosted/zir_sema.zig | 10 ++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 5329841d4b..25f0be16f9 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -1062,6 +1062,45 @@ pub const Type = extern union { } } + /// Returns if type can be used for a runtime variable + pub fn isValidVarType(self: Type) bool { + var ty = self; + while (true) switch (ty.zigTypeTag()) { + .Bool, + .Int, + .Float, + .ErrorSet, + .Enum, + .Frame, + .AnyFrame, + .Vector, + => return true, + + .BoundFn, + .ComptimeFloat, + .ComptimeInt, + .EnumLiteral, + .NoReturn, + .Type, + .Void, + .Undefined, + .Null, + .Opaque, + => return false, + + .Optional => { + var buf: Payload.Pointer = undefined; + return ty.optionalChild(&buf).isValidVarType(); + }, + .Pointer, .Array => ty = ty.elemType(), + + .ErrorUnion => @panic("TODO fn isValidVarType"), + .Fn => @panic("TODO fn isValidVarType"), + .Struct => @panic("TODO struct isValidVarType"), + .Union => @panic("TODO union isValidVarType"), + }; + } + /// Asserts the type is a pointer or array type. pub fn elemType(self: Type) Type { return switch (self.tag()) { diff --git a/src-self-hosted/zir_sema.zig b/src-self-hosted/zir_sema.zig index 593330f782..49009d8dec 100644 --- a/src-self-hosted/zir_sema.zig +++ b/src-self-hosted/zir_sema.zig @@ -364,6 +364,9 @@ fn analyzeInstEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst. fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const var_type = try resolveType(mod, scope, inst.positionals.operand); + if (!var_type.isValidVarType()) { + return mod.fail(scope, inst.base.src, "variable of type '{}' must be const or comptime", .{var_type}); + } const ptr_type = try mod.singlePtrType(scope, inst.base.src, true, var_type); const b = try mod.requireRuntimeBlock(scope, inst.base.src); return mod.addNoOp(b, inst.base.src, ptr_type, .alloc); @@ -760,7 +763,12 @@ fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) Inne const arena = scope.arena(); const param_types = try arena.alloc(Type, fntype.positionals.param_types.len); for (fntype.positionals.param_types) |param_type, i| { - param_types[i] = try resolveType(mod, scope, param_type); + const resolved = try resolveType(mod, scope, param_type); + // TODO skip for comptime params + if (!resolved.isValidVarType()) { + return mod.fail(scope, param_type.src, "parameter of type '{}' must be declared comptime", .{resolved}); + } + param_types[i] = resolved; } const payload = try arena.create(Type.Payload.Function); From e0b01bd4a98e2605f197a04f84e0c281ccc90f81 Mon Sep 17 00:00:00 2001 From: Vexu Date: Tue, 18 Aug 2020 14:28:33 +0300 Subject: [PATCH 5/7] stage2: enum literals --- src-self-hosted/astgen.zig | 10 +++++++++- src-self-hosted/type.zig | 30 +++++++++++++++++++++++++++++- src-self-hosted/value.zig | 35 +++++++++++++++++++++++++++++++++-- src-self-hosted/zir.zig | 14 ++++++++++++++ src-self-hosted/zir_sema.zig | 13 +++++++++++++ 5 files changed, 98 insertions(+), 4 deletions(-) diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig index 46ccde10f1..87f69e053a 100644 --- a/src-self-hosted/astgen.zig +++ b/src-self-hosted/astgen.zig @@ -130,6 +130,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .GroupedExpression => return expr(mod, scope, rl, node.castTag(.GroupedExpression).?.expr), .ArrayType => return rlWrap(mod, scope, rl, try arrayType(mod, scope, node.castTag(.ArrayType).?)), .ArrayTypeSentinel => return rlWrap(mod, scope, rl, try arrayTypeSentinel(mod, scope, node.castTag(.ArrayTypeSentinel).?)), + .EnumLiteral => return rlWrap(mod, scope, rl, try enumLiteral(mod, scope, node.castTag(.EnumLiteral).?)), .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}), .Catch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Catch", .{}), @@ -158,7 +159,6 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .ErrorType => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorType", .{}), .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}), .AnyFrameType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyFrameType", .{}), - .EnumLiteral => return mod.failNode(scope, node, "TODO implement astgen.expr for .EnumLiteral", .{}), .MultilineStringLiteral => return mod.failNode(scope, node, "TODO implement astgen.expr for .MultilineStringLiteral", .{}), .CharLiteral => return mod.failNode(scope, node, "TODO implement astgen.expr for .CharLiteral", .{}), .ErrorSetDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorSetDecl", .{}), @@ -527,6 +527,14 @@ fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSenti }, .{}); } +fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst { + const tree = scope.tree(); + const src = tree.token_locs[node.name].start; + const name = try identifierTokenString(mod, scope, node.name); + + return addZIRInst(mod, scope, src, zir.Inst.EnumLiteral, .{ .name = name }, .{}); +} + fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[node.rtoken].start; diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 25f0be16f9..a6ec90a35b 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -75,6 +75,7 @@ pub const Type = extern union { .optional_single_const_pointer, .optional_single_mut_pointer, => return .Optional, + .enum_literal => return .EnumLiteral, } } @@ -127,6 +128,7 @@ pub const Type = extern union { if (zig_tag_a != zig_tag_b) return false; switch (zig_tag_a) { + .EnumLiteral => return true, .Type => return true, .Void => return true, .Bool => return true, @@ -211,7 +213,6 @@ pub const Type = extern union { .Frame, .AnyFrame, .Vector, - .EnumLiteral, => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }), } } @@ -327,6 +328,7 @@ pub const Type = extern union { .fn_ccc_void_no_args, .single_const_pointer_to_comptime_int, .const_slice_u8, + .enum_literal, => unreachable, .array_u8_sentinel_0 => return self.copyPayloadShallow(allocator, Payload.Array_u8_Sentinel0), @@ -437,6 +439,7 @@ pub const Type = extern union { .noreturn, => return out_stream.writeAll(@tagName(t)), + .enum_literal => return out_stream.writeAll("@TypeOf(.EnumLiteral)"), .@"null" => return out_stream.writeAll("@TypeOf(null)"), .@"undefined" => return out_stream.writeAll("@TypeOf(undefined)"), @@ -561,6 +564,7 @@ pub const Type = extern union { .fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type), .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type), .const_slice_u8 => return Value.initTag(.const_slice_u8_type), + .enum_literal => return Value.initTag(.enum_literal_type), else => { const ty_payload = try allocator.create(Value.Payload.Ty); ty_payload.* = .{ .ty = self }; @@ -625,6 +629,7 @@ pub const Type = extern union { .noreturn, .@"null", .@"undefined", + .enum_literal, => false, }; } @@ -716,6 +721,7 @@ pub const Type = extern union { .noreturn, .@"null", .@"undefined", + .enum_literal, => unreachable, }; } @@ -736,6 +742,7 @@ pub const Type = extern union { .noreturn => unreachable, .@"null" => unreachable, .@"undefined" => unreachable, + .enum_literal => unreachable, .u8, .i8, @@ -863,6 +870,7 @@ pub const Type = extern union { .optional, .optional_single_mut_pointer, .optional_single_const_pointer, + .enum_literal, => false, .single_const_pointer, @@ -924,6 +932,7 @@ pub const Type = extern union { .optional, .optional_single_mut_pointer, .optional_single_const_pointer, + .enum_literal, => false, .const_slice_u8 => true, @@ -980,6 +989,7 @@ pub const Type = extern union { .optional, .optional_single_mut_pointer, .optional_single_const_pointer, + .enum_literal, => false, .single_const_pointer, @@ -1042,6 +1052,7 @@ pub const Type = extern union { .optional, .optional_single_mut_pointer, .optional_single_const_pointer, + .enum_literal, => false, }; } @@ -1147,6 +1158,7 @@ pub const Type = extern union { .optional, .optional_single_const_pointer, .optional_single_mut_pointer, + .enum_literal, => unreachable, .array => self.cast(Payload.Array).?.elem_type, @@ -1252,6 +1264,7 @@ pub const Type = extern union { .optional, .optional_single_mut_pointer, .optional_single_const_pointer, + .enum_literal, => unreachable, .array => self.cast(Payload.Array).?.len, @@ -1311,6 +1324,7 @@ pub const Type = extern union { .optional, .optional_single_mut_pointer, .optional_single_const_pointer, + .enum_literal, => unreachable, .array, .array_u8 => return null, @@ -1368,6 +1382,7 @@ pub const Type = extern union { .optional, .optional_single_mut_pointer, .optional_single_const_pointer, + .enum_literal, => false, .int_signed, @@ -1428,6 +1443,7 @@ pub const Type = extern union { .optional, .optional_single_mut_pointer, .optional_single_const_pointer, + .enum_literal, => false, .int_unsigned, @@ -1478,6 +1494,7 @@ pub const Type = extern union { .optional, .optional_single_mut_pointer, .optional_single_const_pointer, + .enum_literal, => unreachable, .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits }, @@ -1546,6 +1563,7 @@ pub const Type = extern union { .optional, .optional_single_mut_pointer, .optional_single_const_pointer, + .enum_literal, => false, .usize, @@ -1643,6 +1661,7 @@ pub const Type = extern union { .optional, .optional_single_mut_pointer, .optional_single_const_pointer, + .enum_literal, => unreachable, }; } @@ -1706,6 +1725,7 @@ pub const Type = extern union { .optional, .optional_single_mut_pointer, .optional_single_const_pointer, + .enum_literal, => unreachable, } } @@ -1768,6 +1788,7 @@ pub const Type = extern union { .optional, .optional_single_mut_pointer, .optional_single_const_pointer, + .enum_literal, => unreachable, } } @@ -1830,6 +1851,7 @@ pub const Type = extern union { .optional, .optional_single_mut_pointer, .optional_single_const_pointer, + .enum_literal, => unreachable, }; } @@ -1889,6 +1911,7 @@ pub const Type = extern union { .optional, .optional_single_mut_pointer, .optional_single_const_pointer, + .enum_literal, => unreachable, }; } @@ -1948,6 +1971,7 @@ pub const Type = extern union { .optional, .optional_single_mut_pointer, .optional_single_const_pointer, + .enum_literal, => unreachable, }; } @@ -2007,6 +2031,7 @@ pub const Type = extern union { .optional, .optional_single_mut_pointer, .optional_single_const_pointer, + .enum_literal, => false, }; } @@ -2055,6 +2080,7 @@ pub const Type = extern union { .optional, .optional_single_mut_pointer, .optional_single_const_pointer, + .enum_literal, => return null, .void => return Value.initTag(.void_value), @@ -2143,6 +2169,7 @@ pub const Type = extern union { .optional, .optional_single_mut_pointer, .optional_single_const_pointer, + .enum_literal, => return false, }; } @@ -2186,6 +2213,7 @@ pub const Type = extern union { comptime_int, comptime_float, noreturn, + enum_literal, @"null", @"undefined", fn_noreturn_no_args, diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index c8af4716a6..b6356d9b17 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -60,6 +60,7 @@ pub const Value = extern union { fn_ccc_void_no_args_type, single_const_pointer_to_comptime_int_type, const_slice_u8_type, + enum_literal_type, undef, zero, @@ -87,6 +88,7 @@ pub const Value = extern union { float_32, float_64, float_128, + enum_literal, pub const last_no_payload_tag = Tag.bool_false; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; @@ -164,6 +166,7 @@ pub const Value = extern union { .fn_ccc_void_no_args_type, .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, + .enum_literal_type, .undef, .zero, .void_value, @@ -213,7 +216,7 @@ pub const Value = extern union { }; return Value{ .ptr_otherwise = &new_payload.base }; }, - .bytes => return self.copyPayloadShallow(allocator, Payload.Bytes), + .enum_literal, .bytes => return self.copyPayloadShallow(allocator, Payload.Bytes), .repeated => { const payload = @fieldParentPtr(Payload.Repeated, "base", self.ptr_otherwise); const new_payload = try allocator.create(Payload.Repeated); @@ -285,6 +288,7 @@ pub const Value = extern union { .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"), .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"), .const_slice_u8_type => return out_stream.writeAll("[]const u8"), + .enum_literal_type => return out_stream.writeAll("@TypeOf(.EnumLiteral)"), .null_value => return out_stream.writeAll("null"), .undef => return out_stream.writeAll("undefined"), @@ -318,7 +322,7 @@ pub const Value = extern union { val = elem_ptr.array_ptr; }, .empty_array => return out_stream.writeAll(".{}"), - .bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream), + .enum_literal, .bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream), .repeated => { try out_stream.writeAll("(repeated) "); val = val.cast(Payload.Repeated).?.val; @@ -391,6 +395,7 @@ pub const Value = extern union { .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args), .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int), .const_slice_u8_type => Type.initTag(.const_slice_u8), + .enum_literal_type => Type.initTag(.enum_literal), .undef, .zero, @@ -414,6 +419,7 @@ pub const Value = extern union { .float_32, .float_64, .float_128, + .enum_literal, => unreachable, }; } @@ -462,6 +468,7 @@ pub const Value = extern union { .fn_ccc_void_no_args_type, .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, + .enum_literal_type, .null_value, .function, .ref_val, @@ -476,6 +483,7 @@ pub const Value = extern union { .void_value, .unreachable_value, .empty_array, + .enum_literal, => unreachable, .undef => unreachable, @@ -537,6 +545,7 @@ pub const Value = extern union { .fn_ccc_void_no_args_type, .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, + .enum_literal_type, .null_value, .function, .ref_val, @@ -551,6 +560,7 @@ pub const Value = extern union { .void_value, .unreachable_value, .empty_array, + .enum_literal, => unreachable, .undef => unreachable, @@ -612,6 +622,7 @@ pub const Value = extern union { .fn_ccc_void_no_args_type, .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, + .enum_literal_type, .null_value, .function, .ref_val, @@ -626,6 +637,7 @@ pub const Value = extern union { .void_value, .unreachable_value, .empty_array, + .enum_literal, => unreachable, .undef => unreachable, @@ -713,6 +725,7 @@ pub const Value = extern union { .fn_ccc_void_no_args_type, .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, + .enum_literal_type, .null_value, .function, .ref_val, @@ -728,6 +741,7 @@ pub const Value = extern union { .void_value, .unreachable_value, .empty_array, + .enum_literal, => unreachable, .zero, @@ -793,6 +807,7 @@ pub const Value = extern union { .fn_ccc_void_no_args_type, .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, + .enum_literal_type, .null_value, .function, .ref_val, @@ -807,6 +822,7 @@ pub const Value = extern union { .void_value, .unreachable_value, .empty_array, + .enum_literal, => unreachable, .zero, @@ -953,6 +969,7 @@ pub const Value = extern union { .fn_ccc_void_no_args_type, .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, + .enum_literal_type, .bool_true, .bool_false, .null_value, @@ -970,6 +987,7 @@ pub const Value = extern union { .empty_array, .void_value, .unreachable_value, + .enum_literal, => unreachable, .zero => false, @@ -1025,6 +1043,7 @@ pub const Value = extern union { .fn_ccc_void_no_args_type, .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, + .enum_literal_type, .null_value, .function, .ref_val, @@ -1036,6 +1055,7 @@ pub const Value = extern union { .void_value, .unreachable_value, .empty_array, + .enum_literal, => unreachable, .zero, @@ -1102,6 +1122,11 @@ pub const Value = extern union { } pub fn eql(a: Value, b: Value) bool { + if (a.tag() == b.tag() and a.tag() == .enum_literal) { + const a_name = @fieldParentPtr(Payload.Bytes, "base", a.ptr_otherwise).data; + const b_name = @fieldParentPtr(Payload.Bytes, "base", b.ptr_otherwise).data; + return std.mem.eql(u8, a_name, b_name); + } // TODO non numerical comparisons return compare(a, .eq, b); } @@ -1151,6 +1176,7 @@ pub const Value = extern union { .fn_ccc_void_no_args_type, .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, + .enum_literal_type, .zero, .bool_true, .bool_false, @@ -1170,6 +1196,7 @@ pub const Value = extern union { .void_value, .unreachable_value, .empty_array, + .enum_literal, => unreachable, .ref_val => self.cast(Payload.RefVal).?.val, @@ -1227,6 +1254,7 @@ pub const Value = extern union { .fn_ccc_void_no_args_type, .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, + .enum_literal_type, .zero, .bool_true, .bool_false, @@ -1246,6 +1274,7 @@ pub const Value = extern union { .float_128, .void_value, .unreachable_value, + .enum_literal, => unreachable, .empty_array => unreachable, // out of bounds array index @@ -1320,6 +1349,7 @@ pub const Value = extern union { .fn_ccc_void_no_args_type, .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, + .enum_literal_type, .zero, .empty_array, .bool_true, @@ -1339,6 +1369,7 @@ pub const Value = extern union { .float_64, .float_128, .void_value, + .enum_literal, => false, .undef => unreachable, diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index a38c1b8373..235a4c931b 100644 --- a/src-self-hosted/zir.zig +++ b/src-self-hosted/zir.zig @@ -231,6 +231,8 @@ pub const Inst = struct { unwrap_err_unsafe, /// Takes a *E!T and raises a compiler error if T != void ensure_err_payload_void, + /// Enum literal + enum_literal, pub fn Type(tag: Tag) type { return switch (tag) { @@ -326,6 +328,7 @@ pub const Inst = struct { .elemptr => ElemPtr, .condbr => CondBr, .ptr_type => PtrType, + .enum_literal => EnumLiteral, }; } @@ -410,6 +413,7 @@ pub const Inst = struct { .unwrap_err_unsafe, .ptr_type, .ensure_err_payload_void, + .enum_literal, => false, .@"break", @@ -869,6 +873,16 @@ pub const Inst = struct { }, kw_args: struct {}, }; + + pub const EnumLiteral = struct { + pub const base_tag = Tag.enum_literal; + base: Inst, + + positionals: struct { + name: []const u8, + }, + kw_args: struct {}, + }; }; pub const ErrorMsg = struct { diff --git a/src-self-hosted/zir_sema.zig b/src-self-hosted/zir_sema.zig index 49009d8dec..5c473494fb 100644 --- a/src-self-hosted/zir_sema.zig +++ b/src-self-hosted/zir_sema.zig @@ -115,6 +115,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .ensure_err_payload_void => return analyzeInstEnsureErrPayloadVoid(mod, scope, old_inst.castTag(.ensure_err_payload_void).?), .array_type => return analyzeInstArrayType(mod, scope, old_inst.castTag(.array_type).?), .array_type_sentinel => return analyzeInstArrayTypeSentinel(mod, scope, old_inst.castTag(.array_type_sentinel).?), + .enum_literal => return analyzeInstEnumLiteral(mod, scope, old_inst.castTag(.enum_literal).?), } } @@ -701,6 +702,18 @@ fn analyzeInstArrayTypeSentinel(mod: *Module, scope: *Scope, array: *zir.Inst.Ar return mod.constType(scope, array.base.src, try mod.arrayType(scope, len.val.toUnsignedInt(), sentinel.val, elem_type)); } +fn analyzeInstEnumLiteral(mod: *Module, scope: *Scope, inst: *zir.Inst.EnumLiteral) InnerError!*Inst { + const payload = try scope.arena().create(Value.Payload.Bytes); + payload.* = .{ + .base = .{ .tag = .enum_literal }, + .data = try scope.arena().dupe(u8, inst.positionals.name), + }; + return mod.constInst(scope, inst.base.src, .{ + .ty = Type.initTag(.enum_literal), + .val = Value.initPayload(&payload.base), + }); +} + fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst { const operand = try resolveInst(mod, scope, unwrap.positionals.operand); assert(operand.ty.zigTypeTag() == .Pointer); From 2b45e23477605e15fecec3566b3dc90b71e2f7a7 Mon Sep 17 00:00:00 2001 From: Vexu Date: Tue, 18 Aug 2020 15:33:11 +0300 Subject: [PATCH 6/7] stage2: character literals and multiline strings --- lib/std/zig.zig | 101 +++++++++++++++++++++++++++++++++ src-self-hosted/astgen.zig | 53 ++++++++++++++++- src-self-hosted/zir_sema.zig | 1 + test/stage2/compare_output.zig | 32 +++++++++++ 4 files changed, 185 insertions(+), 2 deletions(-) diff --git a/lib/std/zig.zig b/lib/std/zig.zig index b070fbdcd5..36dfe74086 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -80,6 +80,107 @@ pub fn binNameAlloc( } } +/// Only validates escape sequence characters. +/// Slice must be valid utf8 starting and ending with "'" and exactly one codepoint in between. +pub fn parseCharLiteral( + slice: []const u8, + bad_index: *usize, // populated if error.InvalidCharacter is returned) +) error{InvalidCharacter}!u32 { + std.debug.assert(slice.len >= 3 and slice[0] == '\'' and slice[slice.len - 1] == '\''); + + if (slice[1] == '\\') { + switch (slice[2]) { + 'n' => return '\n', + 'r' => return '\r', + '\\' => return '\\', + 't' => return '\t', + '\'' => return '\'', + '"' => return '"', + 'x' => { + if (slice.len != 6) { + bad_index.* = slice.len - 2; + return error.InvalidCharacter; + } + + var value: u32 = 0; + for (slice[3..5]) |c, i| { + switch (slice[3]) { + '0'...'9' => { + value *= 16; + value += c - '0'; + }, + 'a'...'f' => { + value *= 16; + value += c - 'a'; + }, + 'A'...'F' => { + value *= 16; + value += c - 'a'; + }, + else => { + bad_index.* = i; + return error.InvalidCharacter; + }, + } + } + return value; + }, + 'u' => { + if (slice.len < 6 or slice[3] != '{') { + bad_index.* = 2; + return error.InvalidCharacter; + } + var value: u32 = 0; + for (slice[4..]) |c, i| { + if (value > 0x10ffff) { + bad_index.* = i; + return error.InvalidCharacter; + } + switch (c) { + '0'...'9' => { + value *= 16; + value += c - '0'; + }, + 'a'...'f' => { + value *= 16; + value += c - 'a'; + }, + 'A'...'F' => { + value *= 16; + value += c - 'A'; + }, + '}' => break, + else => { + bad_index.* = i; + return error.InvalidCharacter; + }, + } + } + return value; + }, + else => { + bad_index.* = 2; + return error.InvalidCharacter; + } + } + } + return std.unicode.utf8Decode(slice[1 .. slice.len - 1]) catch unreachable; +} + +test "parseCharLiteral" { + var bad_index: usize = undefined; + std.testing.expectEqual(try parseCharLiteral("'a'", &bad_index), 'a'); + std.testing.expectEqual(try parseCharLiteral("'ä'", &bad_index), 'ä'); + std.testing.expectEqual(try parseCharLiteral("'\\x00'", &bad_index), 0); + std.testing.expectEqual(try parseCharLiteral("'ぁ'", &bad_index), 0x3041); + std.testing.expectEqual(try parseCharLiteral("'\\u{3041}'", &bad_index), 0x3041); + + std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\x0'", &bad_index)); + std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\y'", &bad_index)); + std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\u'", &bad_index)); + std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\u{FFFFFF}'", &bad_index)); +} + test "" { @import("std").meta.refAllDecls(@This()); } diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig index 87f69e053a..f05020f6df 100644 --- a/src-self-hosted/astgen.zig +++ b/src-self-hosted/astgen.zig @@ -131,6 +131,8 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .ArrayType => return rlWrap(mod, scope, rl, try arrayType(mod, scope, node.castTag(.ArrayType).?)), .ArrayTypeSentinel => return rlWrap(mod, scope, rl, try arrayTypeSentinel(mod, scope, node.castTag(.ArrayTypeSentinel).?)), .EnumLiteral => return rlWrap(mod, scope, rl, try enumLiteral(mod, scope, node.castTag(.EnumLiteral).?)), + .MultilineStringLiteral => return rlWrap(mod, scope, rl, try multilineStrLiteral(mod, scope, node.castTag(.MultilineStringLiteral).?)), + .CharLiteral => return rlWrap(mod, scope, rl, try charLiteral(mod, scope, node.castTag(.CharLiteral).?)), .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}), .Catch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Catch", .{}), @@ -159,8 +161,6 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .ErrorType => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorType", .{}), .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}), .AnyFrameType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyFrameType", .{}), - .MultilineStringLiteral => return mod.failNode(scope, node, "TODO implement astgen.expr for .MultilineStringLiteral", .{}), - .CharLiteral => return mod.failNode(scope, node, "TODO implement astgen.expr for .CharLiteral", .{}), .ErrorSetDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorSetDecl", .{}), .ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}), .Comptime => return mod.failNode(scope, node, "TODO implement astgen.expr for .Comptime", .{}), @@ -497,6 +497,7 @@ fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst .val = Value.initTag(.usize_type), }); + // TODO check for [_]T const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr); const child_type = try expr(mod, scope, .{ .ty = meta_type }, node.rhs); @@ -515,6 +516,7 @@ fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSenti .val = Value.initTag(.usize_type), }); + // TODO check for [_]T const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr); const sentinel_uncasted = try expr(mod, scope, .none, node.sentinel); const elem_type = try expr(mod, scope, .{ .ty = meta_type }, node.rhs); @@ -1120,6 +1122,53 @@ fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.OneToken) Inner return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{}); } +fn multilineStrLiteral(mod: *Module, scope: *Scope, node: *ast.Node.MultilineStringLiteral) !*zir.Inst { + const tree = scope.tree(); + const lines = node.linesConst(); + const src = tree.token_locs[lines[0]].start; + + // line lengths and new lines + var len = lines.len - 1; + for (lines) |line| { + len += tree.tokenSlice(line).len - 2; + } + + const bytes = try scope.arena().alloc(u8, len); + var i: usize = 0; + for (lines) |line, line_i| { + if (line_i != 0) { + bytes[i] = '\n'; + i += 1; + } + const slice = tree.tokenSlice(line)[2..]; + mem.copy(u8, bytes[i..], slice); + i += slice.len; + } + + return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{}); +} + +fn charLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) !*zir.Inst { + const tree = scope.tree(); + const src = tree.token_locs[node.token].start; + const slice = tree.tokenSlice(node.token); + + var bad_index: usize = undefined; + const value = std.zig.parseCharLiteral(slice, &bad_index) catch |err| switch (err) { + error.InvalidCharacter => { + const bad_byte = slice[bad_index]; + return mod.fail(scope, src + bad_index, "invalid character: '{c}'\n", .{bad_byte}); + }, + }; + + const int_payload = try scope.arena().create(Value.Payload.Int_u64); + int_payload.* = .{ .int = value }; + return addZIRInstConst(mod, scope, src, .{ + .ty = Type.initTag(.comptime_int), + .val = Value.initPayload(&int_payload.base), + }); +} + fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) InnerError!*zir.Inst { const arena = scope.arena(); const tree = scope.tree(); diff --git a/src-self-hosted/zir_sema.zig b/src-self-hosted/zir_sema.zig index 5c473494fb..3a2a593539 100644 --- a/src-self-hosted/zir_sema.zig +++ b/src-self-hosted/zir_sema.zig @@ -365,6 +365,7 @@ fn analyzeInstEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst. fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const var_type = try resolveType(mod, scope, inst.positionals.operand); + // TODO this should happen only for var allocs if (!var_type.isValidVarType()) { return mod.fail(scope, inst.base.src, "variable of type '{}' must be const or comptime", .{var_type}); } diff --git a/test/stage2/compare_output.zig b/test/stage2/compare_output.zig index 4208cc3911..8b92674e9c 100644 --- a/test/stage2/compare_output.zig +++ b/test/stage2/compare_output.zig @@ -543,6 +543,38 @@ pub fn addCases(ctx: *TestContext) !void { , "", ); + + case.addCompareOutput( + \\export fn _start() noreturn { + \\ const ignore = + \\ \\ cool thx + \\ \\ + \\ ; + \\ add('ぁ', '\x03'); + \\ + \\ exit(); + \\} + \\ + \\fn add(a: u32, b: u32) void { + \\ assert(a + b == 12356); + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + \\ + \\fn exit() noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (0) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , + "", + ); } { From e4aefc6d0f9b08a98f566cb8280a6195c08f7a82 Mon Sep 17 00:00:00 2001 From: Vexu Date: Tue, 18 Aug 2020 22:42:35 +0300 Subject: [PATCH 7/7] stage2: split ref from lvalue and add compile error for invalid assignments --- src-self-hosted/astgen.zig | 160 +++++++++++++++++++++++++++++++---- src-self-hosted/zir.zig | 10 +-- src-self-hosted/zir_sema.zig | 6 +- 3 files changed, 153 insertions(+), 23 deletions(-) diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig index f05020f6df..9d40c7899a 100644 --- a/src-self-hosted/astgen.zig +++ b/src-self-hosted/astgen.zig @@ -20,6 +20,8 @@ pub const ResultLoc = union(enum) { /// The expression must generate a pointer rather than a value. For example, the left hand side /// of an assignment uses an "LValue" result location. lvalue, + /// The expression must generate a pointer + ref, /// The expression will be type coerced into this type, but it will be evaluated as an rvalue. ty: *zir.Inst, /// The expression must store its result into this typed pointer. @@ -46,6 +48,132 @@ pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*z /// Turn Zig AST into untyped ZIR istructions. pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst { + if (rl == .lvalue) { + switch (node.tag) { + .Root => unreachable, + .Use => unreachable, + .TestDecl => unreachable, + .DocComment => unreachable, + .VarDecl => unreachable, + .SwitchCase => unreachable, + .SwitchElse => unreachable, + .Else => unreachable, + .Payload => unreachable, + .PointerPayload => unreachable, + .PointerIndexPayload => unreachable, + .ErrorTag => unreachable, + .FieldInitializer => unreachable, + .ContainerField => unreachable, + + .Assign, + .AssignBitAnd, + .AssignBitOr, + .AssignBitShiftLeft, + .AssignBitShiftRight, + .AssignBitXor, + .AssignDiv, + .AssignSub, + .AssignSubWrap, + .AssignMod, + .AssignAdd, + .AssignAddWrap, + .AssignMul, + .AssignMulWrap, + .Add, + .AddWrap, + .Sub, + .SubWrap, + .Mul, + .MulWrap, + .Div, + .Mod, + .BitAnd, + .BitOr, + .BitShiftLeft, + .BitShiftRight, + .BitXor, + .BangEqual, + .EqualEqual, + .GreaterThan, + .GreaterOrEqual, + .LessThan, + .LessOrEqual, + .ArrayCat, + .ArrayMult, + .BoolAnd, + .BoolOr, + .Asm, + .StringLiteral, + .IntegerLiteral, + .Call, + .Unreachable, + .Return, + .If, + .While, + .BoolNot, + .AddressOf, + .FloatLiteral, + .UndefinedLiteral, + .BoolLiteral, + .NullLiteral, + .OptionalType, + .Block, + .LabeledBlock, + .Break, + .PtrType, + .GroupedExpression, + .ArrayType, + .ArrayTypeSentinel, + .EnumLiteral, + .MultilineStringLiteral, + .CharLiteral, + .Defer, + .Catch, + .ErrorUnion, + .MergeErrorSets, + .Range, + .OrElse, + .Await, + .BitNot, + .Negation, + .NegationWrap, + .Resume, + .Try, + .SliceType, + .Slice, + .ArrayInitializer, + .ArrayInitializerDot, + .StructInitializer, + .StructInitializerDot, + .Switch, + .For, + .Suspend, + .Continue, + .AnyType, + .ErrorType, + .FnProto, + .AnyFrameType, + .ErrorSetDecl, + .ContainerDecl, + .Comptime, + .Nosuspend, + => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}), + + // @field can be assigned to + .BuiltinCall => { + const call = node.castTag(.BuiltinCall).?; + const tree = scope.tree(); + const builtin_name = tree.tokenSlice(call.builtin_token); + + if (!mem.eql(u8, builtin_name, "@field")) { + return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}); + } + }, + + // can be assigned to + .UnwrapOptional, .Deref, .Period, .ArrayAccess, .Identifier => {}, + } + } switch (node.tag) { .Root => unreachable, // Top-level declaration. .Use => unreachable, // Top-level declaration. @@ -60,6 +188,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .PointerIndexPayload => unreachable, // Handled explicitly. .ErrorTag => unreachable, // Handled explicitly. .FieldInitializer => unreachable, // Handled explicitly. + .ContainerField => unreachable, // Handled explicitly. .Assign => return rlWrapVoid(mod, scope, rl, node, try assign(mod, scope, node.castTag(.Assign).?)), .AssignBitAnd => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitAnd).?, .bitand)), @@ -165,7 +294,6 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}), .Comptime => return mod.failNode(scope, node, "TODO implement astgen.expr for .Comptime", .{}), .Nosuspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Nosuspend", .{}), - .ContainerField => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerField", .{}), } } @@ -188,7 +316,7 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpr // proper type inference requires peer type resolution on the block's // break operand expressions. const branch_rl: ResultLoc = switch (label.result_loc) { - .discard, .none, .ty, .ptr, .lvalue => label.result_loc, + .discard, .none, .ty, .ptr, .lvalue, .ref => label.result_loc, .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = label.block_inst }, }; const operand = try expr(mod, parent_scope, branch_rl, rhs); @@ -427,7 +555,7 @@ fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerErr } fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst { - return expr(mod, scope, .lvalue, node.rhs); + return expr(mod, scope, .ref, node.rhs); } fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst { @@ -541,9 +669,9 @@ fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Si const tree = scope.tree(); const src = tree.token_locs[node.rtoken].start; - const operand = try expr(mod, scope, .lvalue, node.lhs); + const operand = try expr(mod, scope, .ref, node.lhs); const unwrapped_ptr = try addZIRUnOp(mod, scope, src, .unwrap_optional_safe, operand); - if (rl == .lvalue) return unwrapped_ptr; + if (rl == .lvalue or rl == .ref) return unwrapped_ptr; return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, unwrapped_ptr)); } @@ -718,13 +846,13 @@ const CondKind = union(enum) { return try expr(mod, &block_scope.base, .{ .ty = bool_type }, cond_node); }, .optional => { - const cond_ptr = try expr(mod, &block_scope.base, .lvalue, cond_node); + const cond_ptr = try expr(mod, &block_scope.base, .ref, cond_node); self.* = .{ .optional = cond_ptr }; const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, cond_ptr); return try addZIRUnOp(mod, &block_scope.base, src, .isnonnull, result); }, .err_union => { - const err_ptr = try expr(mod, &block_scope.base, .lvalue, cond_node); + const err_ptr = try expr(mod, &block_scope.base, .ref, cond_node); self.* = .{ .err_union = err_ptr }; const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, err_ptr); return try addZIRUnOp(mod, &block_scope.base, src, .iserr, result); @@ -819,7 +947,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn // proper type inference requires peer type resolution on the if's // branches. const branch_rl: ResultLoc = switch (rl) { - .discard, .none, .ty, .ptr, .lvalue => rl, + .discard, .none, .ty, .ptr, .lvalue, .ref => rl, .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block }, }; @@ -949,7 +1077,7 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W // proper type inference requires peer type resolution on the while's // branches. const branch_rl: ResultLoc = switch (rl) { - .discard, .none, .ty, .ptr, .lvalue => rl, + .discard, .none, .ty, .ptr, .lvalue, .ref => rl, .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = while_block }, }; @@ -1080,7 +1208,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo .local_ptr => { const local_ptr = s.cast(Scope.LocalPtr).?; if (mem.eql(u8, local_ptr.name, ident_name)) { - if (rl == .lvalue) { + if (rl == .lvalue or rl == .ref) { return local_ptr.ptr; } else { const result = try addZIRUnOp(mod, scope, src, .deref, local_ptr.ptr); @@ -1344,7 +1472,8 @@ fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) I _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result); return result; }, - .lvalue => { + .lvalue => unreachable, + .ref => { const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]); return addZIRUnOp(mod, scope, result.src, .ref, result); }, @@ -1395,9 +1524,10 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result); return result; }, - .lvalue => { - const operand = try expr(mod, scope, .lvalue, params[1]); - const result = try addZIRBinOp(mod, scope, src, .bitcast_lvalue, dest_type, operand); + .lvalue => unreachable, + .ref => { + const operand = try expr(mod, scope, .ref, params[1]); + const result = try addZIRBinOp(mod, scope, src, .bitcast_ref, dest_type, operand); return result; }, .ty => |result_ty| { @@ -1662,7 +1792,7 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result); return result; }, - .lvalue => { + .lvalue, .ref => { // We need a pointer but we have a value. return addZIRUnOp(mod, scope, result.src, .ref, result); }, diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index 235a4c931b..e8e21e5365 100644 --- a/src-self-hosted/zir.zig +++ b/src-self-hosted/zir.zig @@ -62,11 +62,11 @@ pub const Inst = struct { bitand, /// TODO delete this instruction, it has no purpose. bitcast, - /// An arbitrary typed pointer, which is to be used as an L-Value, is pointer-casted - /// to a new L-Value. The destination type is given by LHS. The cast is to be evaluated + /// An arbitrary typed pointer is pointer-casted to a new Pointer. + /// The destination type is given by LHS. The cast is to be evaluated /// as if it were a bit-cast operation from the operand pointer element type to the /// provided destination type. - bitcast_lvalue, + bitcast_ref, /// A typed result location pointer is bitcasted to a new result location pointer. /// The new result location pointer has an inferred type. bitcast_result_ptr, @@ -258,7 +258,7 @@ pub const Inst = struct { .ensure_result_non_error, .bitcast_result_ptr, .ref, - .bitcast_lvalue, + .bitcast_ref, .typeof, .single_const_ptr_type, .single_mut_ptr_type, @@ -349,7 +349,7 @@ pub const Inst = struct { .@"asm", .bitand, .bitcast, - .bitcast_lvalue, + .bitcast_ref, .bitcast_result_ptr, .bitor, .block, diff --git a/src-self-hosted/zir_sema.zig b/src-self-hosted/zir_sema.zig index 3a2a593539..fb39a1e077 100644 --- a/src-self-hosted/zir_sema.zig +++ b/src-self-hosted/zir_sema.zig @@ -29,7 +29,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .alloc => return analyzeInstAlloc(mod, scope, old_inst.castTag(.alloc).?), .alloc_inferred => return analyzeInstAllocInferred(mod, scope, old_inst.castTag(.alloc_inferred).?), .arg => return analyzeInstArg(mod, scope, old_inst.castTag(.arg).?), - .bitcast_lvalue => return analyzeInstBitCastLValue(mod, scope, old_inst.castTag(.bitcast_lvalue).?), + .bitcast_ref => return analyzeInstBitCastRef(mod, scope, old_inst.castTag(.bitcast_ref).?), .bitcast_result_ptr => return analyzeInstBitCastResultPtr(mod, scope, old_inst.castTag(.bitcast_result_ptr).?), .block => return analyzeInstBlock(mod, scope, old_inst.castTag(.block).?), .@"break" => return analyzeInstBreak(mod, scope, old_inst.castTag(.@"break").?), @@ -299,8 +299,8 @@ fn analyzeInstCoerceResultBlockPtr( return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultBlockPtr", .{}); } -fn analyzeInstBitCastLValue(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { - return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastLValue", .{}); +fn analyzeInstBitCastRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastRef", .{}); } fn analyzeInstBitCastResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {