diff --git a/releasenotes.md b/releasenotes.md index c374272b9..c24e9f6af 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -86,6 +86,7 @@ - Remove unnecessary "ret" in naked functions #2344. - Lambdas now properly follow its attributes #2346. - Not setting android-ndk resulted in a "set ndk-path" error. +- Lambda deduplication would be incorrect when generated at the global scope. ### Stdlib changes - Improve contract for readline. #2280 diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 74b2e3042..52ba3148c 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -9925,6 +9925,7 @@ INLINE bool lambda_parameter_match(Decl **ct_lambda_params, Decl *candidate) case VARDECL_PARAM_CT: if (!expr_is_const(ct_param->var.init_expr)) return false; if (!expr_is_const(param->var.init_expr)) return false; + if (!expr_both_const_foldable(ct_param->var.init_expr, param->var.init_expr, BINARYOP_EQ)) return false; if (!expr_const_compare(&ct_param->var.init_expr->const_expr, ¶m->var.init_expr->const_expr, BINARYOP_EQ)) return false; break; @@ -10186,6 +10187,11 @@ static inline bool sema_expr_analyse_lambda(SemaContext *context, Type *target_t // Before function analysis, lambda evaluation is deferred if (unit->module->stage < ANALYSIS_FUNCTIONS) { + // Because we cannot check if the parameter is used before everything, set them all as read. + FOREACH(Decl *, decl, ct_lambda_parameters) + { + decl->var.is_read = true; + } vec_add(unit->module->lambdas_to_evaluate, decl); } else diff --git a/test/test_suite/lambda/lambda_global_matches.c3t b/test/test_suite/lambda/lambda_global_matches.c3t new file mode 100644 index 000000000..c033de692 --- /dev/null +++ b/test/test_suite/lambda/lambda_global_matches.c3t @@ -0,0 +1,118 @@ +// #target: macos-x64 +module test; +import std::io; +alias IntFn = fn int(); + +macro IntFn @dofn($m, $n) { + return fn int () => $m; +} + +macro IntFn[*] @doallfn($n) { + IntFn[$n] $vec; + $for var $i = 0; $i < $n; $i++: + $vec[$i] = @dofn($i, $i); + $endfor + return $vec; +} + +IntFn[*] lambda_arr = @doallfn(3); + +fn void main() +{ + int x; + $for var $i = 0; $i < 4; $i++: + x = @dofn($i, 0)(); + $endfor + $for var $i = 0; $i < 4; $i++: + x = @dofn($i, 1)(); + $endfor + foreach (i : lambda_arr) + { + x = i(); + } +} + +/* #expect: test.ll + + +define void @test.main() #0 { +entry: + %x = alloca i32, align 4 + %.anon = alloca i64, align 8 + %i = alloca ptr, align 8 + store i32 0, ptr %x, align 4 + %0 = call i32 @"test.$global$lambda1"() + store i32 %0, ptr %x, align 4 + %1 = call i32 @"test.@dofn$lambda4"() + store i32 %1, ptr %x, align 4 + %2 = call i32 @"test.@dofn$lambda5"() + store i32 %2, ptr %x, align 4 + %3 = call i32 @"test.@dofn$lambda6"() + store i32 %3, ptr %x, align 4 + %4 = call i32 @"test.@dofn$lambda7"() + store i32 %4, ptr %x, align 4 + %5 = call i32 @"test.$global$lambda2"() + store i32 %5, ptr %x, align 4 + %6 = call i32 @"test.@dofn$lambda5"() + store i32 %6, ptr %x, align 4 + %7 = call i32 @"test.@dofn$lambda6"() + store i32 %7, ptr %x, align 4 + store i64 0, ptr %.anon, align 8 + br label %loop.cond + +loop.cond: ; preds = %loop.body, %entry + %8 = load i64, ptr %.anon, align 8 + %gt = icmp ugt i64 3, %8 + br i1 %gt, label %loop.body, label %loop.exit + +loop.body: ; preds = %loop.cond + %9 = load i64, ptr %.anon, align 8 + %ptroffset = getelementptr inbounds [8 x i8], ptr @test.lambda_arr, i64 %9 + %10 = load ptr, ptr %ptroffset, align 8 + store ptr %10, ptr %i, align 8 + %11 = load ptr, ptr %i, align 8 + %12 = call i32 %11() + store i32 %12, ptr %x, align 4 + %13 = load i64, ptr %.anon, align 8 + %addnuw = add nuw i64 %13, 1 + store i64 %addnuw, ptr %.anon, align 8 + br label %loop.cond + +loop.exit: ; preds = %loop.cond + ret void +} + +define internal i32 @"test.@dofn$lambda4"() #0 { +entry: + ret i32 1 +} + +define internal i32 @"test.@dofn$lambda5"() #0 { +entry: + ret i32 2 +} + +define internal i32 @"test.@dofn$lambda6"() #0 { +entry: + ret i32 3 +} + +define internal i32 @"test.@dofn$lambda7"() #0 { +entry: + ret i32 0 +} + +define internal i32 @"test.$global$lambda3"() #0 { +entry: + ret i32 2 +} + +define internal i32 @"test.$global$lambda2"() #0 { +entry: + ret i32 1 +} + +define internal i32 @"test.$global$lambda1"() #0 { +entry: + ret i32 0 +}