aboutsummaryrefslogtreecommitdiff
path: root/src/Sema.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2022-01-20 18:24:01 -0500
committerGitHub <noreply@github.com>2022-01-20 18:24:01 -0500
commitc9ae24503dc8da2e59f46619695bf4eb863fb3ac (patch)
treed55084efed19c32fbccb0c96959940796a1d0c43 /src/Sema.zig
parentf763000dc918c2367ebc181645eb48db896205d8 (diff)
parent1f823eecdd071f619c761a743119f1a2a89af1bf (diff)
downloadzig-c9ae24503dc8da2e59f46619695bf4eb863fb3ac.tar.gz
zig-c9ae24503dc8da2e59f46619695bf4eb863fb3ac.zip
Merge pull request #10649 from ziglang/stage2-tuples
stage2: implement tuples
Diffstat (limited to 'src/Sema.zig')
-rw-r--r--src/Sema.zig259
1 files changed, 205 insertions, 54 deletions
diff --git a/src/Sema.zig b/src/Sema.zig
index aca844218e..d3802a1784 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -2628,6 +2628,7 @@ fn validateUnionInit(
// Otherwise, the bitcast should be preserved and a store instruction should be
// emitted to store the constant union value through the bitcast.
},
+ .alloc => {},
else => |t| {
if (std.debug.runtime_safety) {
std.debug.panic("unexpected AIR tag for union pointer: {s}", .{@tagName(t)});
@@ -10694,12 +10695,77 @@ fn zirArrayInit(
}
}
-fn zirArrayInitAnon(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {
+fn zirArrayInitAnon(
+ sema: *Sema,
+ block: *Block,
+ inst: Zir.Inst.Index,
+ is_ref: bool,
+) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src();
+ const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
+ const operands = sema.code.refSlice(extra.end, extra.data.operands_len);
+
+ const types = try sema.arena.alloc(Type, operands.len);
+ const values = try sema.arena.alloc(Value, operands.len);
+
+ const opt_runtime_src = rs: {
+ var runtime_src: ?LazySrcLoc = null;
+ for (operands) |operand, i| {
+ const elem = sema.resolveInst(operand);
+ types[i] = sema.typeOf(elem);
+ const operand_src = src; // TODO better source location
+ if (try sema.resolveMaybeUndefVal(block, operand_src, elem)) |val| {
+ values[i] = val;
+ } else {
+ values[i] = Value.initTag(.unreachable_value);
+ runtime_src = operand_src;
+ }
+ }
+ break :rs runtime_src;
+ };
- _ = is_ref;
- return sema.fail(block, src, "TODO: Sema.zirArrayInitAnon", .{});
+ const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{
+ .types = types,
+ .values = values,
+ });
+
+ const runtime_src = opt_runtime_src orelse {
+ const tuple_val = try Value.Tag.@"struct".create(sema.arena, values);
+ if (!is_ref) return sema.addConstant(tuple_ty, tuple_val);
+
+ var anon_decl = try block.startAnonDecl();
+ defer anon_decl.deinit();
+ const decl = try anon_decl.finish(
+ try tuple_ty.copy(anon_decl.arena()),
+ try tuple_val.copy(anon_decl.arena()),
+ );
+ return sema.analyzeDeclRef(decl);
+ };
+
+ if (is_ref) {
+ const alloc = try block.addTy(.alloc, tuple_ty);
+ for (operands) |operand, i_usize| {
+ const i = @intCast(u32, i_usize);
+ const field_ptr_ty = try Type.ptr(sema.arena, .{
+ .mutable = true,
+ .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
+ .pointee_type = types[i],
+ });
+ const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty);
+ _ = try block.addBinOp(.store, field_ptr, sema.resolveInst(operand));
+ }
+
+ return alloc;
+ }
+
+ const element_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len);
+ for (operands) |operand, i| {
+ element_refs[i] = sema.resolveInst(operand);
+ }
+
+ try sema.requireRuntimeBlock(block, runtime_src);
+ return block.addVectorInit(tuple_ty, element_refs);
}
fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -13540,10 +13606,50 @@ fn elemVal(
// TODO: If the index is a vector, the result should be a vector.
return elemValArray(sema, block, array, elem_index, array_src, elem_index_src);
},
+ .Struct => {
+ // Tuple field access.
+ const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index);
+ const index = @intCast(u32, index_val.toUnsignedInt());
+ return tupleField(sema, block, array, index, array_src, elem_index_src);
+ },
else => unreachable,
}
}
+fn tupleField(
+ sema: *Sema,
+ block: *Block,
+ tuple: Air.Inst.Ref,
+ field_index: u32,
+ tuple_src: LazySrcLoc,
+ field_index_src: LazySrcLoc,
+) CompileError!Air.Inst.Ref {
+ const tuple_ty = sema.typeOf(tuple);
+ const tuple_info = tuple_ty.castTag(.tuple).?.data;
+
+ if (field_index > tuple_info.types.len) {
+ return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
+ field_index, tuple_info.types.len,
+ });
+ }
+
+ const field_ty = tuple_info.types[field_index];
+ const field_val = tuple_info.values[field_index];
+
+ if (field_val.tag() != .unreachable_value) {
+ return sema.addConstant(field_ty, field_val); // comptime field
+ }
+
+ if (try sema.resolveMaybeUndefVal(block, tuple_src, tuple)) |tuple_val| {
+ if (tuple_val.isUndef()) return sema.addConstUndef(field_ty);
+ const field_values = tuple_val.castTag(.@"struct").?.data;
+ return sema.addConstant(field_ty, field_values[field_index]);
+ }
+
+ try sema.requireRuntimeBlock(block, tuple_src);
+ return block.addStructFieldVal(tuple, field_index, field_ty);
+}
+
fn elemValArray(
sema: *Sema,
block: *Block,
@@ -13901,17 +14007,19 @@ fn coerce(
else => {},
},
.Array => switch (inst_ty.zigTypeTag()) {
- .Vector => return sema.coerceVectorInMemory(block, dest_ty, dest_ty_src, inst, inst_src),
+ .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src),
.Struct => {
if (inst == .empty_struct) {
return arrayInitEmpty(sema, dest_ty);
}
+ if (inst_ty.isTuple()) {
+ return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
+ }
},
else => {},
},
.Vector => switch (inst_ty.zigTypeTag()) {
- .Array => return sema.coerceVectorInMemory(block, dest_ty, dest_ty_src, inst, inst_src),
- .Vector => return sema.coerceVectors(block, dest_ty, dest_ty_src, inst, inst_src),
+ .Array, .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src),
else => {},
},
.Struct => {
@@ -14276,12 +14384,30 @@ fn storePtr2(
uncasted_operand: Air.Inst.Ref,
operand_src: LazySrcLoc,
air_tag: Air.Inst.Tag,
-) !void {
+) CompileError!void {
const ptr_ty = sema.typeOf(ptr);
if (ptr_ty.isConstPtr())
- return sema.fail(block, src, "cannot assign to constant", .{});
+ return sema.fail(block, ptr_src, "cannot assign to constant", .{});
const elem_ty = ptr_ty.childType();
+
+ // To generate better code for tuples, we detect a tuple operand here, and
+ // analyze field loads and stores directly. This avoids an extra allocation + memcpy
+ // which would occur if we used `coerce`.
+ const operand_ty = sema.typeOf(uncasted_operand);
+ if (operand_ty.castTag(.tuple)) |payload| {
+ const tuple_fields_len = payload.data.types.len;
+ var i: u32 = 0;
+ while (i < tuple_fields_len) : (i += 1) {
+ const elem_src = operand_src; // TODO better source location
+ const elem = try tupleField(sema, block, uncasted_operand, i, operand_src, elem_src);
+ const elem_index = try sema.addIntUnsigned(Type.usize, i);
+ const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src);
+ try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store);
+ }
+ return;
+ }
+
const operand = try sema.coerce(block, elem_ty, uncasted_operand, operand_src);
if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)
return;
@@ -14847,10 +14973,8 @@ fn coerceEnumToUnion(
return sema.failWithOwnedErrorMsg(msg);
}
-/// Coerces vectors/arrays which have the same in-memory layout. This can be used for
-/// both coercing from and to vectors.
-/// TODO (affects the lang spec) delete this in favor of always using `coerceVectors`.
-fn coerceVectorInMemory(
+/// If the lengths match, coerces element-wise.
+fn coerceArrayLike(
sema: *Sema,
block: *Block,
dest_ty: Type,
@@ -14860,7 +14984,7 @@ fn coerceVectorInMemory(
) !Air.Inst.Ref {
const inst_ty = sema.typeOf(inst);
const inst_len = inst_ty.arrayLen();
- const dest_len = dest_ty.arrayLen();
+ const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen());
if (dest_len != inst_len) {
const msg = msg: {
@@ -14879,22 +15003,50 @@ fn coerceVectorInMemory(
const dest_elem_ty = dest_ty.childType();
const inst_elem_ty = inst_ty.childType();
const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_elem_ty, inst_elem_ty, false, target, dest_ty_src, inst_src);
- if (in_memory_result != .ok) {
- // TODO recursive error notes for coerceInMemoryAllowed failure
- return sema.fail(block, inst_src, "expected {}, found {}", .{ dest_ty, inst_ty });
+ if (in_memory_result == .ok) {
+ if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |inst_val| {
+ // These types share the same comptime value representation.
+ return sema.addConstant(dest_ty, inst_val);
+ }
+ try sema.requireRuntimeBlock(block, inst_src);
+ return block.addBitCast(dest_ty, inst);
}
- if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |inst_val| {
- // These types share the same comptime value representation.
- return sema.addConstant(dest_ty, inst_val);
+ const element_vals = try sema.arena.alloc(Value, dest_len);
+ const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);
+ var runtime_src: ?LazySrcLoc = null;
+
+ for (element_vals) |*elem, i| {
+ const index_ref = try sema.addConstant(
+ Type.usize,
+ try Value.Tag.int_u64.create(sema.arena, i),
+ );
+ const elem_src = inst_src; // TODO better source location
+ const elem_ref = try elemValArray(sema, block, inst, index_ref, inst_src, elem_src);
+ const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
+ element_refs[i] = coerced;
+ if (runtime_src == null) {
+ if (try sema.resolveMaybeUndefVal(block, elem_src, coerced)) |elem_val| {
+ elem.* = elem_val;
+ } else {
+ runtime_src = elem_src;
+ }
+ }
}
- try sema.requireRuntimeBlock(block, inst_src);
- return block.addBitCast(dest_ty, inst);
+ if (runtime_src) |rs| {
+ try sema.requireRuntimeBlock(block, rs);
+ return block.addVectorInit(dest_ty, element_refs);
+ }
+
+ return sema.addConstant(
+ dest_ty,
+ try Value.Tag.array.create(sema.arena, element_vals),
+ );
}
/// If the lengths match, coerces element-wise.
-fn coerceVectors(
+fn coerceTupleToArray(
sema: *Sema,
block: *Block,
dest_ty: Type,
@@ -14919,30 +15071,15 @@ fn coerceVectors(
return sema.failWithOwnedErrorMsg(msg);
}
- const target = sema.mod.getTarget();
- const dest_elem_ty = dest_ty.childType();
- const inst_elem_ty = inst_ty.childType();
- const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_elem_ty, inst_elem_ty, false, target, dest_ty_src, inst_src);
- if (in_memory_result == .ok) {
- if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |inst_val| {
- // These types share the same comptime value representation.
- return sema.addConstant(dest_ty, inst_val);
- }
- try sema.requireRuntimeBlock(block, inst_src);
- return block.addBitCast(dest_ty, inst);
- }
-
const element_vals = try sema.arena.alloc(Value, dest_len);
const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);
- var runtime_src: ?LazySrcLoc = null;
+ const dest_elem_ty = dest_ty.childType();
- for (element_vals) |*elem, i| {
- const index_ref = try sema.addConstant(
- Type.usize,
- try Value.Tag.int_u64.create(sema.arena, i),
- );
+ var runtime_src: ?LazySrcLoc = null;
+ for (element_vals) |*elem, i_usize| {
+ const i = @intCast(u32, i_usize);
const elem_src = inst_src; // TODO better source location
- const elem_ref = try elemValArray(sema, block, inst, index_ref, inst_src, elem_src);
+ const elem_ref = try tupleField(sema, block, inst, i, inst_src, elem_src);
const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
element_refs[i] = coerced;
if (runtime_src == null) {
@@ -15833,19 +15970,22 @@ fn resolveStructLayout(
ty: Type,
) CompileError!void {
const resolved_ty = try sema.resolveTypeFields(block, src, ty);
- const struct_obj = resolved_ty.castTag(.@"struct").?.data;
- switch (struct_obj.status) {
- .none, .have_field_types => {},
- .field_types_wip, .layout_wip => {
- return sema.fail(block, src, "struct {} depends on itself", .{ty});
- },
- .have_layout, .fully_resolved_wip, .fully_resolved => return,
- }
- struct_obj.status = .layout_wip;
- for (struct_obj.fields.values()) |field| {
- try sema.resolveTypeLayout(block, src, field.ty);
+ if (resolved_ty.castTag(.@"struct")) |payload| {
+ const struct_obj = payload.data;
+ switch (struct_obj.status) {
+ .none, .have_field_types => {},
+ .field_types_wip, .layout_wip => {
+ return sema.fail(block, src, "struct {} depends on itself", .{ty});
+ },
+ .have_layout, .fully_resolved_wip, .fully_resolved => return,
+ }
+ struct_obj.status = .layout_wip;
+ for (struct_obj.fields.values()) |field| {
+ try sema.resolveTypeLayout(block, src, field.ty);
+ }
+ struct_obj.status = .have_layout;
}
- struct_obj.status = .have_layout;
+ // otherwise it's a tuple; no need to resolve anything
}
fn resolveUnionLayout(
@@ -16642,6 +16782,17 @@ pub fn typeHasOnePossibleValue(
}
return Value.initTag(.empty_struct_value);
},
+
+ .tuple => {
+ const tuple = ty.castTag(.tuple).?.data;
+ for (tuple.values) |val| {
+ if (val.tag() == .unreachable_value) {
+ return null; // non-comptime field
+ }
+ }
+ return Value.initTag(.empty_struct_value);
+ },
+
.enum_numbered => {
const resolved_ty = try sema.resolveTypeFields(block, src, ty);
const enum_obj = resolved_ty.castTag(.enum_numbered).?.data;