Skip to content

Commit dbb0e57

Browse files
authored
Fix RCA panic on lambda with explicit return (#2194)
This fixes a quirk leftover from the fixes in #649 and #793. Those introduced and then updated a conditional that avoided unifying certain types during type checking, but it turns out it was always safe to unconditionally unify the types once #649 introduced the explicit return type tracking. With this fix, the lifted lambda callables with explicit returns have the correct output type instead of always being `Unit` and RCA is able to use that type information as expected, avoiding the panic. Fixes #2186
1 parent c6926d3 commit dbb0e57

File tree

4 files changed

+139
-21
lines changed

4 files changed

+139
-21
lines changed

compiler/qsc_frontend/src/lower/tests.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2380,6 +2380,90 @@ fn lambda_with_invalid_free_variable() {
23802380
);
23812381
}
23822382

2383+
#[test]
2384+
fn lambda_with_explicit_return_should_have_non_unit_output_type() {
2385+
check_hir(
2386+
"function Foo(): Unit { let f = i -> return i + 1;}",
2387+
&expect![[r#"
2388+
Package:
2389+
Item 0 [0-50] (Public):
2390+
Namespace (Ident 21 [0-50] "test"): Item 1
2391+
Item 1 [0-50] (Internal):
2392+
Parent: 0
2393+
Callable 0 [0-50] (function):
2394+
name: Ident 1 [9-12] "Foo"
2395+
input: Pat 2 [12-14] [Type Unit]: Unit
2396+
output: Unit
2397+
functors: empty set
2398+
body: SpecDecl 3 [0-50]: Impl:
2399+
Block 4 [21-50] [Type Unit]:
2400+
Stmt 5 [23-49]: Local (Immutable):
2401+
Pat 6 [27-28] [Type (Int -> Int)]: Bind: Ident 7 [27-28] "f"
2402+
Expr 8 [31-48] [Type (Int -> Int)]: Closure([], 2)
2403+
adj: <none>
2404+
ctl: <none>
2405+
ctl-adj: <none>
2406+
Item 2 [31-48] (Internal):
2407+
Parent: 1
2408+
Callable 16 [31-48] (function):
2409+
name: Ident 17 [31-48] "<lambda>"
2410+
input: Pat 15 [31-48] [Type (Int,)]: Tuple:
2411+
Pat 9 [31-32] [Type Int]: Bind: Ident 10 [31-32] "i"
2412+
output: Int
2413+
functors: empty set
2414+
body: SpecDecl 18 [36-48]: Impl:
2415+
Block 19 [36-48] [Type Int]:
2416+
Stmt 20 [36-48]: Expr: Expr 11 [36-48] [Type Int]: Return: Expr 12 [43-48] [Type Int]: BinOp (Add):
2417+
Expr 13 [43-44] [Type Int]: Var: Local 10
2418+
Expr 14 [47-48] [Type Int]: Lit: Int(1)
2419+
adj: <none>
2420+
ctl: <none>
2421+
ctl-adj: <none>"#]],
2422+
);
2423+
}
2424+
2425+
#[test]
2426+
fn lambda_with_implicit_return_should_have_non_unit_output_type() {
2427+
check_hir(
2428+
"function Foo(): Unit { let f = i -> i + 1}",
2429+
&expect![[r#"
2430+
Package:
2431+
Item 0 [0-42] (Public):
2432+
Namespace (Ident 20 [0-42] "test"): Item 1
2433+
Item 1 [0-42] (Internal):
2434+
Parent: 0
2435+
Callable 0 [0-42] (function):
2436+
name: Ident 1 [9-12] "Foo"
2437+
input: Pat 2 [12-14] [Type Unit]: Unit
2438+
output: Unit
2439+
functors: empty set
2440+
body: SpecDecl 3 [0-42]: Impl:
2441+
Block 4 [21-42] [Type Unit]:
2442+
Stmt 5 [23-41]: Local (Immutable):
2443+
Pat 6 [27-28] [Type (Int -> Int)]: Bind: Ident 7 [27-28] "f"
2444+
Expr 8 [31-41] [Type (Int -> Int)]: Closure([], 2)
2445+
adj: <none>
2446+
ctl: <none>
2447+
ctl-adj: <none>
2448+
Item 2 [31-41] (Internal):
2449+
Parent: 1
2450+
Callable 15 [31-41] (function):
2451+
name: Ident 16 [31-41] "<lambda>"
2452+
input: Pat 14 [31-41] [Type (Int,)]: Tuple:
2453+
Pat 9 [31-32] [Type Int]: Bind: Ident 10 [31-32] "i"
2454+
output: Int
2455+
functors: empty set
2456+
body: SpecDecl 17 [36-41]: Impl:
2457+
Block 18 [36-41] [Type Int]:
2458+
Stmt 19 [36-41]: Expr: Expr 11 [36-41] [Type Int]: BinOp (Add):
2459+
Expr 12 [36-37] [Type Int]: Var: Local 10
2460+
Expr 13 [40-41] [Type Int]: Lit: Int(1)
2461+
adj: <none>
2462+
ctl: <none>
2463+
ctl-adj: <none>"#]],
2464+
);
2465+
}
2466+
23832467
#[test]
23842468
fn duplicate_commas_in_tydef() {
23852469
check_hir(

compiler/qsc_frontend/src/typeck/rules.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -399,12 +399,8 @@ impl<'a> Context<'a> {
399399
.take()
400400
.expect("return type should be present");
401401
self.return_ty = prev_ret_ty;
402-
if !body_partial.diverges {
403-
// Only when the type of the body converges do we need to unify with the inferred output type.
404-
// Otherwise we'd get spurious errors from lambdas that use explicit return-expr rather than implicit.
405-
self.inferrer
406-
.eq(body.span, output_ty.clone(), body_partial.ty);
407-
}
402+
self.inferrer
403+
.eq(body.span, body_partial.ty, output_ty.clone());
408404
converge(Ty::Arrow(Box::new(Arrow {
409405
kind: convert::callable_kind_from_ast(*kind),
410406
input: Box::new(input),

compiler/qsc_frontend/src/typeck/tests.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3164,8 +3164,8 @@ fn lambda_inner_return() {
31643164
#12 54-55 "f" : (Unit -> Int)
31653165
#14 58-98 "() -> {\n return 42;\n }" : (Unit -> Int)
31663166
#15 58-60 "()" : Unit
3167-
#16 64-98 "{\n return 42;\n }" : Unit
3168-
#17 64-98 "{\n return 42;\n }" : Unit
3167+
#16 64-98 "{\n return 42;\n }" : Int
3168+
#17 64-98 "{\n return 42;\n }" : Int
31693169
#19 78-87 "return 42" : Unit
31703170
#20 85-87 "42" : Int
31713171
#22 112-113 "r" : Int
@@ -3197,8 +3197,8 @@ fn lambda_inner_return_without_call_ambiguous() {
31973197
#15 58-64 "(a, b)" : (?2, ?2)
31983198
#16 59-60 "a" : ?2
31993199
#18 62-63 "b" : ?2
3200-
#20 68-105 "{\n return a + b;\n }" : Unit
3201-
#21 68-105 "{\n return a + b;\n }" : Unit
3200+
#20 68-105 "{\n return a + b;\n }" : ?2
3201+
#21 68-105 "{\n return a + b;\n }" : ?2
32023202
#23 82-94 "return a + b" : Unit
32033203
#24 89-94 "a + b" : ?2
32043204
#25 89-90 "a" : ?2
@@ -3224,17 +3224,17 @@ fn lambda_implicit_return_without_call_ambiguous() {
32243224
&expect![[r#"
32253225
#6 30-32 "()" : Unit
32263226
#10 40-104 "{\n let f = (a, b) -> {\n a + b\n };\n }" : Unit
3227-
#12 54-55 "f" : ((?2, ?2) -> ?2)
3228-
#14 58-97 "(a, b) -> {\n a + b\n }" : ((?2, ?2) -> ?2)
3229-
#15 58-64 "(a, b)" : (?2, ?2)
3230-
#16 59-60 "a" : ?2
3231-
#18 62-63 "b" : ?2
3232-
#20 68-97 "{\n a + b\n }" : ?2
3233-
#21 68-97 "{\n a + b\n }" : ?2
3234-
#23 82-87 "a + b" : ?2
3235-
#24 82-83 "a" : ?2
3236-
#27 86-87 "b" : ?2
3237-
Error(Type(Error(AmbiguousTy(Span { lo: 62, hi: 63 }))))
3227+
#12 54-55 "f" : ((?3, ?3) -> ?3)
3228+
#14 58-97 "(a, b) -> {\n a + b\n }" : ((?3, ?3) -> ?3)
3229+
#15 58-64 "(a, b)" : (?3, ?3)
3230+
#16 59-60 "a" : ?3
3231+
#18 62-63 "b" : ?3
3232+
#20 68-97 "{\n a + b\n }" : ?3
3233+
#21 68-97 "{\n a + b\n }" : ?3
3234+
#23 82-87 "a + b" : ?3
3235+
#24 82-83 "a" : ?3
3236+
#27 86-87 "b" : ?3
3237+
Error(Type(Error(AmbiguousTy(Span { lo: 68, hi: 97 }))))
32383238
"#]],
32393239
);
32403240
}

compiler/qsc_rca/src/tests/lambdas.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,3 +217,41 @@ fn check_rca_for_operation_lambda_two_parameters_with_controls() {
217217
dynamic_param_applications: <empty>"#]],
218218
);
219219
}
220+
221+
#[test]
222+
fn check_rca_for_function_lambda_using_array_slicing_with_explicit_return() {
223+
let mut compilation_context = CompilationContext::default();
224+
compilation_context.update(
225+
r#"
226+
let lambda = (a, n) -> { return a[...n - 1] };
227+
lambda([1, 2, 3], 2)
228+
"#,
229+
);
230+
let package_store_compute_properties = compilation_context.get_compute_properties();
231+
check_last_statement_compute_properties(
232+
package_store_compute_properties,
233+
&expect![[r#"
234+
ApplicationsGeneratorSet:
235+
inherent: Classical
236+
dynamic_param_applications: <empty>"#]],
237+
);
238+
}
239+
240+
#[test]
241+
fn check_rca_for_function_lambda_using_array_slicing_with_implicit_return() {
242+
let mut compilation_context = CompilationContext::default();
243+
compilation_context.update(
244+
r#"
245+
let lambda = (a, n) -> { a[...n - 1] };
246+
lambda([1, 2, 3], 2)
247+
"#,
248+
);
249+
let package_store_compute_properties = compilation_context.get_compute_properties();
250+
check_last_statement_compute_properties(
251+
package_store_compute_properties,
252+
&expect![[r#"
253+
ApplicationsGeneratorSet:
254+
inherent: Classical
255+
dynamic_param_applications: <empty>"#]],
256+
);
257+
}

0 commit comments

Comments
 (0)