Skip to content

Commit f3cfe33

Browse files
authored
Rollup merge of rust-lang#97206 - jackh726:issue-73154, r=nikomatsakis
Do leak check after function pointer coercion cc rust-lang#73154 I still need to clean diagnostics just a tad, but figured I would put this up anyways. This change is made in order to make match arm coercion order-independent. Basically, any time we do function pointer coercion, we follow it by doing a leak check. This is necessary because the LUB code doesn't handler higher-ranked things correctly, leading us to "coerce", but use the wrong type. A proper fix is to actually fix that code (so the type returned by `unify_and` is a supertype of both `a` and `b` if `Ok`). However, that requires a more in-depth fix, likely heavily overlapping with the new subtyping changes. Here, I've been conservative and error early if we generate unsatisfiable constraints. Note, this should *mostly* only affect NLL, since migrate mode falls back to the LUB implementation (followed by leak check), whereas NLL only does sub. There could be other coercion code that has an order-dependence where a leak check in the coercion code might be useful. However, this is more of a spot-fix for rust-lang#73154 than a "permanent" fix, since we likely want to go the other way long-term, and allow this pattern without error. r? `@nikomatsakis`
2 parents d7b32df + 719ef0d commit f3cfe33

29 files changed

+268
-228
lines changed

compiler/rustc_infer/src/infer/error_reporting/mod.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -1442,6 +1442,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
14421442
/// the message in `secondary_span` as the primary label, and apply the message that would
14431443
/// otherwise be used for the primary label on the `secondary_span` `Span`. This applies on
14441444
/// E0271, like `src/test/ui/issues/issue-39970.stderr`.
1445+
#[tracing::instrument(
1446+
level = "debug",
1447+
skip(self, diag, secondary_span, swap_secondary_and_primary, force_label)
1448+
)]
14451449
pub fn note_type_err(
14461450
&self,
14471451
diag: &mut Diagnostic,
@@ -1453,7 +1457,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
14531457
force_label: bool,
14541458
) {
14551459
let span = cause.span(self.tcx);
1456-
debug!("note_type_err cause={:?} values={:?}, terr={:?}", cause, values, terr);
14571460

14581461
// For some types of errors, expected-found does not make
14591462
// sense, so just ignore the values we were given.
@@ -1621,9 +1624,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
16211624
}
16221625
};
16231626

1624-
// Ignore msg for object safe coercion
1625-
// since E0038 message will be printed
16261627
match terr {
1628+
// Ignore msg for object safe coercion
1629+
// since E0038 message will be printed
16271630
TypeError::ObjectUnsafeCoercion(_) => {}
16281631
_ => {
16291632
let mut label_or_note = |span: Span, msg: &str| {
@@ -1774,6 +1777,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
17741777
// It reads better to have the error origin as the final
17751778
// thing.
17761779
self.note_error_origin(diag, cause, exp_found, terr);
1780+
1781+
debug!(?diag);
17771782
}
17781783

17791784
fn suggest_tuple_pattern(

compiler/rustc_middle/src/ty/error.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,10 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
135135
ArgCount => write!(f, "incorrect number of function parameters"),
136136
FieldMisMatch(adt, field) => write!(f, "field type mismatch: {}.{}", adt, field),
137137
RegionsDoesNotOutlive(..) => write!(f, "lifetime mismatch"),
138-
RegionsInsufficientlyPolymorphic(br, _) => write!(
139-
f,
140-
"expected bound lifetime parameter{}, found concrete lifetime",
141-
br_string(br)
142-
),
138+
// Actually naming the region here is a bit confusing because context is lacking
139+
RegionsInsufficientlyPolymorphic(..) => {
140+
write!(f, "one type is more general than the other")
141+
}
143142
RegionsOverlyPolymorphic(br, _) => write!(
144143
f,
145144
"expected concrete lifetime, found bound lifetime parameter{}",

compiler/rustc_mir_build/src/build/matches/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
151151
///
152152
/// * From each pre-binding block to the next pre-binding block.
153153
/// * From each otherwise block to the next pre-binding block.
154+
#[tracing::instrument(level = "debug", skip(self, arms))]
154155
crate fn match_expr(
155156
&mut self,
156157
destination: Place<'tcx>,

compiler/rustc_mir_build/src/thir/cx/block.rs

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ impl<'tcx> Cx<'tcx> {
7474
};
7575

7676
let mut pattern = self.pattern_from_hir(local.pat);
77+
debug!(?pattern);
7778

7879
if let Some(ty) = &local.ty {
7980
if let Some(&user_ty) =

compiler/rustc_mir_build/src/thir/cx/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ impl<'tcx> Cx<'tcx> {
9696
}
9797
}
9898

99+
#[tracing::instrument(level = "debug", skip(self))]
99100
crate fn pattern_from_hir(&mut self, p: &hir::Pat<'_>) -> Pat<'tcx> {
100101
let p = match self.tcx.hir().get(p.hir_id) {
101102
Node::Pat(p) | Node::Binding(p) => p,

compiler/rustc_typeck/src/check/_match.rs

+41-32
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
5656
let mut all_arms_diverge = Diverges::WarnedAlways;
5757

5858
let expected = orig_expected.adjust_for_branches(self);
59+
debug!(?expected);
5960

6061
let mut coercion = {
6162
let coerce_first = match expected {
@@ -127,6 +128,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
127128
Some(&arm.body),
128129
arm_ty,
129130
Some(&mut |err: &mut Diagnostic| {
131+
let Some(ret) = self.ret_type_span else {
132+
return;
133+
};
134+
let Expectation::IsLast(stmt) = orig_expected else {
135+
return
136+
};
130137
let can_coerce_to_return_ty = match self.ret_coercion.as_ref() {
131138
Some(ret_coercion) if self.in_tail_expr => {
132139
let ret_ty = ret_coercion.borrow().expected_ty();
@@ -138,38 +145,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
138145
}
139146
_ => false,
140147
};
141-
if let (Expectation::IsLast(stmt), Some(ret), true) =
142-
(orig_expected, self.ret_type_span, can_coerce_to_return_ty)
143-
{
144-
let semi_span = expr.span.shrink_to_hi().with_hi(stmt.hi());
145-
let mut ret_span: MultiSpan = semi_span.into();
146-
ret_span.push_span_label(
147-
expr.span,
148-
"this could be implicitly returned but it is a statement, not a \
149-
tail expression"
150-
.to_owned(),
151-
);
152-
ret_span.push_span_label(
153-
ret,
154-
"the `match` arms can conform to this return type".to_owned(),
155-
);
156-
ret_span.push_span_label(
157-
semi_span,
158-
"the `match` is a statement because of this semicolon, consider \
159-
removing it"
160-
.to_owned(),
161-
);
162-
err.span_note(
163-
ret_span,
164-
"you might have meant to return the `match` expression",
165-
);
166-
err.tool_only_span_suggestion(
167-
semi_span,
168-
"remove this semicolon",
169-
String::new(),
170-
Applicability::MaybeIncorrect,
171-
);
148+
if !can_coerce_to_return_ty {
149+
return;
172150
}
151+
152+
let semi_span = expr.span.shrink_to_hi().with_hi(stmt.hi());
153+
let mut ret_span: MultiSpan = semi_span.into();
154+
ret_span.push_span_label(
155+
expr.span,
156+
"this could be implicitly returned but it is a statement, not a \
157+
tail expression"
158+
.to_owned(),
159+
);
160+
ret_span.push_span_label(
161+
ret,
162+
"the `match` arms can conform to this return type".to_owned(),
163+
);
164+
ret_span.push_span_label(
165+
semi_span,
166+
"the `match` is a statement because of this semicolon, consider \
167+
removing it"
168+
.to_owned(),
169+
);
170+
err.span_note(
171+
ret_span,
172+
"you might have meant to return the `match` expression",
173+
);
174+
err.tool_only_span_suggestion(
175+
semi_span,
176+
"remove this semicolon",
177+
String::new(),
178+
Applicability::MaybeIncorrect,
179+
);
173180
}),
174181
false,
175182
);
@@ -199,7 +206,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
199206
// We won't diverge unless the scrutinee or all arms diverge.
200207
self.diverges.set(scrut_diverges | all_arms_diverge);
201208

202-
coercion.complete(self)
209+
let match_ty = coercion.complete(self);
210+
debug!(?match_ty);
211+
match_ty
203212
}
204213

205214
fn get_appropriate_arm_semicolon_removal_span(

compiler/rustc_typeck/src/check/coercion.rs

+32-15
Original file line numberDiff line numberDiff line change
@@ -737,14 +737,27 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
737737
F: FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>,
738738
G: FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>,
739739
{
740-
if let ty::FnPtr(fn_ty_b) = b.kind()
741-
&& let (hir::Unsafety::Normal, hir::Unsafety::Unsafe) =
742-
(fn_ty_a.unsafety(), fn_ty_b.unsafety())
743-
{
744-
let unsafe_a = self.tcx.safe_to_unsafe_fn_ty(fn_ty_a);
745-
return self.unify_and(unsafe_a, b, to_unsafe);
746-
}
747-
self.unify_and(a, b, normal)
740+
self.commit_unconditionally(|snapshot| {
741+
let result = if let ty::FnPtr(fn_ty_b) = b.kind()
742+
&& let (hir::Unsafety::Normal, hir::Unsafety::Unsafe) =
743+
(fn_ty_a.unsafety(), fn_ty_b.unsafety())
744+
{
745+
let unsafe_a = self.tcx.safe_to_unsafe_fn_ty(fn_ty_a);
746+
self.unify_and(unsafe_a, b, to_unsafe)
747+
} else {
748+
self.unify_and(a, b, normal)
749+
};
750+
751+
// FIXME(#73154): This is a hack. Currently LUB can generate
752+
// unsolvable constraints. Additionally, it returns `a`
753+
// unconditionally, even when the "LUB" is `b`. In the future, we
754+
// want the coerced type to be the actual supertype of these two,
755+
// but for now, we want to just error to ensure we don't lock
756+
// ourselves into a specific behavior with NLL.
757+
self.leak_check(false, snapshot)?;
758+
759+
result
760+
})
748761
}
749762

750763
fn coerce_from_fn_pointer(
@@ -1133,8 +1146,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11331146
let (adjustments, target) = self.register_infer_ok_obligations(ok);
11341147
self.apply_adjustments(new, adjustments);
11351148
debug!(
1136-
"coercion::try_find_coercion_lub: was able to coerce from previous type {:?} to new type {:?}",
1137-
prev_ty, new_ty,
1149+
"coercion::try_find_coercion_lub: was able to coerce from new type {:?} to previous type {:?} ({:?})",
1150+
new_ty, prev_ty, target
11381151
);
11391152
return Ok(target);
11401153
}
@@ -1190,15 +1203,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11901203
}
11911204
}
11921205
Ok(ok) => {
1193-
debug!(
1194-
"coercion::try_find_coercion_lub: was able to coerce previous type {:?} to new type {:?}",
1195-
prev_ty, new_ty,
1196-
);
11971206
let (adjustments, target) = self.register_infer_ok_obligations(ok);
11981207
for expr in exprs {
11991208
let expr = expr.as_coercion_site();
12001209
self.apply_adjustments(expr, adjustments.clone());
12011210
}
1211+
debug!(
1212+
"coercion::try_find_coercion_lub: was able to coerce previous type {:?} to new type {:?} ({:?})",
1213+
prev_ty, new_ty, target
1214+
);
12021215
Ok(target)
12031216
}
12041217
}
@@ -1430,6 +1443,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
14301443
})
14311444
};
14321445

1446+
debug!(?result);
14331447
match result {
14341448
Ok(v) => {
14351449
self.final_ty = Some(v);
@@ -1520,7 +1534,10 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
15201534
augment_error(&mut err);
15211535
}
15221536

1523-
if let Some(expr) = expression {
1537+
let is_insufficiently_polymorphic =
1538+
matches!(coercion_error, TypeError::RegionsInsufficientlyPolymorphic(..));
1539+
1540+
if !is_insufficiently_polymorphic && let Some(expr) = expression {
15241541
fcx.emit_coerce_suggestions(
15251542
&mut err,
15261543
expr,

compiler/rustc_typeck/src/check/demand.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
129129
///
130130
/// N.B., this code relies on `self.diverges` to be accurate. In particular, assignments to `!`
131131
/// will be permitted if the diverges flag is currently "always".
132+
#[tracing::instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))]
132133
pub fn demand_coerce_diag(
133134
&self,
134135
expr: &hir::Expr<'tcx>,
@@ -150,7 +151,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
150151
let expr_ty = self.resolve_vars_with_obligations(checked_ty);
151152
let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e.clone());
152153

153-
self.emit_coerce_suggestions(&mut err, expr, expr_ty, expected, expected_ty_expr, Some(e));
154+
let is_insufficiently_polymorphic =
155+
matches!(e, TypeError::RegionsInsufficientlyPolymorphic(..));
156+
157+
// FIXME(#73154): For now, we do leak check when coercing function
158+
// pointers in typeck, instead of only during borrowck. This can lead
159+
// to these `RegionsInsufficientlyPolymorphic` errors that aren't helpful.
160+
if !is_insufficiently_polymorphic {
161+
self.emit_coerce_suggestions(
162+
&mut err,
163+
expr,
164+
expr_ty,
165+
expected,
166+
expected_ty_expr,
167+
Some(e),
168+
);
169+
}
154170

155171
(expected, Some(err))
156172
}

src/test/ui/hrtb/hrtb-exists-forall-fn.nll.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error[E0308]: mismatched types
2-
--> $DIR/hrtb-exists-forall-fn.rs:17:12
2+
--> $DIR/hrtb-exists-forall-fn.rs:17:34
33
|
44
LL | let _: for<'b> fn(&'b u32) = foo();
5-
| ^^^^^^^^^^^^^^^^^^^ one type is more general than the other
5+
| ^^^^^ one type is more general than the other
66
|
77
= note: expected fn pointer `for<'b> fn(&'b u32)`
88
found fn pointer `fn(&u32)`

src/test/ui/lub-glb/old-lub-glb-hr-noteq1.stderr src/test/ui/lub-glb/old-lub-glb-hr-noteq1.baseleak.stderr

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
error[E0308]: `match` arms have incompatible types
2-
--> $DIR/old-lub-glb-hr-noteq1.rs:11:14
2+
--> $DIR/old-lub-glb-hr-noteq1.rs:17:14
33
|
44
LL | let z = match 22 {
55
| _____________-
66
LL | | 0 => x,
77
| | - this is found to be of type `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`
88
LL | | _ => y,
99
| | ^ one type is more general than the other
10+
LL | |
11+
... |
12+
LL | |
1013
LL | | };
1114
| |_____- `match` arms have incompatible types
1215
|
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0308]: `match` arms have incompatible types
2+
--> $DIR/old-lub-glb-hr-noteq1.rs:17:14
3+
|
4+
LL | let z = match 22 {
5+
| _____________-
6+
LL | | 0 => x,
7+
| | - this is found to be of type `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`
8+
LL | | _ => y,
9+
| | ^ one type is more general than the other
10+
LL | |
11+
... |
12+
LL | |
13+
LL | | };
14+
| |_____- `match` arms have incompatible types
15+
|
16+
= note: expected fn pointer `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`
17+
found fn pointer `for<'a> fn(&'a u8, &'a u8) -> &'a u8`
18+
19+
error: aborting due to previous error
20+
21+
For more information about this error, try `rustc --explain E0308`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0308]: `match` arms have incompatible types
2+
--> $DIR/old-lub-glb-hr-noteq1.rs:17:14
3+
|
4+
LL | let z = match 22 {
5+
| _____________-
6+
LL | | 0 => x,
7+
| | - this is found to be of type `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`
8+
LL | | _ => y,
9+
| | ^ one type is more general than the other
10+
LL | |
11+
... |
12+
LL | |
13+
LL | | };
14+
| |_____- `match` arms have incompatible types
15+
|
16+
= note: expected fn pointer `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`
17+
found fn pointer `for<'a> fn(&'a u8, &'a u8) -> &'a u8`
18+
19+
error: aborting due to previous error
20+
21+
For more information about this error, try `rustc --explain E0308`.

src/test/ui/lub-glb/old-lub-glb-hr-noteq1.nll.stderr src/test/ui/lub-glb/old-lub-glb-hr-noteq1.nllnoleak.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0308]: mismatched types
2-
--> $DIR/old-lub-glb-hr-noteq1.rs:11:14
2+
--> $DIR/old-lub-glb-hr-noteq1.rs:17:14
33
|
44
LL | _ => y,
55
| ^ one type is more general than the other

0 commit comments

Comments
 (0)