Skip to content

Commit 4400d8f

Browse files
committed
Auto merge of rust-lang#110204 - compiler-errors:new-solver-hir-typeck-hacks, r=lcnr
Deal with unnormalized projections when structurally resolving types with new solver 1. Normalize types in `structurally_resolved_type` when the new solver is enabled 2. Normalize built-in autoderef targets in `Autoderef` when the new solver is enabled 3. Normalize-erasing-regions in `resolve_type` in writeback This is motivated by the UI test provided, which currently fails with: ``` error[E0609]: no field `x` on type `<usize as SliceIndex<[Foo]>>::Output` --> <source>:9:11 | 9 | xs[0].x = 1; | ^ ``` I'm pretty happy with the approach in (1.) and (2.) and think we'll inevitably need something like this in the long-term, but (3.) seems like a hack to me. It's a *lot* of work to add tons of new calls to every user of these typeck results though (mir build, late lints, etc). Happy to discuss further. r? `@lcnr`
2 parents 8b4b208 + 4cfafb2 commit 4400d8f

File tree

7 files changed

+196
-40
lines changed

7 files changed

+196
-40
lines changed

compiler/rustc_hir_analysis/src/autoderef.rs

+54-22
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use crate::errors::AutoDerefReachedRecursionLimit;
22
use crate::traits::query::evaluate_obligation::InferCtxtExt;
3-
use crate::traits::NormalizeExt;
43
use crate::traits::{self, TraitEngine, TraitEngineExt};
54
use rustc_infer::infer::InferCtxt;
65
use rustc_middle::ty::TypeVisitableExt;
@@ -9,6 +8,7 @@ use rustc_session::Limit;
98
use rustc_span::def_id::LocalDefId;
109
use rustc_span::def_id::LOCAL_CRATE;
1110
use rustc_span::Span;
11+
use rustc_trait_selection::traits::StructurallyNormalizeExt;
1212

1313
#[derive(Copy, Clone, Debug)]
1414
pub enum AutoderefKind {
@@ -66,14 +66,27 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> {
6666
}
6767

6868
// Otherwise, deref if type is derefable:
69-
let (kind, new_ty) =
70-
if let Some(mt) = self.state.cur_ty.builtin_deref(self.include_raw_pointers) {
71-
(AutoderefKind::Builtin, mt.ty)
72-
} else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) {
73-
(AutoderefKind::Overloaded, ty)
69+
let (kind, new_ty) = if let Some(ty::TypeAndMut { ty, .. }) =
70+
self.state.cur_ty.builtin_deref(self.include_raw_pointers)
71+
{
72+
debug_assert_eq!(ty, self.infcx.resolve_vars_if_possible(ty));
73+
// NOTE: we may still need to normalize the built-in deref in case
74+
// we have some type like `&<Ty as Trait>::Assoc`, since users of
75+
// autoderef expect this type to have been structurally normalized.
76+
if self.infcx.tcx.trait_solver_next()
77+
&& let ty::Alias(ty::Projection, _) = ty.kind()
78+
{
79+
let (normalized_ty, obligations) = self.structurally_normalize(ty)?;
80+
self.state.obligations.extend(obligations);
81+
(AutoderefKind::Builtin, normalized_ty)
7482
} else {
75-
return None;
76-
};
83+
(AutoderefKind::Builtin, ty)
84+
}
85+
} else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) {
86+
(AutoderefKind::Overloaded, ty)
87+
} else {
88+
return None;
89+
};
7790

7891
if new_ty.references_error() {
7992
return None;
@@ -119,14 +132,11 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
119132

120133
fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
121134
debug!("overloaded_deref_ty({:?})", ty);
122-
123135
let tcx = self.infcx.tcx;
124136

125137
// <ty as Deref>
126138
let trait_ref = ty::TraitRef::new(tcx, tcx.lang_items().deref_trait()?, [ty]);
127-
128139
let cause = traits::ObligationCause::misc(self.span, self.body_id);
129-
130140
let obligation = traits::Obligation::new(
131141
tcx,
132142
cause.clone(),
@@ -138,26 +148,48 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
138148
return None;
139149
}
140150

141-
let normalized_ty = self
151+
let (normalized_ty, obligations) =
152+
self.structurally_normalize(tcx.mk_projection(tcx.lang_items().deref_target()?, [ty]))?;
153+
debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations);
154+
self.state.obligations.extend(obligations);
155+
156+
Some(self.infcx.resolve_vars_if_possible(normalized_ty))
157+
}
158+
159+
#[instrument(level = "debug", skip(self), ret)]
160+
pub fn structurally_normalize(
161+
&self,
162+
ty: Ty<'tcx>,
163+
) -> Option<(Ty<'tcx>, Vec<traits::PredicateObligation<'tcx>>)> {
164+
let tcx = self.infcx.tcx;
165+
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new_in_snapshot(tcx);
166+
167+
let cause = traits::ObligationCause::misc(self.span, self.body_id);
168+
let normalized_ty = match self
142169
.infcx
143170
.at(&cause, self.param_env)
144-
.normalize(tcx.mk_projection(tcx.lang_items().deref_target()?, trait_ref.substs));
145-
let mut fulfillcx = <dyn TraitEngine<'tcx>>::new_in_snapshot(tcx);
146-
let normalized_ty =
147-
normalized_ty.into_value_registering_obligations(self.infcx, &mut *fulfillcx);
148-
let errors = fulfillcx.select_where_possible(&self.infcx);
171+
.structurally_normalize(ty, &mut *fulfill_cx)
172+
{
173+
Ok(normalized_ty) => normalized_ty,
174+
Err(errors) => {
175+
// This shouldn't happen, except for evaluate/fulfill mismatches,
176+
// but that's not a reason for an ICE (`predicate_may_hold` is conservative
177+
// by design).
178+
debug!(?errors, "encountered errors while fulfilling");
179+
return None;
180+
}
181+
};
182+
183+
let errors = fulfill_cx.select_where_possible(&self.infcx);
149184
if !errors.is_empty() {
150185
// This shouldn't happen, except for evaluate/fulfill mismatches,
151186
// but that's not a reason for an ICE (`predicate_may_hold` is conservative
152187
// by design).
153-
debug!("overloaded_deref_ty: encountered errors {:?} while fulfilling", errors);
188+
debug!(?errors, "encountered errors while fulfilling");
154189
return None;
155190
}
156-
let obligations = fulfillcx.pending_obligations();
157-
debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations);
158-
self.state.obligations.extend(obligations);
159191

160-
Some(self.infcx.resolve_vars_if_possible(normalized_ty))
192+
Some((normalized_ty, fulfill_cx.pending_obligations()))
161193
}
162194

163195
/// Returns the final type we ended up with, which may be an inference

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

+27-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ use rustc_span::symbol::{kw, sym, Ident};
3535
use rustc_span::Span;
3636
use rustc_target::abi::FieldIdx;
3737
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
38-
use rustc_trait_selection::traits::{self, NormalizeExt, ObligationCauseCode, ObligationCtxt};
38+
use rustc_trait_selection::traits::{
39+
self, NormalizeExt, ObligationCauseCode, ObligationCtxt, StructurallyNormalizeExt,
40+
};
3941

4042
use std::collections::hash_map::Entry;
4143
use std::slice;
@@ -1460,10 +1462,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14601462
}
14611463

14621464
/// Resolves `typ` by a single level if `typ` is a type variable.
1465+
///
1466+
/// When the new solver is enabled, this will also attempt to normalize
1467+
/// the type if it's a projection (note that it will not deeply normalize
1468+
/// projections within the type, just the outermost layer of the type).
1469+
///
14631470
/// If no resolution is possible, then an error is reported.
14641471
/// Numeric inference variables may be left unresolved.
14651472
pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
1466-
let ty = self.resolve_vars_with_obligations(ty);
1473+
let mut ty = self.resolve_vars_with_obligations(ty);
1474+
1475+
if self.tcx.trait_solver_next()
1476+
&& let ty::Alias(ty::Projection, _) = ty.kind()
1477+
{
1478+
match self
1479+
.at(&self.misc(sp), self.param_env)
1480+
.structurally_normalize(ty, &mut **self.fulfillment_cx.borrow_mut())
1481+
{
1482+
Ok(normalized_ty) => {
1483+
ty = normalized_ty;
1484+
},
1485+
Err(errors) => {
1486+
let guar = self.err_ctxt().report_fulfillment_errors(&errors);
1487+
return self.tcx.ty_error(guar);
1488+
}
1489+
}
1490+
}
1491+
14671492
if !ty.is_ty_var() {
14681493
ty
14691494
} else {

compiler/rustc_hir_typeck/src/writeback.rs

+20-16
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use rustc_errors::ErrorGuaranteed;
99
use rustc_hir as hir;
1010
use rustc_hir::intravisit::{self, Visitor};
1111
use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
12-
use rustc_infer::infer::InferCtxt;
1312
use rustc_middle::hir::place::Place as HirPlace;
1413
use rustc_middle::mir::FakeReadCause;
1514
use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast};
@@ -737,8 +736,7 @@ impl Locatable for hir::HirId {
737736
/// The Resolver. This is the type folding engine that detects
738737
/// unresolved types and so forth.
739738
struct Resolver<'cx, 'tcx> {
740-
tcx: TyCtxt<'tcx>,
741-
infcx: &'cx InferCtxt<'tcx>,
739+
fcx: &'cx FnCtxt<'cx, 'tcx>,
742740
span: &'cx dyn Locatable,
743741
body: &'tcx hir::Body<'tcx>,
744742

@@ -752,18 +750,18 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
752750
span: &'cx dyn Locatable,
753751
body: &'tcx hir::Body<'tcx>,
754752
) -> Resolver<'cx, 'tcx> {
755-
Resolver { tcx: fcx.tcx, infcx: fcx, span, body, replaced_with_error: None }
753+
Resolver { fcx, span, body, replaced_with_error: None }
756754
}
757755

758756
fn report_error(&self, p: impl Into<ty::GenericArg<'tcx>>) -> ErrorGuaranteed {
759-
match self.tcx.sess.has_errors() {
757+
match self.fcx.tcx.sess.has_errors() {
760758
Some(e) => e,
761759
None => self
762-
.infcx
760+
.fcx
763761
.err_ctxt()
764762
.emit_inference_failure_err(
765-
self.tcx.hir().body_owner_def_id(self.body.id()),
766-
self.span.to_span(self.tcx),
763+
self.fcx.tcx.hir().body_owner_def_id(self.body.id()),
764+
self.span.to_span(self.fcx.tcx),
767765
p.into(),
768766
E0282,
769767
false,
@@ -795,40 +793,46 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EraseEarlyRegions<'tcx> {
795793

796794
impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Resolver<'cx, 'tcx> {
797795
fn interner(&self) -> TyCtxt<'tcx> {
798-
self.tcx
796+
self.fcx.tcx
799797
}
800798

801799
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
802-
match self.infcx.fully_resolve(t) {
800+
match self.fcx.fully_resolve(t) {
801+
Ok(t) if self.fcx.tcx.trait_solver_next() => {
802+
// We must normalize erasing regions here, since later lints
803+
// expect that types that show up in the typeck are fully
804+
// normalized.
805+
self.fcx.tcx.try_normalize_erasing_regions(self.fcx.param_env, t).unwrap_or(t)
806+
}
803807
Ok(t) => {
804808
// Do not anonymize late-bound regions
805809
// (e.g. keep `for<'a>` named `for<'a>`).
806810
// This allows NLL to generate error messages that
807811
// refer to the higher-ranked lifetime names written by the user.
808-
EraseEarlyRegions { tcx: self.tcx }.fold_ty(t)
812+
EraseEarlyRegions { tcx: self.fcx.tcx }.fold_ty(t)
809813
}
810814
Err(_) => {
811815
debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t);
812816
let e = self.report_error(t);
813817
self.replaced_with_error = Some(e);
814-
self.interner().ty_error(e)
818+
self.fcx.tcx.ty_error(e)
815819
}
816820
}
817821
}
818822

819823
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
820824
debug_assert!(!r.is_late_bound(), "Should not be resolving bound region.");
821-
self.tcx.lifetimes.re_erased
825+
self.fcx.tcx.lifetimes.re_erased
822826
}
823827

824828
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
825-
match self.infcx.fully_resolve(ct) {
826-
Ok(ct) => self.tcx.erase_regions(ct),
829+
match self.fcx.fully_resolve(ct) {
830+
Ok(ct) => self.fcx.tcx.erase_regions(ct),
827831
Err(_) => {
828832
debug!("Resolver::fold_const: input const `{:?}` not fully resolvable", ct);
829833
let e = self.report_error(ct);
830834
self.replaced_with_error = Some(e);
831-
self.interner().const_error(ct.ty(), e)
835+
self.fcx.tcx.const_error(ct.ty(), e)
832836
}
833837
}
834838
}

compiler/rustc_trait_selection/src/traits/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub mod query;
1717
mod select;
1818
mod specialize;
1919
mod structural_match;
20+
mod structural_normalize;
2021
mod util;
2122
mod vtable;
2223
pub mod wf;
@@ -62,6 +63,7 @@ pub use self::specialize::{
6263
pub use self::structural_match::{
6364
search_for_adt_const_param_violation, search_for_structural_match_violation,
6465
};
66+
pub use self::structural_normalize::StructurallyNormalizeExt;
6567
pub use self::util::elaborate;
6668
pub use self::util::{expand_trait_aliases, TraitAliasExpander};
6769
pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use rustc_infer::infer::at::At;
2+
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
3+
use rustc_infer::traits::{FulfillmentError, TraitEngine};
4+
use rustc_middle::ty::{self, Ty};
5+
6+
use crate::traits::{query::evaluate_obligation::InferCtxtExt, NormalizeExt, Obligation};
7+
8+
pub trait StructurallyNormalizeExt<'tcx> {
9+
fn structurally_normalize(
10+
&self,
11+
ty: Ty<'tcx>,
12+
fulfill_cx: &mut dyn TraitEngine<'tcx>,
13+
) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>>;
14+
}
15+
16+
impl<'tcx> StructurallyNormalizeExt<'tcx> for At<'_, 'tcx> {
17+
fn structurally_normalize(
18+
&self,
19+
mut ty: Ty<'tcx>,
20+
fulfill_cx: &mut dyn TraitEngine<'tcx>,
21+
) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>> {
22+
assert!(!ty.is_ty_var(), "should have resolved vars before calling");
23+
24+
if self.infcx.tcx.trait_solver_next() {
25+
while let ty::Alias(ty::Projection, projection_ty) = *ty.kind() {
26+
let new_infer_ty = self.infcx.next_ty_var(TypeVariableOrigin {
27+
kind: TypeVariableOriginKind::NormalizeProjectionType,
28+
span: self.cause.span,
29+
});
30+
let obligation = Obligation::new(
31+
self.infcx.tcx,
32+
self.cause.clone(),
33+
self.param_env,
34+
ty::Binder::dummy(ty::ProjectionPredicate {
35+
projection_ty,
36+
term: new_infer_ty.into(),
37+
}),
38+
);
39+
if self.infcx.predicate_may_hold(&obligation) {
40+
fulfill_cx.register_predicate_obligation(self.infcx, obligation);
41+
let errors = fulfill_cx.select_where_possible(self.infcx);
42+
if !errors.is_empty() {
43+
return Err(errors);
44+
}
45+
ty = self.infcx.resolve_vars_if_possible(new_infer_ty);
46+
} else {
47+
break;
48+
}
49+
}
50+
Ok(ty)
51+
} else {
52+
Ok(self.normalize(ty).into_value_registering_obligations(self.infcx, fulfill_cx))
53+
}
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// compile-flags: -Ztrait-solver=next
2+
// check-pass
3+
4+
// Verify that we can assemble inherent impl candidates on a possibly
5+
// unnormalized self type.
6+
7+
trait Foo {
8+
type Assoc;
9+
}
10+
impl Foo for i32 {
11+
type Assoc = Bar;
12+
}
13+
14+
struct Bar;
15+
impl Bar {
16+
fn method(&self) {}
17+
}
18+
19+
fn build<T: Foo>(_: T) -> T::Assoc {
20+
todo!()
21+
}
22+
23+
fn main() {
24+
build(1i32).method();
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// compile-flags: -Ztrait-solver=next
2+
// check-pass
3+
4+
#[derive(Default)]
5+
struct Foo {
6+
x: i32,
7+
}
8+
9+
fn main() {
10+
let mut xs = <[Foo; 1]>::default();
11+
xs[0].x = 1;
12+
(&mut xs[0]).x = 2;
13+
}

0 commit comments

Comments
 (0)