diff --git a/src/analyze.cpp b/src/analyze.cpp index c7ba8ccc74..aaf7c08bd8 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4631,7 +4631,51 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) { zig_unreachable(); } -bool fn_eval_cacheable(Scope *scope) { +static bool return_type_is_cacheable(TypeTableEntry *return_type) { + switch (return_type->id) { + case TypeTableEntryIdInvalid: + zig_unreachable(); + case TypeTableEntryIdMetaType: + case TypeTableEntryIdVoid: + case TypeTableEntryIdBool: + case TypeTableEntryIdUnreachable: + case TypeTableEntryIdInt: + case TypeTableEntryIdFloat: + case TypeTableEntryIdNumLitFloat: + case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdUndefLit: + case TypeTableEntryIdNullLit: + case TypeTableEntryIdNamespace: + case TypeTableEntryIdBoundFn: + case TypeTableEntryIdFn: + case TypeTableEntryIdBlock: + case TypeTableEntryIdOpaque: + case TypeTableEntryIdPromise: + case TypeTableEntryIdErrorSet: + case TypeTableEntryIdEnum: + case TypeTableEntryIdPointer: + return true; + + case TypeTableEntryIdArray: + case TypeTableEntryIdStruct: + case TypeTableEntryIdUnion: + return false; + + case TypeTableEntryIdMaybe: + return return_type_is_cacheable(return_type->data.maybe.child_type); + + case TypeTableEntryIdErrorUnion: + return return_type_is_cacheable(return_type->data.error_union.payload_type); + + case TypeTableEntryIdArgTuple: + zig_panic("TODO var args at comptime is currently not supported"); + } + zig_unreachable(); +} + +bool fn_eval_cacheable(Scope *scope, TypeTableEntry *return_type) { + if (!return_type_is_cacheable(return_type)) + return false; while (scope) { if (scope->id == ScopeIdVarDecl) { ScopeVarDecl *var_scope = (ScopeVarDecl *)scope; diff --git a/src/analyze.hpp b/src/analyze.hpp index d754226086..936134030d 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -196,6 +196,6 @@ TypeTableEntry *get_auto_err_set_type(CodeGen *g, FnTableEntry *fn_entry); uint32_t get_coro_frame_align_bytes(CodeGen *g); bool fn_type_can_fail(FnTypeId *fn_type_id); bool type_can_fail(TypeTableEntry *type_entry); -bool fn_eval_cacheable(Scope *scope); +bool fn_eval_cacheable(Scope *scope, TypeTableEntry *return_type); #endif diff --git a/src/ir.cpp b/src/ir.cpp index a52aaa2086..553fbcbf06 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -11878,7 +11878,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal return_type = specified_return_type; } - bool cacheable = fn_eval_cacheable(exec_scope); + bool cacheable = fn_eval_cacheable(exec_scope, return_type); IrInstruction *result = nullptr; if (cacheable) { auto entry = ira->codegen->memoized_fn_eval_table.maybe_get(exec_scope); diff --git a/std/base64.zig b/std/base64.zig index d9e1d2f908..13f3ea5714 100644 --- a/std/base64.zig +++ b/std/base64.zig @@ -369,7 +369,7 @@ fn calcDecodedSizeExactUnsafe(source: []const u8, pad_char: u8) usize { test "base64" { - @setEvalBranchQuota(5000); + @setEvalBranchQuota(8000); testBase64() catch unreachable; comptime (testBase64() catch unreachable); } diff --git a/test/behavior.zig b/test/behavior.zig index a8ff7958cf..de39b20dad 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -11,6 +11,7 @@ comptime { _ = @import("cases/bugs/394.zig"); _ = @import("cases/bugs/655.zig"); _ = @import("cases/bugs/656.zig"); + _ = @import("cases/bugs/828.zig"); _ = @import("cases/cast.zig"); _ = @import("cases/const_slice_child.zig"); _ = @import("cases/coroutines.zig"); diff --git a/test/cases/bugs/828.zig b/test/cases/bugs/828.zig new file mode 100644 index 0000000000..c46548cb7a --- /dev/null +++ b/test/cases/bugs/828.zig @@ -0,0 +1,37 @@ +const CountBy = struct { + a: usize, + + const One = CountBy { + .a = 1, + }; + + pub fn counter(self: &const CountBy) Counter { + return Counter { + .i = 0, + }; + } +}; + +const Counter = struct { + i: usize, + + pub fn count(self: &Counter) bool { + self.i += 1; + return self.i <= 10; + } +}; + +fn constCount(comptime cb: &const CountBy, comptime unused: u32) void { + comptime { + var cnt = cb.counter(); + if(cnt.i != 0) @compileError("Counter instance reused!"); + while(cnt.count()){} + } +} + +test "comptime struct return should not return the same instance" { + //the first parameter must be passed by reference to trigger the bug + //a second parameter is required to trigger the bug + const ValA = constCount(&CountBy.One, 12); + const ValB = constCount(&CountBy.One, 15); +}