Skip to content

Commit 7a19ab7

Browse files
committed
inherent associated types: better type inference
1 parent b869e84 commit 7a19ab7

File tree

12 files changed

+155
-62
lines changed

12 files changed

+155
-62
lines changed

compiler/rustc_hir_analysis/src/astconv/mod.rs

+55-14
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ use rustc_hir::def_id::{DefId, LocalDefId};
2828
use rustc_hir::intravisit::{walk_generics, Visitor as _};
2929
use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin};
3030
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
31-
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
32-
use rustc_infer::traits::ObligationCause;
31+
use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt};
32+
use rustc_infer::traits::{ObligationCause, PredicateObligations};
3333
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
3434
use rustc_middle::middle::stability::AllowUnstable;
3535
use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef};
@@ -46,7 +46,9 @@ use rustc_trait_selection::traits::error_reporting::{
4646
report_object_safety_error, suggestions::NextTypeParamName,
4747
};
4848
use rustc_trait_selection::traits::wf::object_region_bounds;
49-
use rustc_trait_selection::traits::{self, astconv_object_safety_violations, ObligationCtxt};
49+
use rustc_trait_selection::traits::{
50+
self, astconv_object_safety_violations, NormalizeExt, ObligationCtxt,
51+
};
5052

5153
use smallvec::{smallvec, SmallVec};
5254
use std::collections::BTreeSet;
@@ -127,6 +129,8 @@ pub trait AstConv<'tcx> {
127129

128130
fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span);
129131

132+
fn register_predicate_obligations(&self, obligations: PredicateObligations<'tcx>);
133+
130134
fn astconv(&self) -> &dyn AstConv<'tcx>
131135
where
132136
Self: Sized,
@@ -2234,7 +2238,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
22342238
let mut fulfillment_errors = Vec::new();
22352239
let mut applicable_candidates: Vec<_> = candidates
22362240
.iter()
2237-
.filter_map(|&(impl_, (assoc_item, def_scope))| {
2241+
.filter_map(|&(impl_, item)| {
22382242
infcx.probe(|_| {
22392243
let ocx = ObligationCtxt::new_in_snapshot(&infcx);
22402244

@@ -2250,15 +2254,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
22502254
// Check whether the impl imposes obligations we have to worry about.
22512255
let impl_bounds = tcx.predicates_of(impl_);
22522256
let impl_bounds = impl_bounds.instantiate(tcx, impl_substs);
2253-
22542257
let impl_bounds = ocx.normalize(&cause, param_env, impl_bounds);
2255-
22562258
let impl_obligations = traits::predicates_for_generics(
22572259
|_, _| cause.clone(),
22582260
param_env,
22592261
impl_bounds,
22602262
);
2261-
22622263
ocx.register_obligations(impl_obligations);
22632264

22642265
let mut errors = ocx.select_where_possible();
@@ -2267,26 +2268,66 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
22672268
return None;
22682269
}
22692270

2270-
// FIXME(fmease): Unsolved vars can escape this InferCtxt snapshot.
2271-
Some((assoc_item, def_scope, infcx.resolve_vars_if_possible(impl_substs)))
2271+
let impl_substs = if !self.allow_ty_infer() {
2272+
let substs = infcx.resolve_vars_if_possible(impl_substs);
2273+
assert!(!substs.needs_infer());
2274+
Some(substs)
2275+
} else {
2276+
None
2277+
};
2278+
2279+
Some((item, impl_, impl_substs))
22722280
})
22732281
})
22742282
.collect();
22752283

22762284
if applicable_candidates.len() > 1 {
22772285
return Err(self.complain_about_ambiguous_inherent_assoc_type(
22782286
name,
2279-
applicable_candidates.into_iter().map(|(candidate, ..)| candidate).collect(),
2287+
applicable_candidates.into_iter().map(|((candidate, ..), ..)| candidate).collect(),
22802288
span,
22812289
));
22822290
}
22832291

2284-
if let Some((assoc_item, def_scope, impl_substs)) = applicable_candidates.pop() {
2292+
if let Some(((assoc_item, def_scope), impl_, probe_impl_substs)) =
2293+
applicable_candidates.pop()
2294+
{
22852295
self.check_assoc_ty(assoc_item, name, def_scope, block, span);
22862296

2287-
// FIXME(inherent_associated_types): To fully *confirm* the *probed* candidate, we still
2288-
// need to relate the Self-type with fresh item substs & register region obligations for
2289-
// regionck to prove/disprove.
2297+
// FIXME(fmease, inherent_associated_types): Register WF obligations for the Self type.
2298+
// At the moment, we don't regionck the Self type or the substitutions.
2299+
2300+
let impl_substs;
2301+
if let Some(probe_impl_substs) = probe_impl_substs {
2302+
impl_substs = probe_impl_substs;
2303+
} else {
2304+
impl_substs = infcx.fresh_item_substs(impl_);
2305+
2306+
let impl_bounds = tcx.predicates_of(impl_);
2307+
let impl_bounds = impl_bounds.instantiate(tcx, impl_substs);
2308+
let InferOk { value: impl_bounds, obligations: norm_obligations } =
2309+
infcx.at(&cause, param_env).normalize(impl_bounds);
2310+
self.register_predicate_obligations(norm_obligations);
2311+
let impl_obligations =
2312+
traits::predicates_for_generics(|_, _| cause.clone(), param_env, impl_bounds);
2313+
self.register_predicate_obligations(impl_obligations.collect());
2314+
2315+
let impl_ty = tcx.type_of(impl_);
2316+
let impl_ty = impl_ty.subst(tcx, impl_substs);
2317+
let InferOk { value: impl_ty, obligations: norm_obligations } =
2318+
infcx.at(&cause, param_env).normalize(impl_ty);
2319+
self.register_predicate_obligations(norm_obligations);
2320+
2321+
match infcx.at(&cause, param_env).eq(impl_ty, self_ty) {
2322+
Ok(ok) => self.register_predicate_obligations(ok.obligations),
2323+
Err(_) => {
2324+
tcx.sess.delay_span_bug(
2325+
span,
2326+
&format!("{self_ty:?} was a subtype of {impl_ty:?} but now it is not?"),
2327+
);
2328+
}
2329+
}
2330+
}
22902331

22912332
let item_substs =
22922333
self.create_substs_for_associated_item(span, assoc_item, segment, impl_substs);

compiler/rustc_hir_analysis/src/collect.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
2626
use rustc_hir::intravisit::{self, Visitor};
2727
use rustc_hir::{GenericParamKind, Node};
2828
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
29-
use rustc_infer::traits::ObligationCause;
29+
use rustc_infer::traits::{ObligationCause, PredicateObligations};
3030
use rustc_middle::hir::nested_filter;
3131
use rustc_middle::ty::query::Providers;
3232
use rustc_middle::ty::util::{Discr, IntTypeExt};
@@ -516,6 +516,10 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> {
516516
// There's no place to record types from signatures?
517517
}
518518

519+
fn register_predicate_obligations(&self, _: PredicateObligations<'tcx>) {
520+
// There's no place to track this, so just let it go.
521+
}
522+
519523
fn infcx(&self) -> Option<&InferCtxt<'tcx>> {
520524
None
521525
}

compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ mod suggestions;
66

77
pub use _impl::*;
88
use rustc_errors::ErrorGuaranteed;
9+
use rustc_infer::traits::PredicateObligations;
910
pub use suggestions::*;
1011

1112
use crate::coercion::DynamicCoerceMany;
@@ -326,6 +327,10 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
326327
self.write_ty(hir_id, ty)
327328
}
328329

330+
fn register_predicate_obligations(&self, obligations: PredicateObligations<'tcx>) {
331+
self.register_predicates(obligations)
332+
}
333+
329334
fn infcx(&self) -> Option<&infer::InferCtxt<'tcx>> {
330335
Some(&self.infcx)
331336
}

tests/ui/associated-inherent-types/bugs/ice-substitution.rs

-23
This file was deleted.

tests/ui/associated-inherent-types/bugs/ice-substitution.stderr

-6
This file was deleted.

tests/ui/associated-inherent-types/bugs/inference-fail.rs

-15
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// check-pass
2+
3+
#![feature(inherent_associated_types)]
4+
#![allow(incomplete_features)]
5+
6+
struct Cont<T>(T);
7+
8+
impl<T: Copy> Cont<T> {
9+
type Out = Vec<T>;
10+
}
11+
12+
pub fn weird<T: Copy>(x: T) {
13+
let _: Cont<_>::Out = vec![true];
14+
}
15+
16+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#![feature(inherent_associated_types)]
2+
#![allow(incomplete_features)]
3+
4+
struct S<T>(T);
5+
6+
impl<T> S<T> { type P = (); }
7+
8+
fn main() {
9+
// There is no way to infer this type.
10+
let _: S<_>::P = (); //~ ERROR type annotations needed
11+
}

tests/ui/associated-inherent-types/bugs/inference-fail.stderr renamed to tests/ui/associated-inherent-types/inference-fail.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error[E0282]: type annotations needed
2-
--> $DIR/inference-fail.rs:14:14
2+
--> $DIR/inference-fail.rs:10:12
33
|
4-
LL | let _: S<_>::P;
5-
| ^ cannot infer type
4+
LL | let _: S<_>::P = ();
5+
| ^^^^^^^ cannot infer type
66

77
error: aborting due to previous error
88

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Testing inference capabilities.
2+
// check-pass
3+
4+
#![feature(inherent_associated_types)]
5+
#![allow(incomplete_features)]
6+
7+
use std::convert::identity;
8+
9+
struct Container<T>(T);
10+
11+
impl Container<u32> {
12+
type Sink = ();
13+
}
14+
15+
impl<Any> Container<Any> {
16+
type Thing = Any;
17+
}
18+
19+
impl<T> Container<(T, ())> {
20+
type Output = ((), Wrapped<T>);
21+
}
22+
23+
fn main() {
24+
// Inferred via the Self type of the impl.
25+
let _: Container<_>::Sink;
26+
27+
// Inferred via the RHS:
28+
29+
let _: Container<_>::Thing = 0;
30+
31+
let _: Container<Wrapped<_>>::Thing = Wrapped(false);
32+
33+
let _: Container<_>::Output = (drop(1), Wrapped("..."));
34+
35+
let binding: Container<_>::Thing = Default::default(); // unsolved at this point
36+
identity::<String>(binding); // constrained and solved here
37+
}
38+
39+
struct Wrapped<T>(T);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#![feature(inherent_associated_types)]
2+
#![allow(incomplete_features)]
3+
4+
struct S<T>(T);
5+
6+
impl<T: Copy> S<T> {
7+
type T = T;
8+
}
9+
10+
fn main() {
11+
let _: S<_>::T = String::new(); //~ ERROR the trait bound `String: Copy` is not satisfied
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0277]: the trait bound `String: Copy` is not satisfied
2+
--> $DIR/unsatisfied-bounds-inferred-type.rs:11:12
3+
|
4+
LL | let _: S<_>::T = String::new();
5+
| ^^^^^^^ the trait `Copy` is not implemented for `String`
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)