Skip to content

Commit a9bb589

Browse files
committed
Auto merge of rust-lang#100676 - lcnr:implied-bounds-yay, r=nikomatsakis
implied bounds: explicitly state which types are assumed to be wf Adds a new query which maps each definition to the types which that definition assumes to be well formed. The intent is to make it easier to reason about implied bounds. This change should not influence the user-facing behavior of rustc. Notably, `borrowck` still only assumes that the function signature of associated functions is well formed while `wfcheck` assumes that the both the function signature and the impl trait ref is well formed. Not sure if that by itself can trigger UB or whether it's just annoying. As a next step, we can add `WellFormed` predicates to `predicates_of` of these items and can stop adding the wf bounds at each place which uses them. I also intend to move the computation from `assumed_wf_types` to `implied_bounds` into the `param_env` computation. This requires me to take a deeper look at `compare_predicate_entailment` which is currently somewhat weird wrt implied bounds so I am not touching this here. r? `@nikomatsakis`
2 parents d0ea1d7 + 736288f commit a9bb589

24 files changed

+321
-237
lines changed

compiler/rustc_middle/src/query/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,14 @@ rustc_queries! {
765765
desc { |tcx| "processing `{}`", tcx.def_path_str(key.to_def_id()) }
766766
}
767767

768+
/// Returns the types assumed to be well formed while "inside" of the given item.
769+
///
770+
/// Note that we've liberated the late bound regions of function signatures, so
771+
/// this can not be used to check whether these types are well formed.
772+
query assumed_wf_types(key: DefId) -> &'tcx ty::List<Ty<'tcx>> {
773+
desc { |tcx| "computing the implied bounds of {}", tcx.def_path_str(key) }
774+
}
775+
768776
/// Computes the signature of the function.
769777
query fn_sig(key: DefId) -> ty::PolyFnSig<'tcx> {
770778
desc { |tcx| "computing function signature of `{}`", tcx.def_path_str(key) }

compiler/rustc_middle/src/ty/list.rs

+4
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ impl<T> List<T> {
6565
pub fn len(&self) -> usize {
6666
self.len
6767
}
68+
69+
pub fn as_slice(&self) -> &[T] {
70+
self
71+
}
6872
}
6973

7074
impl<T: Copy> List<T> {

compiler/rustc_trait_selection/src/traits/engine.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use std::cell::RefCell;
33
use super::TraitEngine;
44
use super::{ChalkFulfillmentContext, FulfillmentContext};
55
use crate::infer::InferCtxtExt;
6-
use rustc_hir::def_id::DefId;
6+
use rustc_data_structures::fx::FxHashSet;
7+
use rustc_hir::def_id::{DefId, LocalDefId};
78
use rustc_infer::infer::{InferCtxt, InferOk};
89
use rustc_infer::traits::{
910
FulfillmentError, Obligation, ObligationCause, PredicateObligation, TraitEngineExt as _,
@@ -12,6 +13,7 @@ use rustc_middle::ty::error::TypeError;
1213
use rustc_middle::ty::ToPredicate;
1314
use rustc_middle::ty::TypeFoldable;
1415
use rustc_middle::ty::{self, Ty, TyCtxt};
16+
use rustc_span::Span;
1517

1618
pub trait TraitEngineExt<'tcx> {
1719
fn new(tcx: TyCtxt<'tcx>) -> Box<Self>;
@@ -109,4 +111,23 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
109111
pub fn select_all_or_error(&self) -> Vec<FulfillmentError<'tcx>> {
110112
self.engine.borrow_mut().select_all_or_error(self.infcx)
111113
}
114+
115+
pub fn assumed_wf_types(
116+
&self,
117+
param_env: ty::ParamEnv<'tcx>,
118+
span: Span,
119+
def_id: LocalDefId,
120+
) -> FxHashSet<Ty<'tcx>> {
121+
let tcx = self.infcx.tcx;
122+
let assumed_wf_types = tcx.assumed_wf_types(def_id);
123+
let mut implied_bounds = FxHashSet::default();
124+
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
125+
let cause = ObligationCause::misc(span, hir_id);
126+
for ty in assumed_wf_types {
127+
implied_bounds.insert(ty);
128+
let normalized = self.normalize(cause.clone(), param_env, ty);
129+
implied_bounds.insert(normalized);
130+
}
131+
implied_bounds
132+
}
112133
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
use crate::rustc_middle::ty::DefIdTree;
2+
use rustc_hir::{def::DefKind, def_id::DefId};
3+
use rustc_middle::ty::{self, Ty, TyCtxt};
4+
5+
pub fn provide(providers: &mut ty::query::Providers) {
6+
*providers = ty::query::Providers { assumed_wf_types, ..*providers };
7+
}
8+
9+
fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx ty::List<Ty<'tcx>> {
10+
match tcx.def_kind(def_id) {
11+
DefKind::Fn => {
12+
let sig = tcx.fn_sig(def_id);
13+
let liberated_sig = tcx.liberate_late_bound_regions(def_id, sig);
14+
liberated_sig.inputs_and_output
15+
}
16+
DefKind::AssocFn => {
17+
let sig = tcx.fn_sig(def_id);
18+
let liberated_sig = tcx.liberate_late_bound_regions(def_id, sig);
19+
let mut assumed_wf_types: Vec<_> =
20+
tcx.assumed_wf_types(tcx.parent(def_id)).as_slice().into();
21+
assumed_wf_types.extend(liberated_sig.inputs_and_output);
22+
tcx.intern_type_list(&assumed_wf_types)
23+
}
24+
DefKind::Impl => match tcx.impl_trait_ref(def_id) {
25+
Some(trait_ref) => {
26+
let types: Vec<_> = trait_ref.substs.types().collect();
27+
tcx.intern_type_list(&types)
28+
}
29+
// Only the impl self type
30+
None => tcx.intern_type_list(&[tcx.type_of(def_id)]),
31+
},
32+
DefKind::AssocConst | DefKind::AssocTy => tcx.assumed_wf_types(tcx.parent(def_id)),
33+
DefKind::Mod
34+
| DefKind::Struct
35+
| DefKind::Union
36+
| DefKind::Enum
37+
| DefKind::Variant
38+
| DefKind::Trait
39+
| DefKind::TyAlias
40+
| DefKind::ForeignTy
41+
| DefKind::TraitAlias
42+
| DefKind::TyParam
43+
| DefKind::Const
44+
| DefKind::ConstParam
45+
| DefKind::Static(_)
46+
| DefKind::Ctor(_, _)
47+
| DefKind::Macro(_)
48+
| DefKind::ExternCrate
49+
| DefKind::Use
50+
| DefKind::ForeignMod
51+
| DefKind::AnonConst
52+
| DefKind::InlineConst
53+
| DefKind::OpaqueTy
54+
| DefKind::Field
55+
| DefKind::LifetimeParam
56+
| DefKind::GlobalAsm
57+
| DefKind::Closure
58+
| DefKind::Generator => ty::List::empty(),
59+
}
60+
}

compiler/rustc_ty_utils/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use rustc_middle::ty::query::Providers;
2121
mod assoc;
2222
mod common_traits;
2323
mod consts;
24+
mod implied_bounds;
2425
pub mod instance;
2526
mod needs_drop;
2627
pub mod representability;
@@ -30,6 +31,7 @@ pub fn provide(providers: &mut Providers) {
3031
assoc::provide(providers);
3132
common_traits::provide(providers);
3233
consts::provide(providers);
34+
implied_bounds::provide(providers);
3335
needs_drop::provide(providers);
3436
ty::provide(providers);
3537
instance::provide(providers);

compiler/rustc_typeck/src/check/compare_method.rs

+71-75
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use super::potentially_plural_count;
22
use crate::check::regionck::OutlivesEnvironmentExt;
3-
use crate::check::wfcheck;
43
use crate::errors::LifetimesOrBoundsMismatchOnTrait;
54
use rustc_data_structures::fx::FxHashSet;
65
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed};
@@ -71,6 +70,72 @@ pub(crate) fn compare_impl_method<'tcx>(
7170
}
7271
}
7372

73+
/// This function is best explained by example. Consider a trait:
74+
///
75+
/// trait Trait<'t, T> {
76+
/// // `trait_m`
77+
/// fn method<'a, M>(t: &'t T, m: &'a M) -> Self;
78+
/// }
79+
///
80+
/// And an impl:
81+
///
82+
/// impl<'i, 'j, U> Trait<'j, &'i U> for Foo {
83+
/// // `impl_m`
84+
/// fn method<'b, N>(t: &'j &'i U, m: &'b N) -> Foo;
85+
/// }
86+
///
87+
/// We wish to decide if those two method types are compatible.
88+
/// For this we have to show that, assuming the bounds of the impl hold, the
89+
/// bounds of `trait_m` imply the bounds of `impl_m`.
90+
///
91+
/// We start out with `trait_to_impl_substs`, that maps the trait
92+
/// type parameters to impl type parameters. This is taken from the
93+
/// impl trait reference:
94+
///
95+
/// trait_to_impl_substs = {'t => 'j, T => &'i U, Self => Foo}
96+
///
97+
/// We create a mapping `dummy_substs` that maps from the impl type
98+
/// parameters to fresh types and regions. For type parameters,
99+
/// this is the identity transform, but we could as well use any
100+
/// placeholder types. For regions, we convert from bound to free
101+
/// regions (Note: but only early-bound regions, i.e., those
102+
/// declared on the impl or used in type parameter bounds).
103+
///
104+
/// impl_to_placeholder_substs = {'i => 'i0, U => U0, N => N0 }
105+
///
106+
/// Now we can apply `placeholder_substs` to the type of the impl method
107+
/// to yield a new function type in terms of our fresh, placeholder
108+
/// types:
109+
///
110+
/// <'b> fn(t: &'i0 U0, m: &'b) -> Foo
111+
///
112+
/// We now want to extract and substitute the type of the *trait*
113+
/// method and compare it. To do so, we must create a compound
114+
/// substitution by combining `trait_to_impl_substs` and
115+
/// `impl_to_placeholder_substs`, and also adding a mapping for the method
116+
/// type parameters. We extend the mapping to also include
117+
/// the method parameters.
118+
///
119+
/// trait_to_placeholder_substs = { T => &'i0 U0, Self => Foo, M => N0 }
120+
///
121+
/// Applying this to the trait method type yields:
122+
///
123+
/// <'a> fn(t: &'i0 U0, m: &'a) -> Foo
124+
///
125+
/// This type is also the same but the name of the bound region (`'a`
126+
/// vs `'b`). However, the normal subtyping rules on fn types handle
127+
/// this kind of equivalency just fine.
128+
///
129+
/// We now use these substitutions to ensure that all declared bounds are
130+
/// satisfied by the implementation's method.
131+
///
132+
/// We do this by creating a parameter environment which contains a
133+
/// substitution corresponding to `impl_to_placeholder_substs`. We then build
134+
/// `trait_to_placeholder_substs` and use it to convert the predicates contained
135+
/// in the `trait_m` generics to the placeholder form.
136+
///
137+
/// Finally we register each of these predicates as an obligation and check that
138+
/// they hold.
74139
fn compare_predicate_entailment<'tcx>(
75140
tcx: TyCtxt<'tcx>,
76141
impl_m: &ty::AssocItem,
@@ -97,69 +162,6 @@ fn compare_predicate_entailment<'tcx>(
97162
},
98163
);
99164

100-
// This code is best explained by example. Consider a trait:
101-
//
102-
// trait Trait<'t, T> {
103-
// fn method<'a, M>(t: &'t T, m: &'a M) -> Self;
104-
// }
105-
//
106-
// And an impl:
107-
//
108-
// impl<'i, 'j, U> Trait<'j, &'i U> for Foo {
109-
// fn method<'b, N>(t: &'j &'i U, m: &'b N) -> Foo;
110-
// }
111-
//
112-
// We wish to decide if those two method types are compatible.
113-
//
114-
// We start out with trait_to_impl_substs, that maps the trait
115-
// type parameters to impl type parameters. This is taken from the
116-
// impl trait reference:
117-
//
118-
// trait_to_impl_substs = {'t => 'j, T => &'i U, Self => Foo}
119-
//
120-
// We create a mapping `dummy_substs` that maps from the impl type
121-
// parameters to fresh types and regions. For type parameters,
122-
// this is the identity transform, but we could as well use any
123-
// placeholder types. For regions, we convert from bound to free
124-
// regions (Note: but only early-bound regions, i.e., those
125-
// declared on the impl or used in type parameter bounds).
126-
//
127-
// impl_to_placeholder_substs = {'i => 'i0, U => U0, N => N0 }
128-
//
129-
// Now we can apply placeholder_substs to the type of the impl method
130-
// to yield a new function type in terms of our fresh, placeholder
131-
// types:
132-
//
133-
// <'b> fn(t: &'i0 U0, m: &'b) -> Foo
134-
//
135-
// We now want to extract and substitute the type of the *trait*
136-
// method and compare it. To do so, we must create a compound
137-
// substitution by combining trait_to_impl_substs and
138-
// impl_to_placeholder_substs, and also adding a mapping for the method
139-
// type parameters. We extend the mapping to also include
140-
// the method parameters.
141-
//
142-
// trait_to_placeholder_substs = { T => &'i0 U0, Self => Foo, M => N0 }
143-
//
144-
// Applying this to the trait method type yields:
145-
//
146-
// <'a> fn(t: &'i0 U0, m: &'a) -> Foo
147-
//
148-
// This type is also the same but the name of the bound region ('a
149-
// vs 'b). However, the normal subtyping rules on fn types handle
150-
// this kind of equivalency just fine.
151-
//
152-
// We now use these substitutions to ensure that all declared bounds are
153-
// satisfied by the implementation's method.
154-
//
155-
// We do this by creating a parameter environment which contains a
156-
// substitution corresponding to impl_to_placeholder_substs. We then build
157-
// trait_to_placeholder_substs and use it to convert the predicates contained
158-
// in the trait_m.generics to the placeholder form.
159-
//
160-
// Finally we register each of these predicates as an obligation in
161-
// a fresh FulfillmentCtxt, and invoke select_all_or_error.
162-
163165
// Create mapping from impl to placeholder.
164166
let impl_to_placeholder_substs = InternalSubsts::identity_for_item(tcx, impl_m.def_id);
165167

@@ -1445,14 +1447,17 @@ pub fn check_type_bounds<'tcx>(
14451447
};
14461448
debug!(?normalize_param_env);
14471449

1450+
let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local());
14481451
let impl_ty_substs = InternalSubsts::identity_for_item(tcx, impl_ty.def_id);
14491452
let rebased_substs = impl_ty_substs.rebase_onto(tcx, container_id, impl_trait_ref.substs);
14501453

14511454
tcx.infer_ctxt().enter(move |infcx| {
14521455
let ocx = ObligationCtxt::new(&infcx);
14531456

1457+
let assumed_wf_types =
1458+
ocx.assumed_wf_types(param_env, impl_ty_span, impl_ty.def_id.expect_local());
1459+
14541460
let mut selcx = traits::SelectionContext::new(&infcx);
1455-
let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local());
14561461
let normalize_cause = ObligationCause::new(
14571462
impl_ty_span,
14581463
impl_ty_hir_id,
@@ -1508,17 +1513,8 @@ pub fn check_type_bounds<'tcx>(
15081513

15091514
// Finally, resolve all regions. This catches wily misuses of
15101515
// lifetime parameters.
1511-
let implied_bounds = match impl_ty.container {
1512-
ty::TraitContainer => FxHashSet::default(),
1513-
ty::ImplContainer => wfcheck::impl_implied_bounds(
1514-
tcx,
1515-
param_env,
1516-
container_id.expect_local(),
1517-
impl_ty_span,
1518-
),
1519-
};
15201516
let mut outlives_environment = OutlivesEnvironment::new(param_env);
1521-
outlives_environment.add_implied_bounds(&infcx, implied_bounds, impl_ty_hir_id);
1517+
outlives_environment.add_implied_bounds(&infcx, assumed_wf_types, impl_ty_hir_id);
15221518
infcx.check_region_obligations_and_report_errors(
15231519
impl_ty.def_id.expect_local(),
15241520
&outlives_environment,

0 commit comments

Comments
 (0)