Skip to content

Commit 917c884

Browse files
committed
Auto merge of #134185 - compiler-errors:impl-trait-in-bindings, r=<try>
(Re-)Implement `impl_trait_in_bindings` This reimplements the `impl_trait_in_bindings` feature for local bindings. "`impl Trait` in bindings" serve as a form of *trait* ascription, where the type basically functions as an infer var but additionally registering the `impl Trait`'s trait bounds for the infer type. These trait bounds can be used to enforce that predicates hold, and can guide inference (e.g. for closure signature inference): ```rust let _: impl Fn(&u8) -> &u8 = |x| x; ``` They are implemented as an additional set of bounds that are registered when the type is lowered during typeck, and then these bounds are tied to a given `CanonicalUserTypeAscription` for borrowck. We enforce these `CanonicalUserTypeAscription` bounds during borrowck to make sure that the `impl Trait` types are sensitive to lifetimes: ```rust trait Static: 'static {} impl<T> Static for T where T: 'static {} let local = 1; let x: impl Static = &local; //~^ ERROR `local` does not live long enough ``` r? oli-obk cc #63065 --- Why can't we just use TAIT inference or something? Well, TAITs in bodies have the problem that they cannot reference lifetimes local to a body. For example: ```rust type TAIT = impl Display; let local = 0; let x: TAIT = &local; //~^ ERROR `local` does not live long enough ``` That's because TAITs requires us to do *opaque type inference* which is pretty strict, since we need to remap all of the lifetimes of the hidden type to universal regions. This is simply not possible here. --- I consider this part of the "impl trait everywhere" experiment. I'm not certain if this needs yet another lang team experiment.
2 parents 21fe748 + f84b2f6 commit 917c884

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+405
-51
lines changed

compiler/rustc_ast_lowering/src/block.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use rustc_ast::{Block, BlockCheckMode, Local, LocalKind, Stmt, StmtKind};
22
use rustc_hir as hir;
3+
use rustc_span::sym;
34
use smallvec::SmallVec;
45

56
use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext};
@@ -82,11 +83,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
8283
(self.arena.alloc_from_iter(stmts), expr)
8384
}
8485

86+
fn impl_trait_in_bindings_ctxt(&self, position: ImplTraitPosition) -> ImplTraitContext {
87+
if self.tcx.features().impl_trait_in_bindings() {
88+
ImplTraitContext::InBinding
89+
} else {
90+
ImplTraitContext::FeatureGated(position, sym::impl_trait_in_bindings)
91+
}
92+
}
93+
8594
fn lower_local(&mut self, l: &Local) -> &'hir hir::LetStmt<'hir> {
86-
let ty = l
87-
.ty
88-
.as_ref()
89-
.map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Variable)));
95+
let ty = l.ty.as_ref().map(|t| {
96+
self.lower_ty(t, self.impl_trait_in_bindings_ctxt(ImplTraitPosition::Variable))
97+
});
9098
let init = l.kind.init().map(|init| self.lower_expr(init));
9199
let hir_id = self.lower_node_id(l.id);
92100
let pat = self.lower_pat(&l.pat);

compiler/rustc_ast_lowering/src/lib.rs

+11
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,13 @@ enum ImplTraitContext {
260260
/// equivalent to a new opaque type like `type T = impl Debug; fn foo() -> T`.
261261
///
262262
OpaqueTy { origin: hir::OpaqueTyOrigin<LocalDefId> },
263+
264+
/// Treat `impl Trait` as a "trait ascription", which is like a type
265+
/// variable but that also enforces that a set of trait goals hold.
266+
///
267+
/// This is useful to guide inference for unnameable types.
268+
InBinding,
269+
263270
/// `impl Trait` is unstably accepted in this position.
264271
FeatureGated(ImplTraitPosition, Symbol),
265272
/// `impl Trait` is not accepted in this position.
@@ -1320,6 +1327,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
13201327
}
13211328
path
13221329
}
1330+
ImplTraitContext::InBinding => {
1331+
let bounds = self.lower_param_bounds(bounds, itctx);
1332+
hir::TyKind::TraitAscription(bounds)
1333+
}
13231334
ImplTraitContext::FeatureGated(position, feature) => {
13241335
let guar = self
13251336
.tcx

compiler/rustc_borrowck/src/type_check/canonical.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
275275
user_ty: ty::UserType<'tcx>,
276276
span: Span,
277277
) {
278-
let ty::UserType::Ty(user_ty) = user_ty else { bug!() };
278+
let ty::UserTypeKind::Ty(user_ty) = user_ty.kind else { bug!() };
279279

280280
// A fast path for a common case with closure input/output types.
281281
if let ty::Infer(_) = user_ty.kind() {

compiler/rustc_borrowck/src/type_check/input_output.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
110110
) {
111111
self.ascribe_user_type_skip_wf(
112112
arg_decl.ty,
113-
ty::UserType::Ty(user_ty),
113+
ty::UserType::new(ty::UserTypeKind::Ty(user_ty)),
114114
arg_decl.source_info.span,
115115
);
116116
}
@@ -119,7 +119,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
119119
let output_decl = &body.local_decls[RETURN_PLACE];
120120
self.ascribe_user_type_skip_wf(
121121
output_decl.ty,
122-
ty::UserType::Ty(user_provided_sig.output()),
122+
ty::UserType::new(ty::UserTypeKind::Ty(user_provided_sig.output())),
123123
output_decl.source_info.span,
124124
);
125125
}

compiler/rustc_borrowck/src/type_check/mod.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use rustc_middle::ty::visit::TypeVisitableExt;
3131
use rustc_middle::ty::{
3232
self, Binder, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, CoroutineArgsExt,
3333
Dynamic, GenericArgsRef, OpaqueHiddenType, OpaqueTypeKey, RegionVid, Ty, TyCtxt, UserArgs,
34-
UserType, UserTypeAnnotationIndex,
34+
UserTypeAnnotationIndex,
3535
};
3636
use rustc_middle::{bug, span_bug};
3737
use rustc_mir_dataflow::ResultsCursor;
@@ -370,7 +370,10 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
370370
} else {
371371
self.cx.ascribe_user_type(
372372
constant.const_.ty(),
373-
UserType::TypeOf(uv.def, UserArgs { args: uv.args, user_self_ty: None }),
373+
ty::UserType::new(ty::UserTypeKind::TypeOf(uv.def, UserArgs {
374+
args: uv.args,
375+
user_self_ty: None,
376+
})),
374377
locations.span(self.cx.body),
375378
);
376379
}
@@ -991,9 +994,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
991994
for user_annotation in self.user_type_annotations {
992995
let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation;
993996
let annotation = self.instantiate_canonical(span, user_ty);
994-
if let ty::UserType::TypeOf(def, args) = annotation
997+
if let ty::UserTypeKind::TypeOf(def, args) = annotation.kind
995998
&& let DefKind::InlineConst = tcx.def_kind(def)
996999
{
1000+
assert!(annotation.bounds.is_empty());
9971001
self.check_inline_const(inferred_ty, def.expect_local(), args, span);
9981002
} else {
9991003
self.ascribe_user_type(inferred_ty, annotation, span);

compiler/rustc_feature/src/removed.rs

-3
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,6 @@ declare_features! (
126126
better implied higher-ranked implied bounds support"
127127
)
128128
),
129-
/// Allows `impl Trait` in bindings (`let`, `const`, `static`).
130-
(removed, impl_trait_in_bindings, "1.55.0", Some(63065),
131-
Some("the implementation was not maintainable, the feature may get reintroduced once the current refactorings are done")),
132129
(removed, import_shadowing, "1.0.0", None, None),
133130
/// Allows in-band quantification of lifetime bindings (e.g., `fn foo(x: &'a u8) -> &'a u8`).
134131
(removed, in_band_lifetimes, "1.23.0", Some(44524),

compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,8 @@ declare_features! (
516516
(unstable, if_let_guard, "1.47.0", Some(51114)),
517517
/// Allows `impl Trait` to be used inside associated types (RFC 2515).
518518
(unstable, impl_trait_in_assoc_type, "1.70.0", Some(63063)),
519+
/// Allows `impl Trait` in bindings (`let`).
520+
(unstable, impl_trait_in_bindings, "1.64.0", Some(63065)),
519521
/// Allows `impl Trait` as output type in `Fn` traits in return position of functions.
520522
(unstable, impl_trait_in_fn_trait_return, "1.64.0", Some(99697)),
521523
/// Allows associated types in inherent impls.

compiler/rustc_hir/src/hir.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2889,6 +2889,8 @@ pub enum TyKind<'hir> {
28892889
Path(QPath<'hir>),
28902890
/// An opaque type definition itself. This is only used for `impl Trait`.
28912891
OpaqueDef(&'hir OpaqueTy<'hir>),
2892+
/// A trait ascription type, which is `impl Trait` within a body.
2893+
TraitAscription(GenericBounds<'hir>),
28922894
/// A trait object type `Bound1 + Bound2 + Bound3`
28932895
/// where `Bound` is a trait or a lifetime.
28942896
TraitObject(&'hir [PolyTraitRef<'hir>], &'hir Lifetime, TraitObjectSyntax),

compiler/rustc_hir/src/intravisit.rs

+3
Original file line numberDiff line numberDiff line change
@@ -892,6 +892,9 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul
892892
TyKind::OpaqueDef(opaque) => {
893893
try_visit!(visitor.visit_opaque_ty(opaque));
894894
}
895+
TyKind::TraitAscription(bounds) => {
896+
walk_list!(visitor, visit_param_bound, bounds);
897+
}
895898
TyKind::Array(ref ty, ref length) => {
896899
try_visit!(visitor.visit_ty(ty));
897900
try_visit!(visitor.visit_const_arg(length));

compiler/rustc_hir_analysis/src/collect.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use rustc_errors::{
2929
use rustc_hir::def::DefKind;
3030
use rustc_hir::def_id::{DefId, LocalDefId};
3131
use rustc_hir::intravisit::{self, Visitor, walk_generics};
32-
use rustc_hir::{self as hir, GenericParamKind, Node};
32+
use rustc_hir::{self as hir, GenericParamKind, HirId, Node};
3333
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
3434
use rustc_infer::traits::ObligationCause;
3535
use rustc_middle::hir::nested_filter;
@@ -436,6 +436,15 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
436436
ty::Const::new_error_with_message(self.tcx(), span, "bad placeholder constant")
437437
}
438438

439+
fn register_trait_ascription_bounds(
440+
&self,
441+
_: Vec<(ty::Clause<'tcx>, Span)>,
442+
_: HirId,
443+
span: Span,
444+
) {
445+
self.dcx().span_delayed_bug(span, "trait ascription type not allowed here");
446+
}
447+
439448
fn probe_ty_param_bounds(
440449
&self,
441450
span: Span,

compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs

+15
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,21 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
822822
};
823823
self.with(scope, |this| this.visit_ty(mt.ty));
824824
}
825+
hir::TyKind::TraitAscription(bounds) => {
826+
let scope = Scope::TraitRefBoundary { s: self.scope };
827+
self.with(scope, |this| {
828+
let scope = Scope::LateBoundary {
829+
s: this.scope,
830+
what: "`impl Trait` in binding",
831+
deny_late_regions: true,
832+
};
833+
this.with(scope, |this| {
834+
for bound in bounds {
835+
this.visit_param_bound(bound);
836+
}
837+
})
838+
});
839+
}
825840
_ => intravisit::walk_ty(self, ty),
826841
}
827842
}

compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs

+24
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,13 @@ pub trait HirTyLowerer<'tcx> {
123123
/// Returns the const to use when a const is omitted.
124124
fn ct_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Const<'tcx>;
125125

126+
fn register_trait_ascription_bounds(
127+
&self,
128+
bounds: Vec<(ty::Clause<'tcx>, Span)>,
129+
hir_id: HirId,
130+
span: Span,
131+
);
132+
126133
/// Probe bounds in scope where the bounded type coincides with the given type parameter.
127134
///
128135
/// Rephrased, this returns bounds of the form `T: Trait`, where `T` is a type parameter
@@ -2368,6 +2375,23 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
23682375

23692376
self.lower_opaque_ty(opaque_ty.def_id, in_trait)
23702377
}
2378+
hir::TyKind::TraitAscription(hir_bounds) => {
2379+
let self_ty = self.ty_infer(None, hir_ty.span);
2380+
let mut bounds = Bounds::default();
2381+
self.lower_bounds(
2382+
self_ty,
2383+
hir_bounds.iter(),
2384+
&mut bounds,
2385+
ty::List::empty(),
2386+
PredicateFilter::All,
2387+
);
2388+
self.register_trait_ascription_bounds(
2389+
bounds.clauses().collect(),
2390+
hir_ty.hir_id,
2391+
hir_ty.span,
2392+
);
2393+
self_ty
2394+
}
23712395
// If we encounter a type relative path with RTN generics, then it must have
23722396
// *not* gone through `lower_ty_maybe_return_type_notation`, and therefore
23732397
// it's certainly in an illegal position.

compiler/rustc_hir_pretty/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,9 @@ impl<'a> State<'a> {
289289
self.print_ty_fn(f.abi, f.safety, f.decl, None, f.generic_params, f.param_names);
290290
}
291291
hir::TyKind::OpaqueDef(..) => self.word("/*impl Trait*/"),
292+
hir::TyKind::TraitAscription(bounds) => {
293+
self.print_bounds("impl", bounds);
294+
}
292295
hir::TyKind::Path(ref qpath) => self.print_qpath(qpath, false),
293296
hir::TyKind::TraitObject(bounds, lifetime, syntax) => {
294297
if syntax == ast::TraitObjectSyntax::Dyn {

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

+32-10
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ use std::slice;
44
use rustc_abi::FieldIdx;
55
use rustc_data_structures::fx::FxHashSet;
66
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey};
7-
use rustc_hir as hir;
87
use rustc_hir::def::{CtorOf, DefKind, Res};
98
use rustc_hir::def_id::DefId;
9+
use rustc_hir::intravisit::Visitor;
1010
use rustc_hir::lang_items::LangItem;
11-
use rustc_hir::{ExprKind, GenericArg, HirId, Node, QPath};
11+
use rustc_hir::{self as hir, ExprKind, GenericArg, HirId, Node, QPath, intravisit};
1212
use rustc_hir_analysis::hir_ty_lowering::errors::GenericsArgsErrExtend;
1313
use rustc_hir_analysis::hir_ty_lowering::generics::{
1414
check_generic_arg_count_for_call, lower_generic_args,
@@ -25,7 +25,7 @@ use rustc_middle::ty::fold::TypeFoldable;
2525
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
2626
use rustc_middle::ty::{
2727
self, AdtKind, CanonicalUserType, GenericArgKind, GenericArgsRef, GenericParamDefKind,
28-
IsIdentity, Ty, TyCtxt, UserArgs, UserSelfTy, UserType,
28+
IsIdentity, Ty, TyCtxt, UserArgs, UserSelfTy,
2929
};
3030
use rustc_middle::{bug, span_bug};
3131
use rustc_session::lint;
@@ -216,11 +216,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
216216
debug!("fcx {}", self.tag());
217217

218218
if Self::can_contain_user_lifetime_bounds((args, user_self_ty)) {
219-
let canonicalized =
220-
self.canonicalize_user_type_annotation(UserType::TypeOf(def_id, UserArgs {
221-
args,
222-
user_self_ty,
223-
}));
219+
let canonicalized = self.canonicalize_user_type_annotation(ty::UserType::new(
220+
ty::UserTypeKind::TypeOf(def_id, UserArgs { args, user_self_ty }),
221+
));
224222
debug!(?canonicalized);
225223
self.write_user_type_annotation(hir_id, canonicalized);
226224
}
@@ -462,13 +460,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
462460
LoweredTy::from_raw(self, hir_ty.span, ty)
463461
}
464462

463+
pub(crate) fn collect_impl_trait_clauses_from_hir_ty(
464+
&self,
465+
hir_ty: &'tcx hir::Ty<'tcx>,
466+
) -> ty::Clauses<'tcx> {
467+
struct CollectClauses<'a, 'tcx> {
468+
clauses: Vec<ty::Clause<'tcx>>,
469+
fcx: &'a FnCtxt<'a, 'tcx>,
470+
}
471+
472+
impl<'tcx> intravisit::Visitor<'tcx> for CollectClauses<'_, 'tcx> {
473+
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
474+
if let Some(clauses) = self.fcx.trait_ascriptions.borrow().get(&ty.hir_id.local_id)
475+
{
476+
self.clauses.extend(clauses.iter().cloned());
477+
}
478+
intravisit::walk_ty(self, ty)
479+
}
480+
}
481+
482+
let mut clauses = CollectClauses { clauses: vec![], fcx: self };
483+
clauses.visit_ty(hir_ty);
484+
self.tcx.mk_clauses(&clauses.clauses)
485+
}
486+
465487
#[instrument(level = "debug", skip_all)]
466-
pub(crate) fn lower_ty_saving_user_provided_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> {
488+
pub(crate) fn lower_ty_saving_user_provided_ty(&self, hir_ty: &'tcx hir::Ty<'tcx>) -> Ty<'tcx> {
467489
let ty = self.lower_ty(hir_ty);
468490
debug!(?ty);
469491

470492
if Self::can_contain_user_lifetime_bounds(ty.raw) {
471-
let c_ty = self.canonicalize_response(UserType::Ty(ty.raw));
493+
let c_ty = self.canonicalize_response(ty::UserType::new(ty::UserTypeKind::Ty(ty.raw)));
472494
debug!(?c_ty);
473495
self.typeck_results.borrow_mut().user_provided_types_mut().insert(hir_ty.hir_id, c_ty);
474496
}

compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs

+29-1
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ use std::ops::Deref;
1010

1111
use hir::def_id::CRATE_DEF_ID;
1212
use rustc_errors::DiagCtxtHandle;
13-
use rustc_hir as hir;
1413
use rustc_hir::def_id::{DefId, LocalDefId};
14+
use rustc_hir::{self as hir, HirId, ItemLocalMap};
1515
use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, RegionInferReason};
1616
use rustc_infer::infer;
17+
use rustc_infer::traits::Obligation;
1718
use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt};
1819
use rustc_session::Session;
1920
use rustc_span::symbol::Ident;
@@ -114,6 +115,8 @@ pub(crate) struct FnCtxt<'a, 'tcx> {
114115

115116
pub(super) diverging_fallback_behavior: DivergingFallbackBehavior,
116117
pub(super) diverging_block_behavior: DivergingBlockBehavior,
118+
119+
pub(super) trait_ascriptions: RefCell<ItemLocalMap<Vec<ty::Clause<'tcx>>>>,
117120
}
118121

119122
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -141,6 +144,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
141144
fallback_has_occurred: Cell::new(false),
142145
diverging_fallback_behavior,
143146
diverging_block_behavior,
147+
trait_ascriptions: Default::default(),
144148
}
145149
}
146150

@@ -252,6 +256,30 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
252256
}
253257
}
254258

259+
fn register_trait_ascription_bounds(
260+
&self,
261+
bounds: Vec<(ty::Clause<'tcx>, Span)>,
262+
hir_id: HirId,
263+
_span: Span,
264+
) {
265+
for (clause, span) in bounds {
266+
if clause.has_escaping_bound_vars() {
267+
self.dcx().span_delayed_bug(span, "clause should have no escaping bound vars");
268+
continue;
269+
}
270+
271+
self.trait_ascriptions.borrow_mut().entry(hir_id.local_id).or_default().push(clause);
272+
273+
let clause = self.normalize(span, clause);
274+
self.register_predicate(Obligation::new(
275+
self.tcx,
276+
self.misc(span),
277+
self.param_env,
278+
clause,
279+
));
280+
}
281+
}
282+
255283
fn probe_ty_param_bounds(
256284
&self,
257285
_: Span,

0 commit comments

Comments
 (0)