Commit e4aefc6d0f

Vexu <git@vexu.eu>
2020-08-18 21:42:35
stage2: split ref from lvalue and add compile error for invalid assignments
1 parent 2b45e23
Changed files (3)
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);
         },
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,
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 {