Merge pull request #6086 from Vexu/stage2

Stage2: more astgen stuff
This commit is contained in:
Andrew Kelley 2020-08-18 22:02:55 -04:00 committed by GitHub
commit 771f40204e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 843 additions and 84 deletions

View File

@ -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());
}

View File

@ -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");

View File

@ -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)),
@ -100,6 +229,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 +256,15 @@ 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),
.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", .{}),
.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", .{}),
@ -139,8 +275,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", .{}),
@ -156,15 +290,10 @@ 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", .{}),
.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", .{}),
.Nosuspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Nosuspend", .{}),
.ContainerField => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerField", .{}),
}
}
@ -187,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);
@ -426,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 {
@ -484,13 +613,65 @@ 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),
});
// 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);
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),
});
// 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);
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 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;
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));
}
@ -568,6 +749,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,
@ -583,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);
@ -600,7 +863,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).?;
@ -680,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 },
};
@ -810,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 },
};
@ -941,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);
@ -983,6 +1250,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();
@ -1158,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);
},
@ -1209,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| {
@ -1476,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);
},

View File

@ -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,
@ -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,9 +328,11 @@ 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),
.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 +343,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 => {
@ -425,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)"),
@ -445,6 +460,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 +474,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 ");
@ -539,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 };
@ -588,6 +614,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,
@ -601,6 +629,7 @@ pub const Type = extern union {
.noreturn,
.@"null",
.@"undefined",
.enum_literal,
=> false,
};
}
@ -616,6 +645,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 +689,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|
@ -691,6 +721,7 @@ pub const Type = extern union {
.noreturn,
.@"null",
.@"undefined",
.enum_literal,
=> unreachable,
};
}
@ -711,18 +742,25 @@ pub const Type = extern union {
.noreturn => unreachable,
.@"null" => unreachable,
.@"undefined" => unreachable,
.enum_literal => unreachable,
.u8,
.i8,
.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 +856,8 @@ pub const Type = extern union {
.@"null",
.@"undefined",
.array,
.array_sentinel,
.array_u8,
.array_u8_sentinel_0,
.const_slice_u8,
.fn_noreturn_no_args,
@ -830,6 +870,7 @@ pub const Type = extern union {
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
=> false,
.single_const_pointer,
@ -875,6 +916,8 @@ pub const Type = extern union {
.@"null",
.@"undefined",
.array,
.array_sentinel,
.array_u8,
.array_u8_sentinel_0,
.single_const_pointer,
.single_mut_pointer,
@ -889,6 +932,7 @@ pub const Type = extern union {
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
=> false,
.const_slice_u8 => true,
@ -931,6 +975,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,
@ -943,6 +989,7 @@ pub const Type = extern union {
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
=> false,
.single_const_pointer,
@ -988,6 +1035,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,
@ -1003,6 +1052,7 @@ pub const Type = extern union {
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
=> false,
};
}
@ -1023,6 +1073,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()) {
@ -1069,12 +1158,14 @@ pub const Type = extern union {
.optional,
.optional_single_const_pointer,
.optional_single_mut_pointer,
.enum_literal,
=> 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),
};
}
@ -1173,9 +1264,12 @@ pub const Type = extern union {
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
=> 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,
};
}
@ -1230,9 +1324,11 @@ pub const Type = extern union {
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
=> 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 +1362,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,
@ -1284,6 +1382,7 @@ pub const Type = extern union {
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
=> false,
.int_signed,
@ -1324,10 +1423,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,
@ -1342,6 +1443,7 @@ pub const Type = extern union {
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
=> false,
.int_unsigned,
@ -1382,14 +1484,17 @@ 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,
.optional_single_const_pointer,
.enum_literal,
=> unreachable,
.int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits },
@ -1438,10 +1543,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,
@ -1456,6 +1563,7 @@ pub const Type = extern union {
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
=> false,
.usize,
@ -1523,10 +1631,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,
@ -1551,6 +1661,7 @@ pub const Type = extern union {
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
=> unreachable,
};
}
@ -1584,10 +1695,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,
@ -1612,6 +1725,7 @@ pub const Type = extern union {
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
=> unreachable,
}
}
@ -1644,10 +1758,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,
@ -1672,6 +1788,7 @@ pub const Type = extern union {
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
=> unreachable,
}
}
@ -1704,10 +1821,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,
@ -1732,6 +1851,7 @@ pub const Type = extern union {
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
=> unreachable,
};
}
@ -1761,10 +1881,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,
@ -1789,6 +1911,7 @@ pub const Type = extern union {
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
=> unreachable,
};
}
@ -1818,10 +1941,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,
@ -1846,6 +1971,7 @@ pub const Type = extern union {
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
=> unreachable,
};
}
@ -1895,14 +2021,17 @@ 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,
.optional_single_const_pointer,
.enum_literal,
=> false,
};
}
@ -1944,12 +2073,14 @@ 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,
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
=> return null,
.void => return Value.initTag(.void_value),
@ -1971,11 +2102,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 +2152,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,11 +2161,15 @@ 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,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
=> return false,
};
}
@ -2080,6 +2213,7 @@ pub const Type = extern union {
comptime_int,
comptime_float,
noreturn,
enum_literal,
@"null",
@"undefined",
fn_noreturn_no_args,
@ -2090,8 +2224,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 +2250,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 {

View File

@ -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,

View File

@ -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,
@ -58,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,
@ -225,6 +229,10 @@ 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,
/// Enum literal
enum_literal,
pub fn Type(tag: Tag) type {
return switch (tag) {
@ -250,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,
@ -259,12 +267,14 @@ pub const Inst = struct {
.unwrap_optional_unsafe,
.unwrap_err_safe,
.unwrap_err_unsafe,
.ensure_err_payload_void,
=> UnOp,
.add,
.addwrap,
.array_cat,
.array_mul,
.array_type,
.bitand,
.bitor,
.div,
@ -291,6 +301,7 @@ pub const Inst = struct {
=> BinOp,
.arg => Arg,
.array_type_sentinel => ArrayTypeSentinel,
.block => Block,
.@"break" => Break,
.breakvoid => BreakVoid,
@ -317,6 +328,7 @@ pub const Inst = struct {
.elemptr => ElemPtr,
.condbr => CondBr,
.ptr_type => PtrType,
.enum_literal => EnumLiteral,
};
}
@ -330,12 +342,14 @@ pub const Inst = struct {
.alloc_inferred,
.array_cat,
.array_mul,
.array_type,
.array_type_sentinel,
.arg,
.as,
.@"asm",
.bitand,
.bitcast,
.bitcast_lvalue,
.bitcast_ref,
.bitcast_result_ptr,
.bitor,
.block,
@ -398,6 +412,8 @@ pub const Inst = struct {
.unwrap_err_safe,
.unwrap_err_unsafe,
.ptr_type,
.ensure_err_payload_void,
.enum_literal,
=> false,
.@"break",
@ -845,6 +861,28 @@ 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 EnumLiteral = struct {
pub const base_tag = Tag.enum_literal;
base: Inst,
positionals: struct {
name: []const u8,
},
kw_args: struct {},
};
};
pub const ErrorMsg = struct {
@ -1922,6 +1960,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)}),
}
}

View File

@ -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").?),
@ -112,6 +112,10 @@ 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).?),
.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).?),
}
}
@ -295,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 {
@ -361,6 +365,10 @@ 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});
}
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);
@ -675,31 +683,36 @@ 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 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 {
@ -735,6 +748,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);
@ -760,7 +777,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);

View File

@ -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;
\\}
,
"",
);
}
{