Skip to content

Commit d714a22

Browse files
(Re-)Implement impl_trait_in_bindings
1 parent 1da411e commit d714a22

Some content is hidden

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

45 files changed

+391
-25
lines changed

compiler/rustc_ast_lowering/src/block.rs

+15-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,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
8283
(self.arena.alloc_from_iter(stmts), expr)
8384
}
8485

86+
/// Return an `ImplTraitContext` that allows impl trait in bindings if
87+
/// the feature gate is enabled, or issues a feature error if it is not.
88+
fn impl_trait_in_bindings_ctxt(&self, position: ImplTraitPosition) -> ImplTraitContext {
89+
if self.tcx.features().impl_trait_in_bindings() {
90+
ImplTraitContext::InBinding
91+
} else {
92+
ImplTraitContext::FeatureGated(position, sym::impl_trait_in_bindings)
93+
}
94+
}
95+
8596
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)));
97+
// Let statements are allowed to have impl trait in bindings.
98+
let ty = l.ty.as_ref().map(|t| {
99+
self.lower_ty(t, self.impl_trait_in_bindings_ctxt(ImplTraitPosition::Variable))
100+
});
90101
let init = l.kind.init().map(|init| self.lower_expr(init));
91102
let hir_id = self.lower_node_id(l.id);
92103
let pat = self.lower_pat(&l.pat);

compiler/rustc_ast_lowering/src/lib.rs

+10
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.
@@ -1327,6 +1334,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
13271334
}
13281335
path
13291336
}
1337+
ImplTraitContext::InBinding => {
1338+
hir::TyKind::TraitAscription(self.lower_param_bounds(bounds, itctx))
1339+
}
13301340
ImplTraitContext::FeatureGated(position, feature) => {
13311341
let guar = self
13321342
.tcx

compiler/rustc_borrowck/src/type_check/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -997,7 +997,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
997997
if let ty::UserTypeKind::TypeOf(def, args) = annotation.kind
998998
&& let DefKind::InlineConst = tcx.def_kind(def)
999999
{
1000-
// TODO:
1000+
assert!(annotation.bounds.is_empty());
10011001
self.check_inline_const(inferred_ty, def.expect_local(), args, span);
10021002
} else {
10031003
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
@@ -517,6 +517,8 @@ declare_features! (
517517
(unstable, if_let_guard, "1.47.0", Some(51114)),
518518
/// Allows `impl Trait` to be used inside associated types (RFC 2515).
519519
(unstable, impl_trait_in_assoc_type, "1.70.0", Some(63063)),
520+
/// Allows `impl Trait` in bindings (`let`).
521+
(unstable, impl_trait_in_bindings, "1.64.0", Some(63065)),
520522
/// Allows `impl Trait` as output type in `Fn` traits in return position of functions.
521523
(unstable, impl_trait_in_fn_trait_return, "1.64.0", Some(99697)),
522524
/// Allows associated types in inherent impls.

compiler/rustc_hir/src/hir.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2906,6 +2906,8 @@ pub enum TyKind<'hir> {
29062906
Path(QPath<'hir>),
29072907
/// An opaque type definition itself. This is only used for `impl Trait`.
29082908
OpaqueDef(&'hir OpaqueTy<'hir>),
2909+
/// A trait ascription type, which is `impl Trait` within a local binding.
2910+
TraitAscription(GenericBounds<'hir>),
29092911
/// A trait object type `Bound1 + Bound2 + Bound3`
29102912
/// where `Bound` is a trait or a lifetime.
29112913
TraitObject(&'hir [PolyTraitRef<'hir>], &'hir Lifetime, TraitObjectSyntax),

compiler/rustc_hir/src/intravisit.rs

+3
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,9 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul
900900
TyKind::OpaqueDef(opaque) => {
901901
try_visit!(visitor.visit_opaque_ty(opaque));
902902
}
903+
TyKind::TraitAscription(bounds) => {
904+
walk_list!(visitor, visit_param_bound, bounds);
905+
}
903906
TyKind::Array(ref ty, ref length) => {
904907
try_visit!(visitor.visit_ty(ty));
905908
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
@@ -852,6 +852,21 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
852852
};
853853
self.with(scope, |this| this.visit_ty(mt.ty));
854854
}
855+
hir::TyKind::TraitAscription(bounds) => {
856+
let scope = Scope::TraitRefBoundary { s: self.scope };
857+
self.with(scope, |this| {
858+
let scope = Scope::LateBoundary {
859+
s: this.scope,
860+
what: "`impl Trait` in binding",
861+
deny_late_regions: true,
862+
};
863+
this.with(scope, |this| {
864+
for bound in bounds {
865+
this.visit_param_bound(bound);
866+
}
867+
})
868+
});
869+
}
855870
_ => intravisit::walk_ty(self, ty),
856871
}
857872
}

compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs

+26
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
@@ -2375,6 +2382,25 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
23752382

23762383
self.lower_opaque_ty(opaque_ty.def_id, in_trait)
23772384
}
2385+
hir::TyKind::TraitAscription(hir_bounds) => {
2386+
// Impl trait in bindings lower as an infer var with additional
2387+
// set of type bounds.
2388+
let self_ty = self.ty_infer(None, hir_ty.span);
2389+
let mut bounds = Bounds::default();
2390+
self.lower_bounds(
2391+
self_ty,
2392+
hir_bounds.iter(),
2393+
&mut bounds,
2394+
ty::List::empty(),
2395+
PredicateFilter::All,
2396+
);
2397+
self.register_trait_ascription_bounds(
2398+
bounds.clauses().collect(),
2399+
hir_ty.hir_id,
2400+
hir_ty.span,
2401+
);
2402+
self_ty
2403+
}
23782404
// If we encounter a type relative path with RTN generics, then it must have
23792405
// *not* gone through `lower_ty_maybe_return_type_notation`, and therefore
23802406
// it's certainly in an illegal position.

compiler/rustc_hir_pretty/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,9 @@ impl<'a> State<'a> {
292292
self.print_unsafe_binder(unsafe_binder);
293293
}
294294
hir::TyKind::OpaqueDef(..) => self.word("/*impl Trait*/"),
295+
hir::TyKind::TraitAscription(bounds) => {
296+
self.print_bounds("impl", bounds);
297+
}
295298
hir::TyKind::Path(ref qpath) => self.print_qpath(qpath, false),
296299
hir::TyKind::TraitObject(bounds, lifetime, syntax) => {
297300
if syntax == ast::TraitObjectSyntax::Dyn {

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

+30-3
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,
@@ -460,8 +460,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
460460
LoweredTy::from_raw(self, hir_ty.span, ty)
461461
}
462462

463+
/// Walk a `hir_ty` and collect any clauses that may have come from a type
464+
/// within the `hir_ty`. These clauses will be canonicalized with a user type
465+
/// annotation so that we can enforce these bounds in borrowck, too.
466+
pub(crate) fn collect_impl_trait_clauses_from_hir_ty(
467+
&self,
468+
hir_ty: &'tcx hir::Ty<'tcx>,
469+
) -> ty::Clauses<'tcx> {
470+
struct CollectClauses<'a, 'tcx> {
471+
clauses: Vec<ty::Clause<'tcx>>,
472+
fcx: &'a FnCtxt<'a, 'tcx>,
473+
}
474+
475+
impl<'tcx> intravisit::Visitor<'tcx> for CollectClauses<'_, 'tcx> {
476+
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
477+
if let Some(clauses) = self.fcx.trait_ascriptions.borrow().get(&ty.hir_id.local_id)
478+
{
479+
self.clauses.extend(clauses.iter().cloned());
480+
}
481+
intravisit::walk_ty(self, ty)
482+
}
483+
}
484+
485+
let mut clauses = CollectClauses { clauses: vec![], fcx: self };
486+
clauses.visit_ty(hir_ty);
487+
self.tcx.mk_clauses(&clauses.clauses)
488+
}
489+
463490
#[instrument(level = "debug", skip_all)]
464-
pub(crate) fn lower_ty_saving_user_provided_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> {
491+
pub(crate) fn lower_ty_saving_user_provided_ty(&self, hir_ty: &'tcx hir::Ty<'tcx>) -> Ty<'tcx> {
465492
let ty = self.lower_ty(hir_ty);
466493
debug!(?ty);
467494

compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs

+33-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,12 @@ pub(crate) struct FnCtxt<'a, 'tcx> {
114115

115116
pub(super) diverging_fallback_behavior: DivergingFallbackBehavior,
116117
pub(super) diverging_block_behavior: DivergingBlockBehavior,
118+
119+
/// Clauses that we lowered as part of the `impl_trait_in_bindings` feature.
120+
///
121+
/// These are stored here so we may collect them when canonicalizing user
122+
/// type ascriptions later.
123+
pub(super) trait_ascriptions: RefCell<ItemLocalMap<Vec<ty::Clause<'tcx>>>>,
117124
}
118125

119126
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -141,6 +148,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
141148
fallback_has_occurred: Cell::new(false),
142149
diverging_fallback_behavior,
143150
diverging_block_behavior,
151+
trait_ascriptions: Default::default(),
144152
}
145153
}
146154

@@ -252,6 +260,30 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
252260
}
253261
}
254262

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

compiler/rustc_hir_typeck/src/gather_locals.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,12 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
9292
Some(ref ty) => {
9393
let o_ty = self.fcx.lower_ty(ty);
9494

95-
let c_ty = self.fcx.infcx.canonicalize_user_type_annotation(ty::UserType::new(
96-
ty::UserTypeKind::Ty(o_ty.raw),
97-
));
95+
let c_ty = self.fcx.infcx.canonicalize_user_type_annotation(
96+
ty::UserType::new_with_bounds(
97+
ty::UserTypeKind::Ty(o_ty.raw),
98+
self.fcx.collect_impl_trait_clauses_from_hir_ty(ty),
99+
),
100+
);
98101
debug!("visit_local: ty.hir_id={:?} o_ty={:?} c_ty={:?}", ty.hir_id, o_ty, c_ty);
99102
self.fcx
100103
.typeck_results

compiler/rustc_middle/src/query/plumbing.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ macro_rules! define_callbacks {
323323
// Increase this limit if necessary, but do try to keep the size low if possible
324324
#[cfg(target_pointer_width = "64")]
325325
const _: () = {
326-
if mem::size_of::<Key<'static>>() > 80 {
326+
if mem::size_of::<Key<'static>>() > 88 {
327327
panic!("{}", concat!(
328328
"the query `",
329329
stringify!($name),

compiler/rustc_middle/src/ty/typeck_results.rs

+18-5
Original file line numberDiff line numberDiff line change
@@ -704,11 +704,18 @@ pub type CanonicalUserType<'tcx> = Canonical<'tcx, UserType<'tcx>>;
704704
#[derive(Eq, Hash, HashStable, TypeFoldable, TypeVisitable)]
705705
pub struct UserType<'tcx> {
706706
pub kind: UserTypeKind<'tcx>,
707+
pub bounds: ty::Clauses<'tcx>,
707708
}
708709

709710
impl<'tcx> UserType<'tcx> {
710711
pub fn new(kind: UserTypeKind<'tcx>) -> UserType<'tcx> {
711-
UserType { kind }
712+
UserType { kind, bounds: ty::ListWithCachedTypeInfo::empty() }
713+
}
714+
715+
/// A user type annotation with additional bounds that need to be enforced.
716+
/// These bounds are lowered from `impl Trait` in bindings.
717+
pub fn new_with_bounds(kind: UserTypeKind<'tcx>, bounds: ty::Clauses<'tcx>) -> UserType<'tcx> {
718+
UserType { kind, bounds }
712719
}
713720
}
714721

@@ -733,7 +740,9 @@ impl<'tcx> IsIdentity for CanonicalUserType<'tcx> {
733740
/// Returns `true` if this represents the generic parameters of the form `[?0, ?1, ?2]`,
734741
/// i.e., each thing is mapped to a canonical variable with the same index.
735742
fn is_identity(&self) -> bool {
736-
// TODO:
743+
if !self.value.bounds.is_empty() {
744+
return false;
745+
}
737746

738747
match self.value.kind {
739748
UserTypeKind::Ty(_) => false,
@@ -779,9 +788,13 @@ impl<'tcx> IsIdentity for CanonicalUserType<'tcx> {
779788

780789
impl<'tcx> std::fmt::Display for UserType<'tcx> {
781790
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
782-
// TODO:
783-
784-
self.kind.fmt(f)
791+
if self.bounds.is_empty() {
792+
self.kind.fmt(f)
793+
} else {
794+
self.kind.fmt(f)?;
795+
write!(f, " + ")?;
796+
std::fmt::Debug::fmt(&self.bounds, f)
797+
}
785798
}
786799
}
787800

compiler/rustc_passes/src/input_stats.rs

+1
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
371371
Tup,
372372
Path,
373373
OpaqueDef,
374+
TraitAscription,
374375
TraitObject,
375376
Typeof,
376377
Infer,

0 commit comments

Comments
 (0)