Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support using Self or projections inside an RPIT/async fn #103491

Merged
merged 15 commits into from
Nov 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 6 additions & 12 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
use rustc_hir::definitions::DefPathData;
use rustc_hir::{ConstArg, GenericArg, ItemLocalId, ParamName, TraitCandidate};
use rustc_index::vec::{Idx, IndexVec};
use rustc_middle::span_bug;
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_session::parse::feature_err;
use rustc_span::hygiene::MacroKind;
use rustc_span::source_map::DesugaringKind;
Expand Down Expand Up @@ -1457,17 +1457,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// frequently opened issues show.
let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None);

let opaque_ty_def_id = match origin {
hir::OpaqueTyOrigin::TyAlias => self.create_def(
self.current_hir_id_owner.def_id,
opaque_ty_node_id,
DefPathData::ImplTrait,
),
hir::OpaqueTyOrigin::FnReturn(fn_def_id) => {
self.create_def(fn_def_id, opaque_ty_node_id, DefPathData::ImplTrait)
}
hir::OpaqueTyOrigin::AsyncFn(..) => bug!("unreachable"),
};
let opaque_ty_def_id = self.create_def(
self.current_hir_id_owner.def_id,
opaque_ty_node_id,
DefPathData::ImplTrait,
);
debug!(?opaque_ty_def_id);

// Contains the new lifetime definitions created for the TAIT (if any).
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_borrowck/src/member_constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use std::ops::Index;

/// Compactly stores a set of `R0 member of [R1...Rn]` constraints,
/// indexed by the region `R0`.
#[derive(Debug)]
pub(crate) struct MemberConstraintSet<'tcx, R>
where
R: Copy + Eq,
Expand All @@ -31,6 +32,7 @@ where
}

/// Represents a `R0 member of [R1..Rn]` constraint
#[derive(Debug)]
pub(crate) struct NllMemberConstraint<'tcx> {
next_constraint: Option<NllMemberConstraintIndex>,

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_borrowck/src/region_infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ pub struct RegionInferenceContext<'tcx> {
/// adds a new lower bound to the SCC it is analyzing: so you wind up
/// with `'R: 'O` where `'R` is the pick-region and `'O` is the
/// minimal viable option.
#[derive(Debug)]
pub(crate) struct AppliedMemberConstraint {
/// The SCC that was affected. (The "member region".)
///
Expand Down
48 changes: 37 additions & 11 deletions compiler/rustc_borrowck/src/region_infer/opaque_types.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
use rustc_data_structures::vec_map::VecMap;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::OpaqueTyOrigin;
Expand Down Expand Up @@ -63,17 +63,21 @@ impl<'tcx> RegionInferenceContext<'tcx> {
opaque_ty_decls: VecMap<OpaqueTypeKey<'tcx>, (OpaqueHiddenType<'tcx>, OpaqueTyOrigin)>,
) -> VecMap<LocalDefId, OpaqueHiddenType<'tcx>> {
let mut result: VecMap<LocalDefId, OpaqueHiddenType<'tcx>> = VecMap::new();

let member_constraints: FxHashMap<_, _> = self
.member_constraints
.all_indices()
.map(|ci| (self.member_constraints[ci].key, ci))
.collect();
debug!(?member_constraints);

for (opaque_type_key, (concrete_type, origin)) in opaque_ty_decls {
let substs = opaque_type_key.substs;
debug!(?concrete_type, ?substs);

let mut subst_regions = vec![self.universal_regions.fr_static];
let universal_substs = infcx.tcx.fold_regions(substs, |region, _| {
if let ty::RePlaceholder(..) = region.kind() {
// Higher kinded regions don't need remapping, they don't refer to anything outside of this the substs.
return region;
}
let vid = self.to_region_vid(region);

let to_universal_region = |vid, subst_regions: &mut Vec<_>| {
trace!(?vid);
let scc = self.constraint_sccs.scc(vid);
trace!(?scc);
Expand All @@ -94,10 +98,33 @@ impl<'tcx> RegionInferenceContext<'tcx> {
infcx.tcx.lifetimes.re_static
}
}
};

// Start by inserting universal regions from the member_constraint choice regions.
// This will ensure they get precedence when folding the regions in the concrete type.
if let Some(&ci) = member_constraints.get(&opaque_type_key) {
for &vid in self.member_constraints.choice_regions(ci) {
to_universal_region(vid, &mut subst_regions);
}
}
debug!(?subst_regions);

// Next, insert universal regions from substs, so we can translate regions that appear
// in them but are not subject to member constraints, for instance closure substs.
let universal_substs = infcx.tcx.fold_regions(substs, |region, _| {
if let ty::RePlaceholder(..) = region.kind() {
// Higher kinded regions don't need remapping, they don't refer to anything outside of this the substs.
return region;
}
let vid = self.to_region_vid(region);
to_universal_region(vid, &mut subst_regions)
});
debug!(?universal_substs);
debug!(?subst_regions);

subst_regions.sort();
subst_regions.dedup();
// Deduplicate the set of regions while keeping the chosen order.
let subst_regions = subst_regions.into_iter().collect::<FxIndexSet<_>>();
debug!(?subst_regions);

let universal_concrete_type =
infcx.tcx.fold_regions(concrete_type, |region, _| match *region {
Expand All @@ -108,8 +135,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
.unwrap_or(infcx.tcx.lifetimes.re_erased),
_ => region,
});

debug!(?universal_concrete_type, ?universal_substs);
debug!(?universal_concrete_type);

let opaque_type_key =
OpaqueTypeKey { def_id: opaque_type_key.def_id, substs: universal_substs };
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_error_codes/src/error_codes/E0760.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#### Note: this error code is no longer emitted by the compiler.

`async fn`/`impl trait` return type cannot contain a projection
or `Self` that references lifetimes from a parent scope.

Erroneous code example:

```compile_fail,E0760,edition2018
```compile_fail,edition2018
struct S<'a>(&'a i32);

impl<'a> S<'a> {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,8 @@ declare_features! (
(active, if_let_guard, "1.47.0", Some(51114), None),
/// Allows `impl Trait` as output type in `Fn` traits in return position of functions.
(active, impl_trait_in_fn_trait_return, "1.64.0", Some(99697), None),
/// Allows referencing `Self` and projections in impl-trait.
(active, impl_trait_projections, "CURRENT_RUSTC_VERSION", Some(103532), None),
/// Allows using imported `main` function
(active, imported_main, "1.53.0", Some(28937), None),
/// Allows associated types in inherent impls.
Expand Down
33 changes: 5 additions & 28 deletions compiler/rustc_hir_analysis/src/astconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2767,35 +2767,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let substs = InternalSubsts::for_item(tcx, def_id, |param, _| {
if let Some(i) = (param.index as usize).checked_sub(generics.parent_count) {
// Our own parameters are the resolved lifetimes.
if let GenericParamDefKind::Lifetime = param.kind {
if let hir::GenericArg::Lifetime(lifetime) = &lifetimes[i] {
self.ast_region_to_region(lifetime, None).into()
} else {
bug!()
}
} else {
bug!()
}
let GenericParamDefKind::Lifetime { .. } = param.kind else { bug!() };
let hir::GenericArg::Lifetime(lifetime) = &lifetimes[i] else { bug!() };
self.ast_region_to_region(lifetime, None).into()
} else {
match param.kind {
// For RPIT (return position impl trait), only lifetimes
// mentioned in the impl Trait predicate are captured by
// the opaque type, so the lifetime parameters from the
// parent item need to be replaced with `'static`.
//
// For `impl Trait` in the types of statics, constants,
// locals and type aliases. These capture all parent
// lifetimes, so they can use their identity subst.
GenericParamDefKind::Lifetime
if matches!(
origin,
hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..)
) =>
{
tcx.lifetimes.re_static.into()
}
_ => tcx.mk_param_from_def(param),
}
tcx.mk_param_from_def(param)
}
});
debug!("impl_trait_ty_to_ty: substs={:?}", substs);
Expand Down Expand Up @@ -2972,6 +2948,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
Some(tcx.liberate_late_bound_regions(fn_hir_id.expect_owner().to_def_id(), ty))
}

#[instrument(level = "trace", skip(self, generate_err))]
fn validate_late_bound_regions(
&self,
constrained_regions: FxHashSet<ty::BoundRegionKind>,
Expand Down
91 changes: 41 additions & 50 deletions compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::Visitor;
use rustc_hir::{ItemKind, Node, PathSegment};
use rustc_infer::infer::opaque_types::ConstrainOpaqueTypeRegionVisitor;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{DefiningAnchor, RegionVariableOrigin, TyCtxtInferExt};
use rustc_infer::traits::Obligation;
Expand Down Expand Up @@ -231,7 +232,9 @@ fn check_opaque<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
let substs = InternalSubsts::identity_for_item(tcx, item.owner_id.to_def_id());
let span = tcx.def_span(item.owner_id.def_id);

check_opaque_for_inheriting_lifetimes(tcx, item.owner_id.def_id, span);
if !tcx.features().impl_trait_projections {
check_opaque_for_inheriting_lifetimes(tcx, item.owner_id.def_id, span);
}
if tcx.type_of(item.owner_id.def_id).references_error() {
return;
}
Expand All @@ -240,6 +243,7 @@ fn check_opaque<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
}
check_opaque_meets_bounds(tcx, item.owner_id.def_id, substs, span, &origin);
}

/// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result
/// in "inheriting lifetimes".
#[instrument(level = "debug", skip(tcx, span))]
Expand All @@ -251,52 +255,37 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>(
let item = tcx.hir().expect_item(def_id);
debug!(?item, ?span);

struct FoundParentLifetime;
struct FindParentLifetimeVisitor<'tcx>(&'tcx ty::Generics);
impl<'tcx> ty::visit::TypeVisitor<'tcx> for FindParentLifetimeVisitor<'tcx> {
type BreakTy = FoundParentLifetime;

fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
debug!("FindParentLifetimeVisitor: r={:?}", r);
if let ty::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = *r {
if index < self.0.parent_count as u32 {
return ControlFlow::Break(FoundParentLifetime);
} else {
return ControlFlow::CONTINUE;
}
}

r.super_visit_with(self)
}

fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
if let ty::ConstKind::Unevaluated(..) = c.kind() {
// FIXME(#72219) We currently don't detect lifetimes within substs
// which would violate this check. Even though the particular substitution is not used
// within the const, this should still be fixed.
return ControlFlow::CONTINUE;
}
c.super_visit_with(self)
}
}

struct ProhibitOpaqueVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
opaque_identity_ty: Ty<'tcx>,
generics: &'tcx ty::Generics,
parent_count: u32,
references_parent_regions: bool,
selftys: Vec<(Span, Option<String>)>,
}

impl<'tcx> ty::visit::TypeVisitor<'tcx> for ProhibitOpaqueVisitor<'tcx> {
type BreakTy = Ty<'tcx>;

fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
debug!("check_opaque_for_inheriting_lifetimes: (visit_ty) t={:?}", t);
debug!(?t, "root_visit_ty");
if t == self.opaque_identity_ty {
ControlFlow::CONTINUE
} else {
t.super_visit_with(&mut FindParentLifetimeVisitor(self.generics))
.map_break(|FoundParentLifetime| t)
t.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
tcx: self.tcx,
op: |region| {
if let ty::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = *region
&& index < self.parent_count
{
self.references_parent_regions= true;
}
},
});
if self.references_parent_regions {
ControlFlow::Break(t)
} else {
ControlFlow::CONTINUE
}
}
}
}
Expand Down Expand Up @@ -329,26 +318,27 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>(

if let ItemKind::OpaqueTy(hir::OpaqueTy {
origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..),
in_trait,
..
}) = item.kind
{
let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id());
let opaque_identity_ty = if in_trait {
tcx.mk_projection(def_id.to_def_id(), substs)
} else {
tcx.mk_opaque(def_id.to_def_id(), substs)
};
let mut visitor = ProhibitOpaqueVisitor {
opaque_identity_ty: tcx.mk_opaque(
def_id.to_def_id(),
InternalSubsts::identity_for_item(tcx, def_id.to_def_id()),
),
generics: tcx.generics_of(def_id),
opaque_identity_ty,
parent_count: tcx.generics_of(def_id).parent_count as u32,
references_parent_regions: false,
tcx,
selftys: vec![],
};
let prohibit_opaque = tcx
.explicit_item_bounds(def_id)
.iter()
.try_for_each(|(predicate, _)| predicate.visit_with(&mut visitor));
debug!(
"check_opaque_for_inheriting_lifetimes: prohibit_opaque={:?}, visitor.opaque_identity_ty={:?}, visitor.generics={:?}",
prohibit_opaque, visitor.opaque_identity_ty, visitor.generics
);

if let Some(ty) = prohibit_opaque.break_value() {
visitor.visit_item(&item);
Expand All @@ -359,15 +349,16 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>(
_ => unreachable!(),
};

let mut err = struct_span_err!(
tcx.sess,
let mut err = feature_err(
&tcx.sess.parse_sess,
sym::impl_trait_projections,
span,
E0760,
"`{}` return type cannot contain a projection or `Self` that references lifetimes from \
a parent scope",
if is_async { "async fn" } else { "impl Trait" },
&format!(
"`{}` return type cannot contain a projection or `Self` that references \
lifetimes from a parent scope",
if is_async { "async fn" } else { "impl Trait" },
),
);

for (span, name) in visitor.selftys {
err.span_suggestion(
span,
Expand Down
Loading