aboutsummaryrefslogtreecommitdiff
path: root/src/Sema.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/Sema.zig')
-rw-r--r--src/Sema.zig278
1 files changed, 137 insertions, 141 deletions
diff --git a/src/Sema.zig b/src/Sema.zig
index e3e8982fac..a07c5761ff 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -5119,16 +5119,10 @@ fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src();
const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
- const lhs_src: LazySrcLoc = src; // TODO
const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
const field_name = sema.code.nullTerminatedString(extra.field_name_start);
const object = sema.resolveInst(extra.lhs);
- if (sema.typeOf(object).isSinglePointer()) {
- const result_ptr = try sema.fieldPtr(block, src, object, field_name, field_name_src);
- return sema.analyzeLoad(block, src, result_ptr, lhs_src);
- } else {
- return sema.fieldVal(block, src, object, field_name, field_name_src);
- }
+ return sema.fieldVal(block, src, object, field_name, field_name_src);
}
fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -10688,12 +10682,22 @@ fn fieldVal(
const object_src = src; // TODO better source location
const object_ty = sema.typeOf(object);
- switch (object_ty.zigTypeTag()) {
+ // Zig allows dereferencing a single pointer during field lookup. Note that
+ // we don't actually need to generate the dereference some field lookups, like the
+ // length of arrays and other comptime operations.
+ const is_pointer_to = object_ty.isSinglePointer();
+
+ const inner_ty = if (is_pointer_to)
+ object_ty.childType()
+ else
+ object_ty;
+
+ switch (inner_ty.zigTypeTag()) {
.Array => {
if (mem.eql(u8, field_name, "len")) {
return sema.addConstant(
Type.initTag(.comptime_int),
- try Value.Tag.int_u64.create(arena, object_ty.arrayLen()),
+ try Value.Tag.int_u64.create(arena, inner_ty.arrayLen()),
);
} else {
return sema.fail(
@@ -10704,75 +10708,60 @@ fn fieldVal(
);
}
},
- .Pointer => switch (object_ty.ptrSize()) {
- .Slice => {
- if (mem.eql(u8, field_name, "ptr")) {
- const buf = try arena.create(Type.SlicePtrFieldTypeBuffer);
- const result_ty = object_ty.slicePtrFieldType(buf);
- if (try sema.resolveMaybeUndefVal(block, object_src, object)) |val| {
- if (val.isUndef()) return sema.addConstUndef(result_ty);
- return sema.addConstant(result_ty, val.slicePtr());
- }
- try sema.requireRuntimeBlock(block, src);
- return block.addTyOp(.slice_ptr, result_ty, object);
- } else if (mem.eql(u8, field_name, "len")) {
- const result_ty = Type.usize;
- if (try sema.resolveMaybeUndefVal(block, object_src, object)) |val| {
- if (val.isUndef()) return sema.addConstUndef(result_ty);
- return sema.addConstant(
- result_ty,
- try Value.Tag.int_u64.create(arena, val.sliceLen()),
- );
- }
- try sema.requireRuntimeBlock(block, src);
- return block.addTyOp(.slice_len, result_ty, object);
- } else {
- return sema.fail(
- block,
- field_name_src,
- "no member named '{s}' in '{}'",
- .{ field_name, object_ty },
- );
+ .Pointer => if (inner_ty.isSlice()) {
+ if (mem.eql(u8, field_name, "ptr")) {
+ const slice = if (is_pointer_to)
+ try sema.analyzeLoad(block, src, object, object_src)
+ else
+ object;
+
+ const buf = try arena.create(Type.SlicePtrFieldTypeBuffer);
+ const result_ty = inner_ty.slicePtrFieldType(buf);
+
+ if (try sema.resolveMaybeUndefVal(block, object_src, slice)) |val| {
+ if (val.isUndef()) return sema.addConstUndef(result_ty);
+ return sema.addConstant(result_ty, val.slicePtr());
}
- },
- .One => {
- const ptr_child = object_ty.elemType();
- switch (ptr_child.zigTypeTag()) {
- .Array => {
- if (mem.eql(u8, field_name, "len")) {
- return sema.addConstant(
- Type.initTag(.comptime_int),
- try Value.Tag.int_u64.create(arena, ptr_child.arrayLen()),
- );
- } else {
- return sema.fail(
- block,
- field_name_src,
- "no member named '{s}' in '{}'",
- .{ field_name, object_ty },
- );
- }
- },
- .Struct => {
- const struct_ptr_deref = try sema.analyzeLoad(block, src, object, object_src);
- return sema.unionFieldVal(block, src, struct_ptr_deref, field_name, field_name_src, ptr_child);
- },
- .Union => {
- const union_ptr_deref = try sema.analyzeLoad(block, src, object, object_src);
- return sema.unionFieldVal(block, src, union_ptr_deref, field_name, field_name_src, ptr_child);
- },
- else => {},
+ try sema.requireRuntimeBlock(block, src);
+ return block.addTyOp(.slice_ptr, result_ty, slice);
+ } else if (mem.eql(u8, field_name, "len")) {
+ const slice = if (is_pointer_to)
+ try sema.analyzeLoad(block, src, object, object_src)
+ else
+ object;
+
+ const result_ty = Type.usize;
+
+ if (try sema.resolveMaybeUndefVal(block, object_src, slice)) |val| {
+ if (val.isUndef()) return sema.addConstUndef(result_ty);
+ return sema.addConstant(
+ result_ty,
+ try Value.Tag.int_u64.create(arena, val.sliceLen()),
+ );
}
- },
- .Many, .C => {},
+ try sema.requireRuntimeBlock(block, src);
+ return block.addTyOp(.slice_len, result_ty, slice);
+ } else {
+ return sema.fail(
+ block,
+ field_name_src,
+ "no member named '{s}' in '{}'",
+ .{ field_name, object_ty },
+ );
+ }
},
.Type => {
- const val = (try sema.resolveDefinedValue(block, object_src, object)).?;
+ const dereffed_type = if (is_pointer_to)
+ try sema.analyzeLoad(block, src, object, object_src)
+ else
+ object;
+
+ const val = (try sema.resolveDefinedValue(block, object_src, dereffed_type)).?;
var to_type_buffer: Value.ToTypeBuffer = undefined;
const child_type = val.toType(&to_type_buffer);
+
switch (child_type.zigTypeTag()) {
.ErrorSet => {
- // TODO resolve inferred error sets
const name: []const u8 = if (child_type.castTag(.error_set)) |payload| blk: {
const error_set = payload.data;
// TODO this is O(N). I'm putting off solving this until we solve inferred
@@ -10842,8 +10831,20 @@ fn fieldVal(
else => return sema.fail(block, src, "type '{}' has no members", .{child_type}),
}
},
- .Struct => return sema.structFieldVal(block, src, object, field_name, field_name_src, object_ty),
- .Union => return sema.unionFieldVal(block, src, object, field_name, field_name_src, object_ty),
+ .Struct => if (is_pointer_to) {
+ // Avoid loading the entire struct by fetching a pointer and loading that
+ const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty);
+ return sema.analyzeLoad(block, src, field_ptr, object_src);
+ } else {
+ return sema.structFieldVal(block, src, object, field_name, field_name_src, inner_ty);
+ },
+ .Union => if (is_pointer_to) {
+ // Avoid loading the entire union by fetching a pointer and loading that
+ const field_ptr = try sema.unionFieldPtr(block, src, object, field_name, field_name_src, inner_ty);
+ return sema.analyzeLoad(block, src, field_ptr, object_src);
+ } else {
+ return sema.unionFieldVal(block, src, object, field_name, field_name_src, inner_ty);
+ },
else => {},
}
return sema.fail(block, src, "type '{}' does not support field access", .{object_ty});
@@ -10866,14 +10867,25 @@ fn fieldPtr(
.Pointer => object_ptr_ty.elemType(),
else => return sema.fail(block, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty}),
};
- switch (object_ty.zigTypeTag()) {
+
+ // Zig allows dereferencing a single pointer during field lookup. Note that
+ // we don't actually need to generate the dereference some field lookups, like the
+ // length of arrays and other comptime operations.
+ const is_pointer_to = object_ty.isSinglePointer();
+
+ const inner_ty = if (is_pointer_to)
+ object_ty.childType()
+ else
+ object_ty;
+
+ switch (inner_ty.zigTypeTag()) {
.Array => {
if (mem.eql(u8, field_name, "len")) {
var anon_decl = try block.startAnonDecl();
defer anon_decl.deinit();
return sema.analyzeDeclRef(try anon_decl.finish(
Type.initTag(.comptime_int),
- try Value.Tag.int_u64.create(anon_decl.arena(), object_ty.arrayLen()),
+ try Value.Tag.int_u64.create(anon_decl.arena(), inner_ty.arrayLen()),
));
} else {
return sema.fail(
@@ -10884,77 +10896,49 @@ fn fieldPtr(
);
}
},
- .Pointer => switch (object_ty.ptrSize()) {
- .Slice => {
- // Here for the ptr and len fields what we need to do is the situation
- // when a temporary has its address taken, e.g. `&a[c..d].len`.
- // This value may be known at compile-time or runtime. In the former
- // case, it should create an anonymous Decl and return a decl_ref to it.
- // In the latter case, it should add an `alloc` instruction, store
- // the runtime value to it, and then return the `alloc`.
- // In both cases the pointer should be const.
- if (mem.eql(u8, field_name, "ptr")) {
- return sema.fail(
- block,
- field_name_src,
- "TODO: implement reference to 'ptr' field of slice '{}'",
- .{object_ty},
- );
- } else if (mem.eql(u8, field_name, "len")) {
- return sema.fail(
- block,
- field_name_src,
- "TODO: implement reference to 'len' field of slice '{}'",
- .{object_ty},
- );
- } else {
- return sema.fail(
- block,
- field_name_src,
- "no member named '{s}' in '{}'",
- .{ field_name, object_ty },
- );
- }
- },
- .One => {
- const ptr_child = object_ty.elemType();
- switch (ptr_child.zigTypeTag()) {
- .Array => {
- if (mem.eql(u8, field_name, "len")) {
- var anon_decl = try block.startAnonDecl();
- defer anon_decl.deinit();
- return sema.analyzeDeclRef(try anon_decl.finish(
- Type.initTag(.comptime_int),
- try Value.Tag.int_u64.create(anon_decl.arena(), ptr_child.arrayLen()),
- ));
- } else {
- return sema.fail(
- block,
- field_name_src,
- "no member named '{s}' in '{}'",
- .{ field_name, object_ty },
- );
- }
- },
- .Struct => {
- const struct_ptr_deref = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);
- return sema.structFieldPtr(block, src, struct_ptr_deref, field_name, field_name_src, ptr_child);
- },
- .Union => {
- const union_ptr_deref = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);
- return sema.unionFieldPtr(block, src, union_ptr_deref, field_name, field_name_src, ptr_child);
- },
- else => {},
- }
- },
- .Many, .C => {},
+ .Pointer => if (inner_ty.isSlice()) {
+ // Here for the ptr and len fields what we need to do is the situation
+ // when a temporary has its address taken, e.g. `&a[c..d].len`.
+ // This value may be known at compile-time or runtime. In the former
+ // case, it should create an anonymous Decl and return a decl_ref to it.
+ // In the latter case, it should add an `alloc` instruction, store
+ // the runtime value to it, and then return the `alloc`.
+ // In both cases the pointer should be const.
+ if (mem.eql(u8, field_name, "ptr")) {
+ return sema.fail(
+ block,
+ field_name_src,
+ "TODO: implement reference to 'ptr' field of slice '{}'",
+ .{inner_ty},
+ );
+ } else if (mem.eql(u8, field_name, "len")) {
+ return sema.fail(
+ block,
+ field_name_src,
+ "TODO: implement reference to 'len' field of slice '{}'",
+ .{inner_ty},
+ );
+ } else {
+ return sema.fail(
+ block,
+ field_name_src,
+ "no member named '{s}' in '{}'",
+ .{ field_name, object_ty },
+ );
+ }
},
.Type => {
_ = try sema.resolveConstValue(block, object_ptr_src, object_ptr);
const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);
- const val = (sema.resolveDefinedValue(block, src, result) catch unreachable).?;
+ const inner = if (is_pointer_to)
+ try sema.analyzeLoad(block, src, result, object_ptr_src)
+ else
+ result;
+
+ const val = (sema.resolveDefinedValue(block, src, inner) catch unreachable).?;
var to_type_buffer: Value.ToTypeBuffer = undefined;
const child_type = val.toType(&to_type_buffer);
+
switch (child_type.zigTypeTag()) {
.ErrorSet => {
// TODO resolve inferred error sets
@@ -11033,8 +11017,20 @@ fn fieldPtr(
else => return sema.fail(block, src, "type '{}' has no members", .{child_type}),
}
},
- .Struct => return sema.structFieldPtr(block, src, object_ptr, field_name, field_name_src, object_ty),
- .Union => return sema.unionFieldPtr(block, src, object_ptr, field_name, field_name_src, object_ty),
+ .Struct => {
+ const inner_ptr = if (is_pointer_to)
+ try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
+ else
+ object_ptr;
+ return sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty);
+ },
+ .Union => {
+ const inner_ptr = if (is_pointer_to)
+ try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
+ else
+ object_ptr;
+ return sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty);
+ },
else => {},
}
return sema.fail(block, src, "type '{}' does not support field access", .{object_ty});