From 51a65503e3e66e64a9522b2a2ab1b275d569542b Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 7 Jun 2020 19:52:05 +0100 Subject: [PATCH] Document some opaque types code --- src/librustc_resolve/late/lifetimes.rs | 4 +- src/librustc_trait_selection/opaque_types.rs | 14 +- src/librustc_typeck/check/wfcheck.rs | 181 +++++++++---------- src/librustc_typeck/collect/type_of.rs | 10 + 4 files changed, 109 insertions(+), 100 deletions(-) diff --git a/src/librustc_resolve/late/lifetimes.rs b/src/librustc_resolve/late/lifetimes.rs index 0af49075c6758..5bbf8703f0b60 100644 --- a/src/librustc_resolve/late/lifetimes.rs +++ b/src/librustc_resolve/late/lifetimes.rs @@ -258,8 +258,8 @@ enum Elide { Exact(Region), /// Less or more than one lifetime were found, error on unspecified. Error(Vec), - /// Forbid lifetime elision inside of a larger scope that does. For - /// example, in let position impl trait. + /// Forbid lifetime elision inside of a larger scope where it would be + /// permitted. For example, in let position impl trait. Forbid, } diff --git a/src/librustc_trait_selection/opaque_types.rs b/src/librustc_trait_selection/opaque_types.rs index e1f0c579a3c36..d53a0ec9ef884 100644 --- a/src/librustc_trait_selection/opaque_types.rs +++ b/src/librustc_trait_selection/opaque_types.rs @@ -407,12 +407,20 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let first_own_region = match opaque_defn.origin { hir::OpaqueTyOrigin::FnReturn | hir::OpaqueTyOrigin::AsyncFn => { - // For these opaque types, only the item's own lifetime - // parameters are considered. + // We lower + // + // fn foo<'l0..'ln>() -> impl Trait<'l0..'lm> + // + // into + // + // type foo::<'p0..'pn>::Foo<'q0..'qm> + // fn foo() -> foo::<'static..'static>::Foo<'l0..'lm>. + // + // For these types we onlt iterate over `'l0..lm` below. tcx.generics_of(def_id).parent_count } // These opaque type inherit all lifetime parameters from their - // parent. + // parent, so we have to check them all. hir::OpaqueTyOrigin::Binding | hir::OpaqueTyOrigin::Misc => 0, }; diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index c0be1d42d134c..f3297ed674347 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -801,18 +801,14 @@ fn check_where_clauses<'tcx, 'fcx>( traits::Obligation::new(cause, fcx.param_env, pred) }); - let mut predicates = predicates.instantiate_identity(fcx.tcx); + let predicates = predicates.instantiate_identity(fcx.tcx); if let Some((mut return_ty, span)) = return_ty { if return_ty.has_infer_types_or_consts() { fcx.select_obligations_where_possible(false, |_| {}); return_ty = fcx.resolve_vars_if_possible(&return_ty); } - let opaque_types = check_opaque_types(tcx, fcx, def_id.expect_local(), span, return_ty); - for _ in 0..opaque_types.len() { - predicates.spans.push(span); - } - predicates.predicates.extend(opaque_types); + check_opaque_types(tcx, fcx, def_id.expect_local(), span, return_ty); } let predicates = fcx.normalize_associated_types_in(span, &predicates); @@ -884,113 +880,109 @@ fn check_opaque_types<'fcx, 'tcx>( fn_def_id: LocalDefId, span: Span, ty: Ty<'tcx>, -) -> Vec> { +) { trace!("check_opaque_types(ty={:?})", ty); - let mut substituted_predicates = Vec::new(); ty.fold_with(&mut ty::fold::BottomUpFolder { tcx: fcx.tcx, ty_op: |ty| { if let ty::Opaque(def_id, substs) = ty.kind { trace!("check_opaque_types: opaque_ty, {:?}, {:?}", def_id, substs); let generics = tcx.generics_of(def_id); - // Only check named `impl Trait` types defined in this crate. - if !def_id.is_local() { + + let opaque_hir_id = if let Some(local_id) = def_id.as_local() { + tcx.hir().as_local_hir_id(local_id) + } else { + // Opaque types from other crates won't have defining uses in this crate. return ty; - } - let opaque_hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); + }; if let hir::ItemKind::OpaqueTy(hir::OpaqueTy { impl_trait_fn: Some(_), .. }) = tcx.hir().expect_item(opaque_hir_id).kind { - // Don't check return position impl trait. + // No need to check return position impl trait (RPIT) + // because for type and const parameters they are correct + // by construction: we convert + // + // fn foo() -> impl Trait + // + // into + // + // type Foo + // fn foo() -> Foo. + // + // For lifetime parameters we convert + // + // fn foo<'l0..'ln>() -> impl Trait<'l0..'lm> + // + // into + // + // type foo::<'p0..'pn>::Foo<'q0..'qm> + // fn foo() -> foo::<'static..'static>::Foo<'l0..'lm>. + // + // which would error here on all of the `'static` args. + return ty; + } + if !may_define_opaque_type(tcx, fn_def_id, opaque_hir_id) { return ty; } - if may_define_opaque_type(tcx, fn_def_id, opaque_hir_id) { - trace!("check_opaque_types: may define, generics={:#?}", generics); - let mut seen_params: FxHashMap<_, Vec<_>> = FxHashMap::default(); - for (i, arg) in substs.iter().enumerate() { - let arg_is_param = match arg.unpack() { - GenericArgKind::Type(ty) => matches!(ty.kind, ty::Param(_)), - - GenericArgKind::Lifetime(region) => { - if let ty::ReStatic = region { - tcx.sess - .struct_span_err( - span, - "non-defining opaque type use in defining scope", - ) - .span_label( - tcx.def_span(generics.param_at(i, tcx).def_id), - "cannot use static lifetime; use a bound lifetime \ + trace!("check_opaque_types: may define, generics={:#?}", generics); + let mut seen_params: FxHashMap<_, Vec<_>> = FxHashMap::default(); + for (i, arg) in substs.iter().enumerate() { + let arg_is_param = match arg.unpack() { + GenericArgKind::Type(ty) => matches!(ty.kind, ty::Param(_)), + + GenericArgKind::Lifetime(region) => { + if let ty::ReStatic = region { + tcx.sess + .struct_span_err( + span, + "non-defining opaque type use in defining scope", + ) + .span_label( + tcx.def_span(generics.param_at(i, tcx).def_id), + "cannot use static lifetime; use a bound lifetime \ instead or remove the lifetime parameter from the \ opaque type", - ) - .emit(); - continue; - } - - true + ) + .emit(); + continue; } - GenericArgKind::Const(ct) => matches!(ct.val, ty::ConstKind::Param(_)), - }; - - if arg_is_param { - seen_params.entry(arg).or_default().push(i); - } else { - // Prevent `fn foo() -> Foo` from being defining. - let opaque_param = generics.param_at(i, tcx); - tcx.sess - .struct_span_err( - span, - "non-defining opaque type use in defining scope", - ) - .span_note( - tcx.def_span(opaque_param.def_id), - &format!( - "used non-generic {} `{}` for generic parameter", - opaque_param.kind.descr(), - arg, - ), - ) - .emit(); - } - } // for (arg, param) - - for (_, indices) in seen_params { - if indices.len() > 1 { - let descr = generics.param_at(indices[0], tcx).kind.descr(); - let spans: Vec<_> = indices - .into_iter() - .map(|i| tcx.def_span(generics.param_at(i, tcx).def_id)) - .collect(); - tcx.sess - .struct_span_err( - span, - "non-defining opaque type use in defining scope", - ) - .span_note(spans, &format!("{} used multiple times", descr)) - .emit(); + true } + + GenericArgKind::Const(ct) => matches!(ct.val, ty::ConstKind::Param(_)), + }; + + if arg_is_param { + seen_params.entry(arg).or_default().push(i); + } else { + // Prevent `fn foo() -> Foo` from being defining. + let opaque_param = generics.param_at(i, tcx); + tcx.sess + .struct_span_err(span, "non-defining opaque type use in defining scope") + .span_note( + tcx.def_span(opaque_param.def_id), + &format!( + "used non-generic {} `{}` for generic parameter", + opaque_param.kind.descr(), + arg, + ), + ) + .emit(); } - } // if may_define_opaque_type - - // Now register the bounds on the parameters of the opaque type - // so the parameters given by the function need to fulfill them. - // - // type Foo = impl Baz + 'static; - // fn foo() -> Foo { .. *} - // - // becomes - // - // type Foo = impl Baz + 'static; - // fn foo() -> Foo { .. *} - let predicates = tcx.predicates_of(def_id); - trace!("check_opaque_types: may define, predicates={:#?}", predicates,); - for &(pred, _) in predicates.predicates { - let substituted_pred = pred.subst(fcx.tcx, substs); - // Avoid duplication of predicates that contain no parameters, for example. - if !predicates.predicates.iter().any(|&(p, _)| p == substituted_pred) { - substituted_predicates.push(substituted_pred); + } // for (arg, param) + + for (_, indices) in seen_params { + if indices.len() > 1 { + let descr = generics.param_at(indices[0], tcx).kind.descr(); + let spans: Vec<_> = indices + .into_iter() + .map(|i| tcx.def_span(generics.param_at(i, tcx).def_id)) + .collect(); + tcx.sess + .struct_span_err(span, "non-defining opaque type use in defining scope") + .span_note(spans, &format!("{} used multiple times", descr)) + .emit(); } } } // if let Opaque @@ -999,7 +991,6 @@ fn check_opaque_types<'fcx, 'tcx>( lt_op: |lt| lt, ct_op: |ct| ct, }); - substituted_predicates } const HELP_FOR_SELF_TYPE: &str = "consider changing to `self`, `&self`, `&mut self`, `self: Box`, \ diff --git a/src/librustc_typeck/collect/type_of.rs b/src/librustc_typeck/collect/type_of.rs index 0ef561fdbbb29..549a20531e299 100644 --- a/src/librustc_typeck/collect/type_of.rs +++ b/src/librustc_typeck/collect/type_of.rs @@ -573,6 +573,16 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { } } +/// Retrieve the inferred concrete type for let position impl trait. +/// +/// This is different to other kinds of impl trait because: +/// +/// 1. We know which function contains the defining use (the function that +/// contains the let statement) +/// 2. We do not currently allow (free) lifetimes in the return type. `let` +/// statements in some statically unreachable code are removed from the MIR +/// by the time we borrow check, and it's not clear how we should handle +/// those. fn let_position_impl_trait_type(tcx: TyCtxt<'_>, opaque_ty_id: LocalDefId) -> Ty<'_> { let scope = tcx.hir().get_defining_scope(tcx.hir().as_local_hir_id(opaque_ty_id)); let scope_def_id = tcx.hir().local_def_id(scope);