diff --git a/src/AstGen.zig b/src/AstGen.zig index 5006730cad..5113ddfb8f 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -339,6 +339,8 @@ pub const ResultInfo = struct { fn_arg, /// The expression is the right-hand side of an initializer for a `const` variable const_init, + /// The expression is the right-hand side of an assignment expression. + assignment, /// No specific operator in particular. none, }; @@ -826,7 +828,13 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE .slice_open => { const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); + + maybeAdvanceSourceCursorToMainToken(gz, node); + const line = gz.astgen.source_line - gz.decl_line; + const column = gz.astgen.source_column; + const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs); + try emitDbgStmt(gz, line, column); const result = try gz.addPlNode(.slice_start, node, Zir.Inst.SliceStart{ .lhs = lhs, .start = start, @@ -835,9 +843,15 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE }, .slice => { const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); + + maybeAdvanceSourceCursorToMainToken(gz, node); + const line = gz.astgen.source_line - gz.decl_line; + const column = gz.astgen.source_column; + const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice); const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start); const end = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end); + try emitDbgStmt(gz, line, column); const result = try gz.addPlNode(.slice_end, node, Zir.Inst.SliceEnd{ .lhs = lhs, .start = start, @@ -847,10 +861,16 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE }, .slice_sentinel => { const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); + + maybeAdvanceSourceCursorToMainToken(gz, node); + const line = gz.astgen.source_line - gz.decl_line; + const column = gz.astgen.source_column; + const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel); const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start); const end = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none; const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel); + try emitDbgStmt(gz, line, column); const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{ .lhs = lhs, .start = start, @@ -881,16 +901,26 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE return rvalue(gz, ri, result, node); }, .unwrap_optional => switch (ri.rl) { - .ref => return gz.addUnNode( - .optional_payload_safe_ptr, - try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs), - node, - ), - else => return rvalue(gz, ri, try gz.addUnNode( - .optional_payload_safe, - try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs), - node, - ), node), + .ref => { + const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); + + maybeAdvanceSourceCursorToMainToken(gz, node); + const line = gz.astgen.source_line - gz.decl_line; + const column = gz.astgen.source_column; + try emitDbgStmt(gz, line, column); + + return gz.addUnNode(.optional_payload_safe_ptr, lhs, node); + }, + else => { + const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs); + + maybeAdvanceSourceCursorToMainToken(gz, node); + const line = gz.astgen.source_line - gz.decl_line; + const column = gz.astgen.source_column; + try emitDbgStmt(gz, line, column); + + return rvalue(gz, ri, try gz.addUnNode(.optional_payload_safe, lhs, node), node); + }, }, .block_two, .block_two_semicolon => { const statements = [2]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs }; @@ -3216,7 +3246,7 @@ fn assign(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!voi // This intentionally does not support `@"_"` syntax. const ident_name = tree.tokenSlice(main_tokens[lhs]); if (mem.eql(u8, ident_name, "_")) { - _ = try expr(gz, scope, .{ .rl = .discard }, rhs); + _ = try expr(gz, scope, .{ .rl = .discard, .ctx = .assignment }, rhs); return; } } @@ -3239,10 +3269,27 @@ fn assignOp( const node_datas = tree.nodes.items(.data); const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs); + + var line: u32 = undefined; + var column: u32 = undefined; + switch (op_inst_tag) { + .add, .sub, .mul, .div, .mod_rem => { + maybeAdvanceSourceCursorToMainToken(gz, infix_node); + line = gz.astgen.source_line - gz.decl_line; + column = gz.astgen.source_column; + }, + else => {}, + } const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node); const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node); const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = lhs_type } }, node_datas[infix_node].rhs); + switch (op_inst_tag) { + .add, .sub, .mul, .div, .mod_rem => { + try emitDbgStmt(gz, line, column); + }, + else => {}, + } const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs, @@ -5294,9 +5341,11 @@ fn orelseCatchExpr( // up for this fact by calling rvalue on the else branch. const operand = try reachableExpr(&block_scope, &block_scope.base, operand_ri, lhs, rhs); const cond = try block_scope.addUnNode(cond_op, operand, node); - const condbr = try block_scope.addCondBr(.condbr, node); + const condbr_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .condbr_inline else .condbr; + const condbr = try block_scope.addCondBr(condbr_tag, node); - const block = try parent_gz.makeBlockInst(.block, node); + const block_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .block_inline else .block; + const block = try parent_gz.makeBlockInst(block_tag, node); try block_scope.setBlockBody(block); // block_scope unstacked now, can add new instructions to parent_gz try parent_gz.instructions.append(astgen.gpa, block); @@ -5471,9 +5520,15 @@ fn addFieldAccess( const dot_token = main_tokens[node]; const field_ident = dot_token + 1; const str_index = try astgen.identAsString(field_ident); + const lhs = try expr(gz, scope, lhs_ri, object_node); + + maybeAdvanceSourceCursorToMainToken(gz, node); + const line = gz.astgen.source_line - gz.decl_line; + const column = gz.astgen.source_column; + try emitDbgStmt(gz, line, column); return gz.addPlNode(tag, node, Zir.Inst.Field{ - .lhs = try expr(gz, scope, lhs_ri, object_node), + .lhs = lhs, .field_name_start = str_index, }); } @@ -5484,18 +5539,33 @@ fn arrayAccess( ri: ResultInfo, node: Ast.Node.Index, ) InnerError!Zir.Inst.Ref { - const astgen = gz.astgen; - const tree = astgen.tree; + const tree = gz.astgen.tree; const node_datas = tree.nodes.items(.data); switch (ri.rl) { - .ref => return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{ - .lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs), - .rhs = try expr(gz, scope, .{ .rl = .{ .ty = .usize_type } }, node_datas[node].rhs), - }), - else => return rvalue(gz, ri, try gz.addPlNode(.elem_val_node, node, Zir.Inst.Bin{ - .lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs), - .rhs = try expr(gz, scope, .{ .rl = .{ .ty = .usize_type } }, node_datas[node].rhs), - }), node), + .ref => { + const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); + + maybeAdvanceSourceCursorToMainToken(gz, node); + const line = gz.astgen.source_line - gz.decl_line; + const column = gz.astgen.source_column; + + const rhs = try expr(gz, scope, .{ .rl = .{ .ty = .usize_type } }, node_datas[node].rhs); + try emitDbgStmt(gz, line, column); + + return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }); + }, + else => { + const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs); + + maybeAdvanceSourceCursorToMainToken(gz, node); + const line = gz.astgen.source_line - gz.decl_line; + const column = gz.astgen.source_column; + + const rhs = try expr(gz, scope, .{ .rl = .{ .ty = .usize_type } }, node_datas[node].rhs); + try emitDbgStmt(gz, line, column); + + return rvalue(gz, ri, try gz.addPlNode(.elem_val_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }), node); + }, } } @@ -5510,10 +5580,26 @@ fn simpleBinOp( const tree = astgen.tree; const node_datas = tree.nodes.items(.data); - const result = try gz.addPlNode(op_inst_tag, node, Zir.Inst.Bin{ - .lhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].lhs, node), - .rhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].rhs, node), - }); + const lhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].lhs, node); + var line: u32 = undefined; + var column: u32 = undefined; + switch (op_inst_tag) { + .add, .sub, .mul, .div, .mod_rem => { + maybeAdvanceSourceCursorToMainToken(gz, node); + line = gz.astgen.source_line - gz.decl_line; + column = gz.astgen.source_column; + }, + else => {}, + } + const rhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].rhs, node); + + switch (op_inst_tag) { + .add, .sub, .mul, .div, .mod_rem => { + try emitDbgStmt(gz, line, column); + }, + else => {}, + } + const result = try gz.addPlNode(op_inst_tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }); return rvalue(gz, ri, result, node); } @@ -5608,9 +5694,11 @@ fn ifExpr( } }; - const condbr = try block_scope.addCondBr(.condbr, node); + const condbr_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .condbr_inline else .condbr; + const condbr = try block_scope.addCondBr(condbr_tag, node); - const block = try parent_gz.makeBlockInst(.block, node); + const block_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .block_inline else .block; + const block = try parent_gz.makeBlockInst(block_tag, node); try block_scope.setBlockBody(block); // block_scope unstacked now, can add new instructions to parent_gz try parent_gz.instructions.append(astgen.gpa, block); @@ -7084,7 +7172,7 @@ fn localVarRef( if (local_val.name == name_str_index) { // Locals cannot shadow anything, so we do not need to look for ambiguous // references in this case. - if (ri.rl == .discard) { + if (ri.rl == .discard and ri.ctx == .assignment) { local_val.discarded = ident_token; } else { local_val.used = ident_token; @@ -7107,7 +7195,7 @@ fn localVarRef( .local_ptr => { const local_ptr = s.cast(Scope.LocalPtr).?; if (local_ptr.name == name_str_index) { - if (ri.rl == .discard) { + if (ri.rl == .discard and ri.ctx == .assignment) { local_ptr.discarded = ident_token; } else { local_ptr.used = ident_token; @@ -7969,6 +8057,8 @@ fn builtinCall( return rvalue(gz, ri, result, node); }, .err_set_cast => { + try emitDbgNode(gz, node); + const result = try gz.addExtendedPayload(.err_set_cast, Zir.Inst.BinNode{ .lhs = try typeExpr(gz, scope, params[0]), .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]), @@ -8274,6 +8364,8 @@ fn typeCast( rhs_node: Ast.Node.Index, tag: Zir.Inst.Tag, ) InnerError!Zir.Inst.Ref { + try emitDbgNode(gz, node); + const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ .lhs = try typeExpr(gz, scope, lhs_node), .rhs = try expr(gz, scope, .{ .rl = .none }, rhs_node), @@ -8303,6 +8395,10 @@ fn simpleUnOp( operand_node: Ast.Node.Index, tag: Zir.Inst.Tag, ) InnerError!Zir.Inst.Ref { + switch (tag) { + .tag_name, .error_name, .ptr_to_int => try emitDbgNode(gz, node), + else => {}, + } const operand = try expr(gz, scope, operand_ri, operand_node); const result = try gz.addUnNode(tag, operand, node); return rvalue(gz, ri, result, node); @@ -8375,6 +8471,8 @@ fn divBuiltin( rhs_node: Ast.Node.Index, tag: Zir.Inst.Tag, ) InnerError!Zir.Inst.Ref { + try emitDbgNode(gz, node); + const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ .lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node), .rhs = try expr(gz, scope, .{ .rl = .none }, rhs_node), @@ -8428,8 +8526,15 @@ fn shiftOp( tag: Zir.Inst.Tag, ) InnerError!Zir.Inst.Ref { const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node); + + maybeAdvanceSourceCursorToMainToken(gz, node); + const line = gz.astgen.source_line - gz.decl_line; + const column = gz.astgen.source_column; + const log2_int_type = try gz.addUnNode(.typeof_log2_int_type, lhs, lhs_node); const rhs = try expr(gz, scope, .{ .rl = .{ .ty = log2_int_type }, .ctx = .shift_op }, rhs_node); + + try emitDbgStmt(gz, line, column); const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs, @@ -10668,14 +10773,19 @@ const GenZir = struct { gz.break_result_info = parent_ri; }, - .discard, .none, .ref => { + .none, .ref => { gz.rl_ty_inst = .none; gz.break_result_info = parent_ri; }, + .discard => { + gz.rl_ty_inst = .none; + gz.break_result_info = .{ .rl = .discard }; + }, + .ptr => |ptr_res| { gz.rl_ty_inst = .none; - gz.break_result_info = .{ .rl = .{ .ptr = .{ .inst = ptr_res.inst } } }; + gz.break_result_info = .{ .rl = .{ .ptr = .{ .inst = ptr_res.inst } }, .ctx = parent_ri.ctx }; }, .inferred_ptr => |ptr| { @@ -12054,6 +12164,18 @@ fn detectLocalShadowing( }; } +/// Advances the source cursor to the main token of `node` if not in comptime scope. +/// Usually paired with `emitDbgStmt`. +fn maybeAdvanceSourceCursorToMainToken(gz: *GenZir, node: Ast.Node.Index) void { + if (gz.force_comptime) return; + + const tree = gz.astgen.tree; + const token_starts = tree.tokens.items(.start); + const main_tokens = tree.nodes.items(.main_token); + const node_start = token_starts[main_tokens[node]]; + gz.astgen.advanceSourceCursor(node_start); +} + /// Advances the source cursor to the beginning of `node`. fn advanceSourceCursorToNode(astgen: *AstGen, node: Ast.Node.Index) void { const tree = astgen.tree; diff --git a/src/Autodoc.zig b/src/Autodoc.zig index 04b13c3026..4d13416e1f 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -2129,7 +2129,15 @@ fn walkInstruction( file, parent_scope, parent_src, - getBlockInlineBreak(file.zir, inst_index), + getBlockInlineBreak(file.zir, inst_index) orelse { + const res = DocData.WalkResult{ .expr = .{ + .comptimeExpr = self.comptime_exprs.items.len, + } }; + try self.comptime_exprs.append(self.arena, .{ + .code = "if (...) { ... }", + }); + return res; + }, need_type, ); }, @@ -3155,7 +3163,7 @@ fn walkDecls( 2 => { // decl test const decl_being_tested = scope.resolveDeclName(doc_comment_index); - const func_index = getBlockInlineBreak(file.zir, value_index); + const func_index = getBlockInlineBreak(file.zir, value_index).?; const pl_node = data[Zir.refToIndex(func_index).?].pl_node; const fn_src = try self.srcLocInfo(file, pl_node.src_node, decl_src); @@ -4301,12 +4309,13 @@ fn walkRef( } } -fn getBlockInlineBreak(zir: Zir, inst_index: usize) Zir.Inst.Ref { +fn getBlockInlineBreak(zir: Zir, inst_index: usize) ?Zir.Inst.Ref { const tags = zir.instructions.items(.tag); const data = zir.instructions.items(.data); const pl_node = data[inst_index].pl_node; const extra = zir.extraData(Zir.Inst.Block, pl_node.payload_index); const break_index = zir.extra[extra.end..][extra.data.body_len - 1]; + if (tags[break_index] == .condbr_inline) return null; std.debug.assert(tags[break_index] == .break_inline); return data[break_index].@"break".operand; } diff --git a/src/Sema.zig b/src/Sema.zig index 0ca92fb649..f40f36e511 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1491,6 +1491,8 @@ fn analyzeBodyInner( return err; }; const inline_body = if (cond.val.toBool()) then_body else else_body; + + try sema.maybeErrorUnwrapCondbr(block, inline_body, extra.data.condition, cond_src); const old_runtime_index = block.runtime_index; defer block.runtime_index = old_runtime_index; const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse @@ -5658,14 +5660,14 @@ fn lookupInNamespace( const src_file = block.namespace.file_scope; const gpa = sema.gpa; - var checked_namespaces: std.AutoArrayHashMapUnmanaged(*Namespace, void) = .{}; + var checked_namespaces: std.AutoArrayHashMapUnmanaged(*Namespace, bool) = .{}; defer checked_namespaces.deinit(gpa); // Keep track of name conflicts for error notes. var candidates: std.ArrayListUnmanaged(Decl.Index) = .{}; defer candidates.deinit(gpa); - try checked_namespaces.put(gpa, namespace, {}); + try checked_namespaces.put(gpa, namespace, namespace.file_scope == src_file); var check_i: usize = 0; while (check_i < checked_namespaces.count()) : (check_i += 1) { @@ -5674,7 +5676,7 @@ fn lookupInNamespace( // Skip decls which are not marked pub, which are in a different // file than the `a.b`/`@hasDecl` syntax. const decl = mod.declPtr(decl_index); - if (decl.is_pub or src_file == decl.getFileScope()) { + if (decl.is_pub or (src_file == decl.getFileScope() and checked_namespaces.values()[check_i])) { try candidates.append(gpa, decl_index); } } @@ -5693,7 +5695,7 @@ fn lookupInNamespace( try sema.ensureDeclAnalyzed(sub_usingnamespace_decl_index); const ns_ty = sub_usingnamespace_decl.val.castTag(.ty).?.data; const sub_ns = ns_ty.getNamespace().?; - try checked_namespaces.put(gpa, sub_ns, {}); + try checked_namespaces.put(gpa, sub_ns, src_file == sub_usingnamespace_decl.getFileScope()); } } @@ -6331,6 +6333,7 @@ fn analyzeCall( .instructions = .{}, .label = null, .inlining = &inlining, + .is_typeof = block.is_typeof, .is_comptime = is_comptime_call, .comptime_reason = comptime_reason, .error_return_trace_index = block.error_return_trace_index, @@ -16530,9 +16533,6 @@ fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE // This is only relevant at runtime. if (block.is_comptime or block.is_typeof) return; - // This is only relevant within functions. - if (sema.func == null) return; - const save_index = inst_data.operand == .none or b: { const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); @@ -20179,8 +20179,8 @@ fn analyzeShuffle( .elem_type = elem_ty, }); - if (maybe_a_len == null) a = try sema.addConstUndef(a_ty); - if (maybe_b_len == null) b = try sema.addConstUndef(b_ty); + if (maybe_a_len == null) a = try sema.addConstUndef(a_ty) else a = try sema.coerce(block, a_ty, a, a_src); + if (maybe_b_len == null) b = try sema.addConstUndef(b_ty) else b = try sema.coerce(block, b_ty, b, b_src); const operand_info = [2]std.meta.Tuple(&.{ u64, LazySrcLoc, Type }){ .{ a_len, a_src, a_ty }, @@ -27503,9 +27503,6 @@ fn analyzeLoad( if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |elem_val| { return sema.addConstant(elem_ty, elem_val); } - if (block.is_typeof) { - return sema.addConstUndef(elem_ty); - } } return block.addTyOp(.load, elem_ty, ptr); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index acbf5b337f..abe4256a30 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1966,7 +1966,7 @@ pub const Object = struct { for (tuple.types) |field_ty, i| { const field_val = tuple.values[i]; - if (field_val.tag() != .unreachable_value) continue; + if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBits()) continue; const field_size = field_ty.abiSize(target); const field_align = field_ty.abiAlignment(target); @@ -2901,7 +2901,7 @@ pub const DeclGen = struct { for (tuple.types) |field_ty, i| { const field_val = tuple.values[i]; - if (field_val.tag() != .unreachable_value) continue; + if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBits()) continue; const field_align = field_ty.abiAlignment(target); big_align = @max(big_align, field_align); @@ -3198,7 +3198,8 @@ pub const DeclGen = struct { /// There are other similar cases handled here as well. fn lowerPtrElemTy(dg: *DeclGen, elem_ty: Type) Allocator.Error!*llvm.Type { const lower_elem_ty = switch (elem_ty.zigTypeTag()) { - .Opaque, .Fn => true, + .Opaque => true, + .Fn => !elem_ty.fnInfo().is_generic, .Array => elem_ty.childType().hasRuntimeBitsIgnoreComptime(), else => elem_ty.hasRuntimeBitsIgnoreComptime(), }; @@ -4145,7 +4146,9 @@ pub const DeclGen = struct { } const is_fn_body = decl.ty.zigTypeTag() == .Fn; - if (!is_fn_body and !decl.ty.hasRuntimeBits()) { + if ((!is_fn_body and !decl.ty.hasRuntimeBits()) or + (is_fn_body and decl.ty.fnInfo().is_generic)) + { return self.lowerPtrToVoid(tv.ty); } @@ -8671,9 +8674,9 @@ pub const FuncGen = struct { const arena = arena_allocator.allocator(); const mod = self.dg.module; - const llvm_fn_name = try std.fmt.allocPrintZ(arena, "__zig_is_named_enum_value_{s}", .{ - try mod.declPtr(enum_decl).getFullyQualifiedName(mod), - }); + const fqn = try mod.declPtr(enum_decl).getFullyQualifiedName(mod); + defer self.gpa.free(fqn); + const llvm_fn_name = try std.fmt.allocPrintZ(arena, "__zig_is_named_enum_value_{s}", .{fqn}); var int_tag_type_buffer: Type.Payload.Bits = undefined; const int_tag_ty = enum_ty.intTagType(&int_tag_type_buffer); @@ -8752,9 +8755,9 @@ pub const FuncGen = struct { const arena = arena_allocator.allocator(); const mod = self.dg.module; - const llvm_fn_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{s}", .{ - try mod.declPtr(enum_decl).getFullyQualifiedName(mod), - }); + const fqn = try mod.declPtr(enum_decl).getFullyQualifiedName(mod); + defer self.gpa.free(fqn); + const llvm_fn_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{s}", .{fqn}); const slice_ty = Type.initTag(.const_slice_u8_sentinel_0); const llvm_ret_ty = try self.dg.lowerType(slice_ty); @@ -10204,7 +10207,7 @@ fn llvmFieldIndex( const tuple = ty.tupleFields(); var llvm_field_index: c_uint = 0; for (tuple.types) |field_ty, i| { - if (tuple.values[i].tag() != .unreachable_value) continue; + if (tuple.values[i].tag() != .unreachable_value or !field_ty.hasRuntimeBits()) continue; const field_align = field_ty.abiAlignment(target); big_align = @max(big_align, field_align); @@ -10216,7 +10219,7 @@ fn llvmFieldIndex( llvm_field_index += 1; } - if (field_index == i) { + if (field_index <= i) { ptr_pl_buf.* = .{ .data = .{ .pointee_type = field_ty, @@ -10249,7 +10252,7 @@ fn llvmFieldIndex( llvm_field_index += 1; } - if (field_index == i) { + if (field_index <= i) { ptr_pl_buf.* = .{ .data = .{ .pointee_type = field.ty, @@ -10768,7 +10771,7 @@ fn isByRef(ty: Type) bool { const tuple = ty.tupleFields(); var count: usize = 0; for (tuple.values) |field_val, i| { - if (field_val.tag() != .unreachable_value) continue; + if (field_val.tag() != .unreachable_value or !tuple.types[i].hasRuntimeBits()) continue; count += 1; if (count > max_fields_byval) return true; diff --git a/src/type.zig b/src/type.zig index f24c89ef6f..d80c63f3ce 100644 --- a/src/type.zig +++ b/src/type.zig @@ -640,7 +640,9 @@ pub const Type = extern union { const a_info = a.fnInfo(); const b_info = b.fnInfo(); - if (!eql(a_info.return_type, b_info.return_type, mod)) + if (a_info.return_type.tag() != .generic_poison and + b_info.return_type.tag() != .generic_poison and + !eql(a_info.return_type, b_info.return_type, mod)) return false; if (a_info.is_var_args != b_info.is_var_args) @@ -5757,7 +5759,7 @@ pub const Type = extern union { for (tuple.types) |field_ty, i| { const field_val = tuple.values[i]; - if (field_val.tag() != .unreachable_value) { + if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBits()) { // comptime field if (i == index) return offset; continue; diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 8a669b28f3..cd0891990c 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -1499,3 +1499,11 @@ test "non-optional and optional array elements concatenated" { var index: usize = 0; try expect(array[index].? == 'A'); } + +test "inline call in @TypeOf inherits is_inline property" { + const S = struct { + inline fn doNothing() void {} + const T = @TypeOf(doNothing()); + }; + try expect(S.T == void); +} diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig index 02070cb933..0d704d9680 100644 --- a/test/behavior/generics.zig +++ b/test/behavior/generics.zig @@ -405,3 +405,15 @@ test "null sentinel pointer passed as generic argument" { }; try S.doTheTest((@intToPtr([*:null]const [*c]const u8, 8))); } + +test "generic function passed as comptime argument" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + const S = struct { + fn doMath(comptime f: fn (type, i32, i32) error{Overflow}!i32, a: i32, b: i32) !void { + const result = try f(i32, a, b); + try expect(result == 11); + } + }; + try S.doMath(std.math.add, 5, 6); +} diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 8ee7b5142a..43e62fdc93 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -489,3 +489,20 @@ test "ptrCast comptime known slice to C pointer" { var p = @ptrCast([*c]const u8, s); try std.testing.expectEqualStrings(s, std.mem.sliceTo(p, 0)); } + +test "ptrToInt on a generic function" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64 and builtin.os.tag != .linux) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag != .linux) return error.SkipZigTest; // TODO + + const S = struct { + fn generic(i: anytype) @TypeOf(i) { + return i; + } + fn doTheTest(a: anytype) !void { + try expect(@ptrToInt(a) != 0); + } + }; + try S.doTheTest(&S.generic); +} diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index a6cfd0f987..b598dca026 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -1398,3 +1398,23 @@ test "under-aligned struct field" { const result = std.mem.readIntNative(u64, array[4..12]); try expect(result == 1234); } + +test "address of zero-bit field is equal to address of only field" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + + { + const A = struct { b: void = {}, u: u8 }; + var a = A{ .u = 0 }; + const a_ptr = @fieldParentPtr(A, "b", &a.b); + try std.testing.expectEqual(&a, a_ptr); + } + { + const A = struct { u: u8, b: void = {} }; + var a = A{ .u = 0 }; + const a_ptr = @fieldParentPtr(A, "b", &a.b); + try std.testing.expectEqual(&a, a_ptr); + } +} diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig index f9a1603a5f..a172587554 100644 --- a/test/behavior/tuple.zig +++ b/test/behavior/tuple.zig @@ -323,3 +323,13 @@ test "zero sized struct in tuple handled correctly" { var s: State = undefined; try expect(s.do() == 0); } + +test "tuple type with void field and a runtime field" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const T = std.meta.Tuple(&[_]type{ usize, void }); + var t: T = .{ 5, {} }; + try expect(t[0] == 5); +} diff --git a/test/behavior/usingnamespace.zig b/test/behavior/usingnamespace.zig index 83f720ff85..97ab86b172 100644 --- a/test/behavior/usingnamespace.zig +++ b/test/behavior/usingnamespace.zig @@ -75,3 +75,7 @@ test { const a = AA.b(42); try expect(a.x == AA.c().expected); } + +comptime { + _ = @import("usingnamespace/file_1.zig"); +} diff --git a/test/behavior/usingnamespace/file_0.zig b/test/behavior/usingnamespace/file_0.zig new file mode 100644 index 0000000000..584f583c56 --- /dev/null +++ b/test/behavior/usingnamespace/file_0.zig @@ -0,0 +1 @@ +pub const A = 123; diff --git a/test/behavior/usingnamespace/file_1.zig b/test/behavior/usingnamespace/file_1.zig new file mode 100644 index 0000000000..971fb8958d --- /dev/null +++ b/test/behavior/usingnamespace/file_1.zig @@ -0,0 +1,9 @@ +const std = @import("std"); +const expect = std.testing.expect; +const imports = @import("imports.zig"); + +const A = 456; + +test { + try expect(imports.A == 123); +} diff --git a/test/behavior/usingnamespace/imports.zig b/test/behavior/usingnamespace/imports.zig new file mode 100644 index 0000000000..bbbc7dd8ca --- /dev/null +++ b/test/behavior/usingnamespace/imports.zig @@ -0,0 +1,5 @@ +const file_0 = @import("file_0.zig"); +const file_1 = @import("file_1.zig"); + +pub usingnamespace file_0; +pub usingnamespace file_1; diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 1f4faae636..36a51d8275 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -3,6 +3,7 @@ const builtin = @import("builtin"); const mem = std.mem; const math = std.math; const expect = std.testing.expect; +const expectEqual = std.testing.expectEqual; test "implicit cast vector to array - bool" { if (builtin.zig_backend == .stage1) { @@ -1231,3 +1232,17 @@ test "modRem with zero divisor" { _ = zeros[0]; } } + +test "array operands to shuffle are coerced to vectors" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const mask = [5]i32{ -1, 0, 1, 2, 3 }; + + var a = [5]u32{ 3, 5, 7, 9, 0 }; + var b = @shuffle(u32, a, @splat(5, @as(u24, 0)), mask); + try expectEqual([_]u32{ 0, 3, 5, 7, 9 }, b); +} diff --git a/test/cases/compile_errors/branch_in_comptime_only_scope_uses_condbr_inline.zig b/test/cases/compile_errors/branch_in_comptime_only_scope_uses_condbr_inline.zig new file mode 100644 index 0000000000..92e9620c99 --- /dev/null +++ b/test/cases/compile_errors/branch_in_comptime_only_scope_uses_condbr_inline.zig @@ -0,0 +1,22 @@ +pub export fn entry1() void { + var x: u32 = 3; + _ = @shuffle(u32, [_]u32{0}, @splat(1, @as(u32, 0)), [_]i8{ + if (x > 1) 1 else -1, + }); +} + +pub export fn entry2() void { + var y: ?i8 = -1; + _ = @shuffle(u32, [_]u32{0}, @splat(1, @as(u32, 0)), [_]i8{ + y orelse 1, + }); +} + +// error +// backend=stage2 +// target=native +// +// :4:15: error: unable to resolve comptime value +// :4:15: note: condition in comptime branch must be comptime-known +// :11:11: error: unable to resolve comptime value +// :11:11: note: condition in comptime branch must be comptime-known diff --git a/test/cases/compile_errors/pointless discard.zig b/test/cases/compile_errors/pointless discard.zig index 048bf8ac8d..14400a4423 100644 --- a/test/cases/compile_errors/pointless discard.zig +++ b/test/cases/compile_errors/pointless discard.zig @@ -3,6 +3,14 @@ export fn foo() void { x += 1; _ = x; } +export fn bar() void { + var b: u32 = 1; + _ = blk: { + const a = 1; + b = a; + break :blk a; + }; +} // error // backend=stage2