diff --git a/doc/langref.html.in b/doc/langref.html.in index 7a3c96595c..4f39ad72dd 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -12062,24 +12062,24 @@ ContainerMembers <- ContainerDeclarations (ContainerField COMMA)* (ContainerFiel ContainerDeclarations <- TestDecl ContainerDeclarations - / TopLevelComptime ContainerDeclarations - / doc_comment? KEYWORD_pub? TopLevelDecl ContainerDeclarations + / ComptimeDecl ContainerDeclarations + / doc_comment? KEYWORD_pub? Decl ContainerDeclarations / -TestDecl <- doc_comment? KEYWORD_test STRINGLITERALSINGLE? Block +TestDecl <- KEYWORD_test STRINGLITERALSINGLE? Block -TopLevelComptime <- doc_comment? KEYWORD_comptime BlockExpr +ComptimeDecl <- KEYWORD_comptime Block -TopLevelDecl +Decl <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block) / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl / KEYWORD_usingnamespace Expr SEMICOLON -FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr +FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr -VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON +VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection? (EQUAL Expr)? SEMICOLON -ContainerField <- doc_comment? KEYWORD_comptime? IDENTIFIER (COLON (KEYWORD_anytype / TypeExpr) ByteAlign?)? (EQUAL Expr)? +ContainerField <- doc_comment? KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)? # *** Block Level *** Statement @@ -12240,6 +12240,8 @@ WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN +AddrSpace <- KEYWORD_addrspace LPAREN Expr RPAREN + # Fn specific CallConv <- KEYWORD_callconv LPAREN Expr RPAREN @@ -12267,7 +12269,7 @@ PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE # Switch specific -SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr +SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? AssignExpr SwitchCase <- SwitchItem (COMMA SwitchItem)* COMMA? @@ -12278,11 +12280,15 @@ SwitchItem <- Expr (DOT3 Expr)? # Operators AssignOp <- ASTERISKEQUAL + / ASTERISKPIPEEQUAL / SLASHEQUAL / PERCENTEQUAL / PLUSEQUAL + / PLUSPIPEEQUAL / MINUSEQUAL + / MINUSPIPEEQUAL / LARROW2EQUAL + / LARROW2PIPEEQUAL / RARROW2EQUAL / AMPERSANDEQUAL / CARETEQUAL @@ -12310,6 +12316,7 @@ BitwiseOp BitShiftOp <- LARROW2 / RARROW2 + / LARROW2PIPE AdditionOp <- PLUS @@ -12317,6 +12324,8 @@ AdditionOp / PLUS2 / PLUSPERCENT / MINUSPERCENT + / PLUSPIPE + / MINUSPIPE MultiplyOp <- PIPE2 @@ -12325,6 +12334,7 @@ MultiplyOp / PERCENT / ASTERISK2 / ASTERISKPERCENT + / ASTERISKPIPE PrefixOp <- EXCLAMATIONMARK @@ -12338,8 +12348,8 @@ PrefixOp PrefixTypeOp <- QUESTIONMARK / KEYWORD_anyframe MINUSRARROW - / SliceTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* - / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* + / SliceTypeStart (ByteAlign / AddrSpace / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* + / PtrTypeStart (AddrSpace / KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* / ArrayTypeStart SuffixOp @@ -12364,7 +12374,7 @@ ArrayTypeStart <- LBRACKET Expr (COLON Expr)? RBRACKET ContainerDeclAuto <- ContainerDeclType LBRACE container_doc_comment? ContainerMembers RBRACE ContainerDeclType - <- KEYWORD_struct + <- KEYWORD_struct (LPAREN Expr RPAREN)? / KEYWORD_opaque / KEYWORD_enum (LPAREN Expr RPAREN)? / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)? @@ -12455,7 +12465,7 @@ string_char / [^\\"\n] container_doc_comment <- ('//!' [^\n]* [ \n]*)+ -doc_comment <- ('///' [^\n]* [ \n]*)+ +doc_comment <- ('///' [^\n]* [ \n]*)+ skip line_comment <- '//' ![!/][^\n]* / '////' [^\n]* line_string <- ("\\\\" [^\n]* [ \n]*)+ skip <- ([ \n] / line_comment)* @@ -12483,11 +12493,13 @@ BUILTINIDENTIFIER <- "@"[A-Za-z_][A-Za-z0-9_]* skip AMPERSAND <- '&' ![=] skip AMPERSANDEQUAL <- '&=' skip -ASTERISK <- '*' ![*%=] skip +ASTERISK <- '*' ![*%=|] skip ASTERISK2 <- '**' skip ASTERISKEQUAL <- '*=' skip ASTERISKPERCENT <- '*%' ![=] skip ASTERISKPERCENTEQUAL <- '*%=' skip +ASTERISKPIPE <- '*|' ![=] skip +ASTERISKPIPEEQUAL <- '*|=' skip CARET <- '^' ![=] skip CARETEQUAL <- '^=' skip COLON <- ':' skip @@ -12503,27 +12515,33 @@ EQUALRARROW <- '=>' skip EXCLAMATIONMARK <- '!' ![=] skip EXCLAMATIONMARKEQUAL <- '!=' skip LARROW <- '<' ![<=] skip -LARROW2 <- '<<' ![=] skip +LARROW2 <- '<<' ![=|] skip LARROW2EQUAL <- '<<=' skip +LARROW2PIPE <- '<<|' ![=] skip +LARROW2PIPEEQUAL <- '<<|=' skip LARROWEQUAL <- '<=' skip LBRACE <- '{' skip LBRACKET <- '[' skip LPAREN <- '(' skip -MINUS <- '-' ![%=>] skip +MINUS <- '-' ![%=>|] skip MINUSEQUAL <- '-=' skip MINUSPERCENT <- '-%' ![=] skip MINUSPERCENTEQUAL <- '-%=' skip +MINUSPIPE <- '-|' ![=] skip +MINUSPIPEEQUAL <- '-|=' skip MINUSRARROW <- '->' skip PERCENT <- '%' ![=] skip PERCENTEQUAL <- '%=' skip PIPE <- '|' ![|=] skip PIPE2 <- '||' skip PIPEEQUAL <- '|=' skip -PLUS <- '+' ![%+=] skip +PLUS <- '+' ![%+=|] skip PLUS2 <- '++' skip PLUSEQUAL <- '+=' skip PLUSPERCENT <- '+%' ![=] skip PLUSPERCENTEQUAL <- '+%=' skip +PLUSPIPE <- '+|' ![=] skip +PLUSPIPEEQUAL <- '+|=' skip LETTERC <- 'c' skip QUESTIONMARK <- '?' skip RARROW <- '>' ![>=] skip @@ -12539,6 +12557,7 @@ SLASHEQUAL <- '/=' skip TILDE <- '~' skip end_of_word <- ![a-zA-Z0-9_] skip +KEYWORD_addrspace <- 'addrspace' end_of_word KEYWORD_align <- 'align' end_of_word KEYWORD_allowzero <- 'allowzero' end_of_word KEYWORD_and <- 'and' end_of_word @@ -12588,11 +12607,11 @@ KEYWORD_var <- 'var' end_of_word KEYWORD_volatile <- 'volatile' end_of_word KEYWORD_while <- 'while' end_of_word -keyword <- KEYWORD_align / KEYWORD_allowzero / KEYWORD_and / KEYWORD_anyframe - / KEYWORD_anytype / KEYWORD_asm / KEYWORD_async / KEYWORD_await - / KEYWORD_break / KEYWORD_callconv / KEYWORD_catch / KEYWORD_comptime - / KEYWORD_const / KEYWORD_continue / KEYWORD_defer / KEYWORD_else - / KEYWORD_enum / KEYWORD_errdefer / KEYWORD_error / KEYWORD_export +keyword <- KEYWORD_addrspace / KEYWORD_align / KEYWORD_allowzero / KEYWORD_and + / KEYWORD_anyframe / KEYWORD_anytype / KEYWORD_asm / KEYWORD_async + / KEYWORD_await / KEYWORD_break / KEYWORD_callconv / KEYWORD_catch + / KEYWORD_comptime / KEYWORD_const / KEYWORD_continue / KEYWORD_defer + / KEYWORD_else / KEYWORD_enum / KEYWORD_errdefer / KEYWORD_error / KEYWORD_export / KEYWORD_extern / KEYWORD_fn / KEYWORD_for / KEYWORD_if / KEYWORD_inline / KEYWORD_noalias / KEYWORD_nosuspend / KEYWORD_noinline / KEYWORD_opaque / KEYWORD_or / KEYWORD_orelse / KEYWORD_packed diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 205b094e93..ced4b05ba9 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -240,10 +240,10 @@ const Parser = struct { /// ContainerMembers <- ContainerDeclarations (ContainerField COMMA)* (ContainerField / ContainerDeclarations) /// ContainerDeclarations /// <- TestDecl ContainerDeclarations - /// / TopLevelComptime ContainerDeclarations - /// / KEYWORD_pub? TopLevelDecl ContainerDeclarations + /// / ComptimeDecl ContainerDeclarations + /// / doc_comment? KEYWORD_pub? Decl ContainerDeclarations /// / - /// TopLevelComptime <- KEYWORD_comptime Block + /// ComptimeDecl <- KEYWORD_comptime Block fn parseContainerMembers(p: *Parser) !Members { const scratch_top = p.scratch.items.len; defer p.scratch.shrinkRetainingCapacity(scratch_top); @@ -622,7 +622,7 @@ const Parser = struct { }; } - /// TopLevelDecl + /// Decl /// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block) /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl /// / KEYWORD_usingnamespace Expr SEMICOLON diff --git a/src/Sema.zig b/src/Sema.zig index de9018be6e..d25f3076d2 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -335,6 +335,7 @@ pub const Block = struct { /// It is shared among all the blocks in an inline or comptime called /// function. pub const Inlining = struct { + func: ?*Module.Fn, comptime_result: Air.Inst.Ref, merges: Merges, }; @@ -6428,7 +6429,6 @@ fn analyzeCall( }), else => unreachable, }; - if (!is_comptime_call and module_fn.state == .sema_failure) return error.AnalysisFail; if (func_ty_info.is_var_args) { return sema.fail(block, call_src, "{s} call of variadic function", .{ @as([]const u8, if (is_comptime_call) "comptime" else "inline"), @@ -6448,6 +6448,7 @@ fn analyzeCall( // This one is shared among sub-blocks within the same callee, but not // shared among the entire inline/comptime call stack. var inlining: Block.Inlining = .{ + .func = null, .comptime_result = undefined, .merges = .{ .results = .{}, @@ -6534,6 +6535,7 @@ fn analyzeCall( const fn_info = sema.code.getFnInfo(module_fn.zir_body_inst); try sema.inst_map.ensureSpaceForInstructions(sema.gpa, fn_info.param_body); + var has_comptime_args = false; var arg_i: usize = 0; for (fn_info.param_body) |inst| { sema.analyzeInlineCallArg( @@ -6549,6 +6551,7 @@ fn analyzeCall( memoized_call_key, func_ty_info.param_types, func, + &has_comptime_args, ) catch |err| switch (err) { error.NeededSourceLocation => { _ = sema.inst_map.remove(inst); @@ -6566,6 +6569,7 @@ fn analyzeCall( memoized_call_key, func_ty_info.param_types, func, + &has_comptime_args, ); unreachable; }, @@ -6573,6 +6577,19 @@ fn analyzeCall( }; } + if (!has_comptime_args and module_fn.state == .sema_failure) return error.AnalysisFail; + + const recursive_msg = "inline call is recursive"; + var head = if (!has_comptime_args) block else null; + while (head) |some| { + const parent_inlining = some.inlining orelse break; + if (parent_inlining.func == module_fn) { + return sema.fail(block, call_src, recursive_msg, .{}); + } + head = some.parent; + } + if (!has_comptime_args) inlining.func = module_fn; + // In case it is a generic function with an expression for the return type that depends // on parameters, we must now do the same for the return type as we just did with // each of the parameters, resolving the return type and providing it to the child @@ -6657,6 +6674,7 @@ fn analyzeCall( error.ComptimeReturn => break :result inlining.comptime_result, error.AnalysisFail => { const err_msg = sema.err orelse return err; + if (std.mem.eql(u8, err_msg.msg, recursive_msg)) return err; try sema.errNote(block, call_src, err_msg, "called from here", .{}); err_msg.clearTrace(sema.gpa); return err; @@ -6814,8 +6832,13 @@ fn analyzeInlineCallArg( memoized_call_key: Module.MemoizedCall.Key, raw_param_types: []const Type, func_inst: Air.Inst.Ref, + has_comptime_args: *bool, ) !void { const zir_tags = sema.code.instructions.items(.tag); + switch (zir_tags[inst]) { + .param_comptime, .param_anytype_comptime => has_comptime_args.* = true, + else => {}, + } switch (zir_tags[inst]) { .param, .param_comptime => { // Evaluate the parameter type expression now that previous ones have @@ -6870,23 +6893,20 @@ fn analyzeInlineCallArg( .ty = param_ty, .val = arg_val, }; - } else if (zir_tags[inst] == .param_comptime or try sema.typeRequiresComptime(param_ty)) { - sema.inst_map.putAssumeCapacityNoClobber(inst, casted_arg); - } else if (try sema.resolveMaybeUndefVal(casted_arg)) |val| { - // We have a comptime value but we need a runtime value to preserve inlining semantics, - const wrapped = try sema.addConstant(param_ty, try Value.Tag.runtime_value.create(sema.arena, val)); - sema.inst_map.putAssumeCapacityNoClobber(inst, wrapped); } else { sema.inst_map.putAssumeCapacityNoClobber(inst, casted_arg); } + if (try sema.resolveMaybeUndefVal(casted_arg)) |_| { + has_comptime_args.* = true; + } + arg_i.* += 1; }, .param_anytype, .param_anytype_comptime => { // No coercion needed. const uncasted_arg = uncasted_args[arg_i.*]; new_fn_info.param_types[arg_i.*] = sema.typeOf(uncasted_arg); - const param_ty = sema.typeOf(uncasted_arg); if (is_comptime_call) { sema.inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg); @@ -6912,16 +6932,14 @@ fn analyzeInlineCallArg( .ty = sema.typeOf(uncasted_arg), .val = arg_val, }; - } else if (zir_tags[inst] == .param_anytype_comptime or try sema.typeRequiresComptime(param_ty)) { - sema.inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg); - } else if (try sema.resolveMaybeUndefVal(uncasted_arg)) |val| { - // We have a comptime value but we need a runtime value to preserve inlining semantics, - const wrapped = try sema.addConstant(param_ty, try Value.Tag.runtime_value.create(sema.arena, val)); - sema.inst_map.putAssumeCapacityNoClobber(inst, wrapped); } else { sema.inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg); } + if (try sema.resolveMaybeUndefVal(uncasted_arg)) |_| { + has_comptime_args.* = true; + } + arg_i.* += 1; }, else => {}, @@ -33075,7 +33093,7 @@ fn elemPtrType(sema: *Sema, ptr_ty: Type, offset: ?usize) !Type { host_size: u16 = 0, alignment: u32 = 0, vector_index: VI = .none, - } = if (parent_ty.tag() == .vector) blk: { + } = if (parent_ty.tag() == .vector and ptr_info.size == .One) blk: { const elem_bits = elem_ty.bitSize(target); if (elem_bits == 0) break :blk .{}; const is_packed = elem_bits < 8 or !std.math.isPowerOfTwo(elem_bits); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 9fc3055969..4c553f0305 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3361,6 +3361,10 @@ pub const DeclGen = struct { const llvm_type = try dg.lowerType(tv.ty); return llvm_type.constNull(); }, + .opt_payload => { + const payload = tv.val.castTag(.opt_payload).?.data; + return dg.lowerParentPtr(payload, tv.ty); + }, else => |tag| return dg.todo("implement const of pointer type '{}' ({})", .{ tv.ty.fmtDebug(), tag, }), diff --git a/test/behavior.zig b/test/behavior.zig index 36403a8f0c..72f6bf8e6a 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -133,7 +133,6 @@ test { _ = @import("behavior/bugs/13113.zig"); _ = @import("behavior/bugs/13128.zig"); _ = @import("behavior/bugs/13159.zig"); - _ = @import("behavior/bugs/13164.zig"); _ = @import("behavior/bugs/13171.zig"); _ = @import("behavior/bugs/13209.zig"); _ = @import("behavior/bugs/13285.zig"); diff --git a/test/behavior/bugs/13164.zig b/test/behavior/bugs/13164.zig deleted file mode 100644 index 66f4e28fd8..0000000000 --- a/test/behavior/bugs/13164.zig +++ /dev/null @@ -1,18 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); - -inline fn setLimits(min: ?u32, max: ?u32) !void { - if (min != null and max != null) { - try std.testing.expect(min.? <= max.?); - } -} - -test { - 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 - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - - var x: u32 = 42; - try setLimits(x, null); -} diff --git a/test/behavior/call.zig b/test/behavior/call.zig index 4addd93227..73ffe7c157 100644 --- a/test/behavior/call.zig +++ b/test/behavior/call.zig @@ -381,3 +381,17 @@ test "generic function with generic function parameter" { }; try S.f(S.g, 123); } + +test "recursive inline call with comptime known argument" { + const S = struct { + inline fn foo(x: i32) i32 { + if (x <= 0) { + return 0; + } else { + return x * 2 + foo(x - 1); + } + } + }; + + try expect(S.foo(4) == 20); +} diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 322142d8fc..7a441a925d 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -1261,3 +1261,19 @@ test "store packed vector element" { v[0] = 0; try expectEqual(@Vector(4, u1){ 0, 1, 1, 1 }, v); } + +test "store to vector in slice" { + 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_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + var v = [_]@Vector(3, f32){ + .{ 1, 1, 1 }, + .{ 0, 0, 0 }, + }; + var s: []@Vector(3, f32) = &v; + var i: usize = 1; + s[i] = s[0]; + try expectEqual(v[1], v[0]); +} diff --git a/test/cases/compile_errors/recursive_inline_fn.zig b/test/cases/compile_errors/recursive_inline_fn.zig new file mode 100644 index 0000000000..2308bbdbc7 --- /dev/null +++ b/test/cases/compile_errors/recursive_inline_fn.zig @@ -0,0 +1,18 @@ +inline fn foo(x: i32) i32 { + if (x <= 0) { + return 0; + } else { + return x * 2 + foo(x - 1); + } +} + +pub export fn entry() void { + var x: i32 = 4; + _ = foo(x) == 20; +} + +// error +// backend=stage2 +// target=native +// +// :5:27: error: inline call is recursive