Skip to content

Commit 1cf3dc6

Browse files
authored
Rollup merge of rust-lang#147840 - jdonszelmann:unsizing-coercions, r=lcnr
Rework unsizing coercions in the new solver Replaces rust-lang#141926, contains: - a commit adding tests that fail before this work - the two commits from the previous PR - a commit in which these tests are fixed - finally, a fixup for an in my opinion rather large regression in diagnostics. It's still not perfect, but better? I hope this is roughly what you had in mind Fixes rust-lang/trait-system-refactor-initiative#241 and rust-lang/trait-system-refactor-initiative#238, adding tests for both r? ````@lcnr````
2 parents ef8003b + 1756622 commit 1cf3dc6

19 files changed

+294
-34
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

Lines changed: 107 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@
3535
//! // and are then unable to coerce `&7i32` to `&mut i32`.
3636
//! ```
3737
38-
use std::ops::Deref;
38+
use std::ops::{ControlFlow, Deref};
3939

4040
use rustc_errors::codes::*;
4141
use rustc_errors::{Applicability, Diag, struct_span_code_err};
42-
use rustc_hir as hir;
4342
use rustc_hir::attrs::InlineAttr;
4443
use rustc_hir::def_id::{DefId, LocalDefId};
44+
use rustc_hir::{self as hir, LangItem};
4545
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
4646
use rustc_infer::infer::relate::RelateResult;
4747
use rustc_infer::infer::{DefineOpaqueTypes, InferOk, InferResult, RegionVariableOrigin};
@@ -56,6 +56,8 @@ use rustc_middle::ty::error::TypeError;
5656
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
5757
use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Span};
5858
use rustc_trait_selection::infer::InferCtxtExt as _;
59+
use rustc_trait_selection::solve::inspect::{self, InferCtxtProofTreeExt, ProofTreeVisitor};
60+
use rustc_trait_selection::solve::{Certainty, Goal, NoSolution};
5961
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
6062
use rustc_trait_selection::traits::{
6163
self, ImplSource, NormalizeExt, ObligationCause, ObligationCauseCode, ObligationCtxt,
@@ -639,24 +641,52 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
639641
Adjust::Pointer(PointerCoercion::Unsize),
640642
)?;
641643

642-
let mut selcx = traits::SelectionContext::new(self);
643-
644644
// Create an obligation for `Source: CoerceUnsized<Target>`.
645645
let cause = self.cause(self.cause.span, ObligationCauseCode::Coercion { source, target });
646+
let pred = ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target]);
647+
let obligation = Obligation::new(self.tcx, cause, self.fcx.param_env, pred);
648+
649+
if self.next_trait_solver() {
650+
coercion.obligations.push(obligation);
651+
652+
if self
653+
.infcx
654+
.visit_proof_tree(
655+
Goal::new(self.tcx, self.param_env, pred),
656+
&mut CoerceVisitor { fcx: self.fcx, span: self.cause.span },
657+
)
658+
.is_break()
659+
{
660+
return Err(TypeError::Mismatch);
661+
}
662+
} else {
663+
self.coerce_unsized_old_solver(
664+
obligation,
665+
&mut coercion,
666+
coerce_unsized_did,
667+
unsize_did,
668+
)?;
669+
}
646670

671+
Ok(coercion)
672+
}
673+
674+
fn coerce_unsized_old_solver(
675+
&self,
676+
obligation: Obligation<'tcx, ty::Predicate<'tcx>>,
677+
coercion: &mut InferOk<'tcx, (Vec<Adjustment<'tcx>>, Ty<'tcx>)>,
678+
coerce_unsized_did: DefId,
679+
unsize_did: DefId,
680+
) -> Result<(), TypeError<'tcx>> {
681+
let mut selcx = traits::SelectionContext::new(self);
647682
// Use a FIFO queue for this custom fulfillment procedure.
648683
//
649684
// A Vec (or SmallVec) is not a natural choice for a queue. However,
650685
// this code path is hot, and this queue usually has a max length of 1
651686
// and almost never more than 3. By using a SmallVec we avoid an
652687
// allocation, at the (very small) cost of (occasionally) having to
653688
// shift subsequent elements down when removing the front element.
654-
let mut queue: SmallVec<[PredicateObligation<'tcx>; 4]> = smallvec![Obligation::new(
655-
self.tcx,
656-
cause,
657-
self.fcx.param_env,
658-
ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target])
659-
)];
689+
let mut queue: SmallVec<[PredicateObligation<'tcx>; 4]> = smallvec![obligation];
660690

661691
// Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid
662692
// emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where
@@ -749,7 +779,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
749779
// with the unsizing - the lack of a coercion should
750780
// be silent, as it causes a type mismatch later.
751781
}
752-
753782
Ok(Some(ImplSource::UserDefined(impl_source))) => {
754783
queue.extend(impl_source.nested);
755784
// Certain incoherent `CoerceUnsized` implementations may cause ICEs,
@@ -767,7 +796,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
767796
}
768797
}
769798

770-
Ok(coercion)
799+
Ok(())
771800
}
772801

773802
/// Applies reborrowing for `Pin`
@@ -2005,3 +2034,69 @@ impl AsCoercionSite for hir::Arm<'_> {
20052034
self.body
20062035
}
20072036
}
2037+
2038+
/// Recursively visit goals to decide whether an unsizing is possible.
2039+
/// `Break`s when it isn't, and an error should be raised.
2040+
/// `Continue`s when an unsizing ok based on an implementation of the `Unsize` trait / lang item.
2041+
struct CoerceVisitor<'a, 'tcx> {
2042+
fcx: &'a FnCtxt<'a, 'tcx>,
2043+
span: Span,
2044+
}
2045+
2046+
impl<'tcx> ProofTreeVisitor<'tcx> for CoerceVisitor<'_, 'tcx> {
2047+
type Result = ControlFlow<()>;
2048+
2049+
fn span(&self) -> Span {
2050+
self.span
2051+
}
2052+
2053+
fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
2054+
let Some(pred) = goal.goal().predicate.as_trait_clause() else {
2055+
return ControlFlow::Continue(());
2056+
};
2057+
2058+
// Make sure this predicate is referring to either an `Unsize` or `CoerceUnsized` trait,
2059+
// Otherwise there's nothing to do.
2060+
if !self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::Unsize)
2061+
&& !self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::CoerceUnsized)
2062+
{
2063+
return ControlFlow::Continue(());
2064+
}
2065+
2066+
match goal.result() {
2067+
// If we prove the `Unsize` or `CoerceUnsized` goal, continue recursing.
2068+
Ok(Certainty::Yes) => ControlFlow::Continue(()),
2069+
Err(NoSolution) => {
2070+
// Even if we find no solution, continue recursing if we find a single candidate
2071+
// for which we're shallowly certain it holds to get the right error source.
2072+
if let [only_candidate] = &goal.candidates()[..]
2073+
&& only_candidate.shallow_certainty() == Certainty::Yes
2074+
{
2075+
only_candidate.visit_nested_no_probe(self)
2076+
} else {
2077+
ControlFlow::Break(())
2078+
}
2079+
}
2080+
Ok(Certainty::Maybe { .. }) => {
2081+
// FIXME: structurally normalize?
2082+
if self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::Unsize)
2083+
&& let ty::Dynamic(..) = pred.skip_binder().trait_ref.args.type_at(1).kind()
2084+
&& let ty::Infer(ty::TyVar(vid)) = *pred.self_ty().skip_binder().kind()
2085+
&& self.fcx.type_var_is_sized(vid)
2086+
{
2087+
// We get here when trying to unsize a type variable to a `dyn Trait`,
2088+
// knowing that that variable is sized. Unsizing definitely has to happen in that case.
2089+
// If the variable weren't sized, we may not need an unsizing coercion.
2090+
// In general, we don't want to add coercions too eagerly since it makes error messages much worse.
2091+
ControlFlow::Continue(())
2092+
} else if let Some(cand) = goal.unique_applicable_candidate()
2093+
&& cand.shallow_certainty() == Certainty::Yes
2094+
{
2095+
cand.visit_nested_no_probe(self)
2096+
} else {
2097+
ControlFlow::Break(())
2098+
}
2099+
}
2100+
}
2101+
}
2102+
}

compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_middle::ty::{self, Ty, TypeVisitableExt};
55
use rustc_span::Span;
66
use rustc_trait_selection::solve::Certainty;
77
use rustc_trait_selection::solve::inspect::{
8-
InspectConfig, InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor,
8+
InferCtxtProofTreeExt, InspectConfig, InspectGoal, ProofTreeVisitor,
99
};
1010
use tracing::{debug, instrument, trace};
1111

compiler/rustc_trait_selection/src/solve/fulfill.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use tracing::instrument;
2424
use self::derive_errors::*;
2525
use super::Certainty;
2626
use super::delegate::SolverDelegate;
27-
use super::inspect::{self, ProofTreeInferCtxtExt};
27+
use super::inspect::{self, InferCtxtProofTreeExt};
2828
use crate::traits::{FulfillmentError, ScrubbedTraitError};
2929

3030
mod derive_errors;

compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rustc_next_trait_solver::solve::{GoalEvaluation, SolverDelegateEvalExt as _}
1515
use tracing::{instrument, trace};
1616

1717
use crate::solve::delegate::SolverDelegate;
18-
use crate::solve::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor};
18+
use crate::solve::inspect::{self, InferCtxtProofTreeExt, ProofTreeVisitor};
1919
use crate::solve::{Certainty, deeply_normalize_for_diagnostics};
2020
use crate::traits::{FulfillmentError, FulfillmentErrorCode, wf};
2121

compiler/rustc_trait_selection/src/solve/inspect/analyse.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ pub trait ProofTreeVisitor<'tcx> {
463463
fn visit_goal(&mut self, goal: &InspectGoal<'_, 'tcx>) -> Self::Result;
464464
}
465465

466-
#[extension(pub trait ProofTreeInferCtxtExt<'tcx>)]
466+
#[extension(pub trait InferCtxtProofTreeExt<'tcx>)]
467467
impl<'tcx> InferCtxt<'tcx> {
468468
fn visit_proof_tree<V: ProofTreeVisitor<'tcx>>(
469469
&self,

compiler/rustc_trait_selection/src/solve/select.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_middle::{bug, span_bug};
1212
use rustc_span::Span;
1313
use thin_vec::thin_vec;
1414

15-
use crate::solve::inspect::{self, ProofTreeInferCtxtExt};
15+
use crate::solve::inspect::{self, InferCtxtProofTreeExt};
1616

1717
#[extension(pub trait InferCtxtSelectExt<'tcx>)]
1818
impl<'tcx> InferCtxt<'tcx> {

compiler/rustc_trait_selection/src/traits/coherence.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use tracing::{debug, instrument, warn};
2929
use super::ObligationCtxt;
3030
use crate::error_reporting::traits::suggest_new_overflow_limit;
3131
use crate::infer::InferOk;
32-
use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor};
32+
use crate::solve::inspect::{InferCtxtProofTreeExt, InspectGoal, ProofTreeVisitor};
3333
use crate::solve::{SolverDelegate, deeply_normalize_for_diagnostics, inspect};
3434
use crate::traits::query::evaluate_obligation::InferCtxtExt;
3535
use crate::traits::select::IntercrateAmbiguityCause;

tests/ui/dst/dst-object-from-unsized-type.stderr renamed to tests/ui/dst/dst-object-from-unsized-type.current.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0277]: the size for values of type `T` cannot be known at compilation time
2-
--> $DIR/dst-object-from-unsized-type.rs:8:23
2+
--> $DIR/dst-object-from-unsized-type.rs:11:23
33
|
44
LL | fn test1<T: ?Sized + Foo>(t: &T) {
55
| - this type parameter needs to be `Sized`
@@ -14,7 +14,7 @@ LL + fn test1<T: Foo>(t: &T) {
1414
|
1515

1616
error[E0277]: the size for values of type `T` cannot be known at compilation time
17-
--> $DIR/dst-object-from-unsized-type.rs:13:23
17+
--> $DIR/dst-object-from-unsized-type.rs:17:23
1818
|
1919
LL | fn test2<T: ?Sized + Foo>(t: &T) {
2020
| - this type parameter needs to be `Sized`
@@ -29,7 +29,7 @@ LL + fn test2<T: Foo>(t: &T) {
2929
|
3030

3131
error[E0277]: the size for values of type `str` cannot be known at compilation time
32-
--> $DIR/dst-object-from-unsized-type.rs:18:28
32+
--> $DIR/dst-object-from-unsized-type.rs:23:28
3333
|
3434
LL | let _: &[&dyn Foo] = &["hi"];
3535
| ^^^^ doesn't have a size known at compile-time
@@ -38,7 +38,7 @@ LL | let _: &[&dyn Foo] = &["hi"];
3838
= note: required for the cast from `&'static str` to `&dyn Foo`
3939

4040
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
41-
--> $DIR/dst-object-from-unsized-type.rs:23:23
41+
--> $DIR/dst-object-from-unsized-type.rs:29:23
4242
|
4343
LL | let _: &dyn Foo = x as &dyn Foo;
4444
| ^ doesn't have a size known at compile-time
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
error[E0277]: the trait bound `&T: CoerceUnsized<&dyn Foo>` is not satisfied in `T`
2+
--> $DIR/dst-object-from-unsized-type.rs:11:23
3+
|
4+
LL | fn test1<T: ?Sized + Foo>(t: &T) {
5+
| - this type parameter needs to be `Sized`
6+
LL | let u: &dyn Foo = t;
7+
| ^ within `T`, the trait `Sized` is not implemented for `T`
8+
|
9+
= note: required because it appears within the type `T`
10+
= note: required for `&T` to implement `CoerceUnsized<&dyn Foo>`
11+
= note: required for the cast from `&T` to `&dyn Foo`
12+
help: consider removing the `?Sized` bound to make the type parameter `Sized`
13+
|
14+
LL - fn test1<T: ?Sized + Foo>(t: &T) {
15+
LL + fn test1<T: Foo>(t: &T) {
16+
|
17+
18+
error[E0277]: the trait bound `&T: CoerceUnsized<&dyn Foo>` is not satisfied in `T`
19+
--> $DIR/dst-object-from-unsized-type.rs:17:23
20+
|
21+
LL | fn test2<T: ?Sized + Foo>(t: &T) {
22+
| - this type parameter needs to be `Sized`
23+
LL | let v: &dyn Foo = t as &dyn Foo;
24+
| ^ within `T`, the trait `Sized` is not implemented for `T`
25+
|
26+
= note: required because it appears within the type `T`
27+
= note: required for `&T` to implement `CoerceUnsized<&dyn Foo>`
28+
= note: required for the cast from `&T` to `&dyn Foo`
29+
help: consider removing the `?Sized` bound to make the type parameter `Sized`
30+
|
31+
LL - fn test2<T: ?Sized + Foo>(t: &T) {
32+
LL + fn test2<T: Foo>(t: &T) {
33+
|
34+
35+
error[E0277]: the trait bound `&str: CoerceUnsized<&dyn Foo>` is not satisfied in `str`
36+
--> $DIR/dst-object-from-unsized-type.rs:23:28
37+
|
38+
LL | let _: &[&dyn Foo] = &["hi"];
39+
| ^^^^ within `str`, the trait `Sized` is not implemented for `str`
40+
|
41+
= note: `str` is considered to contain a `[u8]` slice for auto trait purposes
42+
= note: required for `&str` to implement `CoerceUnsized<&dyn Foo>`
43+
= note: required for the cast from `&'static str` to `&dyn Foo`
44+
45+
error[E0277]: the trait bound `&[u8]: CoerceUnsized<&dyn Foo>` is not satisfied in `[u8]`
46+
--> $DIR/dst-object-from-unsized-type.rs:29:23
47+
|
48+
LL | let _: &dyn Foo = x as &dyn Foo;
49+
| ^ within `[u8]`, the trait `Sized` is not implemented for `[u8]`
50+
|
51+
= note: required because it appears within the type `[u8]`
52+
= note: required for `&[u8]` to implement `CoerceUnsized<&dyn Foo>`
53+
= note: required for the cast from `&[u8]` to `&dyn Foo`
54+
55+
error: aborting due to 4 previous errors
56+
57+
For more information about this error, try `rustc --explain E0277`.
Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,34 @@
11
// Test that we cannot create objects from unsized types.
2+
//@ revisions: current next
3+
//@ ignore-compare-mode-next-solver (explicit revisions)
4+
//@[next] compile-flags: -Znext-solver
25

36
trait Foo { fn foo(&self) {} }
47
impl Foo for str {}
58
impl Foo for [u8] {}
69

710
fn test1<T: ?Sized + Foo>(t: &T) {
811
let u: &dyn Foo = t;
9-
//~^ ERROR the size for values of type
12+
//[current]~^ ERROR the size for values of type
13+
//[next]~^^ ERROR the trait bound `&T: CoerceUnsized<&dyn Foo>` is not satisfied in `T`
1014
}
1115

1216
fn test2<T: ?Sized + Foo>(t: &T) {
1317
let v: &dyn Foo = t as &dyn Foo;
14-
//~^ ERROR the size for values of type
18+
//[current]~^ ERROR the size for values of type
19+
//[next]~^^ ERROR the trait bound `&T: CoerceUnsized<&dyn Foo>` is not satisfied in `T`
1520
}
1621

1722
fn test3() {
1823
let _: &[&dyn Foo] = &["hi"];
19-
//~^ ERROR the size for values of type
24+
//[current]~^ ERROR the size for values of type
25+
//[next]~^^ ERROR the trait bound `&str: CoerceUnsized<&dyn Foo>` is not satisfied in `str`
2026
}
2127

2228
fn test4(x: &[u8]) {
2329
let _: &dyn Foo = x as &dyn Foo;
24-
//~^ ERROR the size for values of type
30+
//[current]~^ ERROR the size for values of type
31+
//[next]~^^ ERROR the trait bound `&[u8]: CoerceUnsized<&dyn Foo>` is not satisfied in `[u8]`
2532
}
2633

2734
fn main() { }

0 commit comments

Comments
 (0)