From de8660ab610f046bbdeb1a90f2c94cba1d3d3185 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 24 Jun 2019 09:15:07 +0100 Subject: [PATCH] typeck: merge opaque type inference logic This commit merges the logic used for opaque type type inference for impl Trait and non-impl Trait cases. This fixes an ICE where existential types used in the return types of functions would be allowed to have an out-of-scope generic type parameter. --- src/librustc/infer/opaque_types/mod.rs | 76 +++++++++- src/librustc_typeck/check/writeback.rs | 153 ++------------------ src/test/ui/impl-trait/issue-55872-1.rs | 22 +++ src/test/ui/impl-trait/issue-55872-1.stderr | 33 +++++ src/test/ui/impl-trait/issue-55872-2.rs | 20 +++ src/test/ui/impl-trait/issue-55872-2.stderr | 21 +++ src/test/ui/impl-trait/issue-55872.rs | 19 +++ src/test/ui/impl-trait/issue-55872.stderr | 12 ++ 8 files changed, 217 insertions(+), 139 deletions(-) create mode 100644 src/test/ui/impl-trait/issue-55872-1.rs create mode 100644 src/test/ui/impl-trait/issue-55872-1.stderr create mode 100644 src/test/ui/impl-trait/issue-55872-2.rs create mode 100644 src/test/ui/impl-trait/issue-55872-2.stderr create mode 100644 src/test/ui/impl-trait/issue-55872.rs create mode 100644 src/test/ui/impl-trait/issue-55872.stderr diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index f43e3fa0b7787..9c105b6b891f9 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -4,6 +4,7 @@ use crate::hir::Node; use crate::infer::outlives::free_region_map::FreeRegionRelations; use crate::infer::{self, InferCtxt, InferOk, TypeVariableOrigin, TypeVariableOriginKind}; use crate::middle::region; +use crate::mir::interpret::ConstValue; use crate::traits::{self, PredicateObligation}; use crate::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder, TypeVisitor}; use crate::ty::subst::{InternalSubsts, Kind, SubstsRef, UnpackedKind}; @@ -553,6 +554,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { def_id: DefId, opaque_defn: &OpaqueTypeDecl<'tcx>, instantiated_ty: Ty<'tcx>, + span: Span, ) -> Ty<'tcx> { debug!( "infer_opaque_definition_from_instantiation(def_id={:?}, instantiated_ty={:?})", @@ -584,6 +586,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { def_id, map, instantiated_ty, + span, )); debug!("infer_opaque_definition_from_instantiation: definition_ty={:?}", definition_ty); @@ -761,6 +764,9 @@ struct ReverseMapper<'tcx> { /// initially `Some`, set to `None` once error has been reported hidden_ty: Option>, + + /// Span of function being checked. + span: Span, } impl ReverseMapper<'tcx> { @@ -770,6 +776,7 @@ impl ReverseMapper<'tcx> { opaque_type_def_id: DefId, map: FxHashMap, Kind<'tcx>>, hidden_ty: Ty<'tcx>, + span: Span, ) -> Self { Self { tcx, @@ -778,6 +785,7 @@ impl ReverseMapper<'tcx> { map, map_missing_regions_to_empty: false, hidden_ty: Some(hidden_ty), + span, } } @@ -812,10 +820,11 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { _ => { } } + let generics = self.tcx().generics_of(self.opaque_type_def_id); match self.map.get(&r.into()).map(|k| k.unpack()) { Some(UnpackedKind::Lifetime(r1)) => r1, Some(u) => panic!("region mapped to unexpected kind: {:?}", u), - None => { + None if generics.parent.is_some() => { if !self.map_missing_regions_to_empty && !self.tainted_by_errors { if let Some(hidden_ty) = self.hidden_ty.take() { unexpected_hidden_region_diagnostic( @@ -829,6 +838,21 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { } self.tcx.lifetimes.re_empty } + None => { + self.tcx.sess + .struct_span_err( + self.span, + "non-defining existential type use in defining scope" + ) + .span_label( + self.span, + format!("lifetime `{}` is part of concrete type but not used in \ + parameter list of existential type", r), + ) + .emit(); + + self.tcx().global_tcx().mk_region(ty::ReStatic) + }, } } @@ -890,9 +914,59 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { self.tcx.mk_generator(def_id, ty::GeneratorSubsts { substs }, movability) } + ty::Param(..) => { + // Look it up in the substitution list. + match self.map.get(&ty.into()).map(|k| k.unpack()) { + // Found it in the substitution list; replace with the parameter from the + // existential type. + Some(UnpackedKind::Type(t1)) => t1, + Some(u) => panic!("type mapped to unexpected kind: {:?}", u), + None => { + self.tcx.sess + .struct_span_err( + self.span, + &format!("type parameter `{}` is part of concrete type but not \ + used in parameter list for existential type", ty), + ) + .emit(); + + self.tcx().types.err + } + } + } + _ => ty.super_fold_with(self), } } + + fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + trace!("checking const {:?}", ct); + // Find a const parameter + match ct.val { + ConstValue::Param(..) => { + // Look it up in the substitution list. + match self.map.get(&ct.into()).map(|k| k.unpack()) { + // Found it in the substitution list, replace with the parameter from the + // existential type. + Some(UnpackedKind::Const(c1)) => c1, + Some(u) => panic!("const mapped to unexpected kind: {:?}", u), + None => { + self.tcx.sess + .struct_span_err( + self.span, + &format!("const parameter `{}` is part of concrete type but not \ + used in parameter list for existential type", ct) + ) + .emit(); + + self.tcx().consts.err + } + } + } + + _ => ct, + } + } } struct Instantiator<'a, 'tcx> { diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index 28711e32a4c51..2a33b47c93a61 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -9,10 +9,8 @@ use rustc::hir::def_id::{DefId, DefIndex}; use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc::infer::InferCtxt; use rustc::ty::adjustment::{Adjust, Adjustment, PointerCast}; -use rustc::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder}; -use rustc::ty::subst::UnpackedKind; +use rustc::ty::fold::{TypeFoldable, TypeFolder}; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::mir::interpret::ConstValue; use rustc::util::nodemap::DefIdSet; use rustc_data_structures::sync::Lrc; use std::mem; @@ -440,141 +438,20 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { debug_assert!(!instantiated_ty.has_escaping_bound_vars()); - let generics = self.tcx().generics_of(def_id); - - let definition_ty = if generics.parent.is_some() { - // `impl Trait` - self.fcx.infer_opaque_definition_from_instantiation( - def_id, - opaque_defn, - instantiated_ty, - ) - } else { - // Prevent: - // * `fn foo() -> Foo` - // * `fn foo() -> Foo` - // from being defining. - - // Also replace all generic params with the ones from the existential type - // definition so that - // ```rust - // existential type Foo: 'static; - // fn foo() -> Foo { .. } - // ``` - // figures out the concrete type with `U`, but the stored type is with `T`. - instantiated_ty.fold_with(&mut BottomUpFolder { - tcx: self.tcx().global_tcx(), - ty_op: |ty| { - trace!("checking type {:?}", ty); - // Find a type parameter. - if let ty::Param(..) = ty.sty { - // Look it up in the substitution list. - assert_eq!(opaque_defn.substs.len(), generics.params.len()); - for (subst, param) in opaque_defn.substs.iter().zip(&generics.params) { - if let UnpackedKind::Type(subst) = subst.unpack() { - if subst == ty { - // Found it in the substitution list; replace with the - // parameter from the existential type. - return self.tcx() - .global_tcx() - .mk_ty_param(param.index, param.name); - } - } - } - self.tcx() - .sess - .struct_span_err( - span, - &format!( - "type parameter `{}` is part of concrete type but not used \ - in parameter list for existential type", - ty, - ), - ) - .emit(); - return self.tcx().types.err; - } - ty - }, - lt_op: |region| { - match region { - // Skip static and bound regions: they don't require substitution. - ty::ReStatic | ty::ReLateBound(..) => region, - _ => { - trace!("checking {:?}", region); - for (subst, p) in opaque_defn.substs.iter().zip(&generics.params) { - if let UnpackedKind::Lifetime(subst) = subst.unpack() { - if subst == region { - // Found it in the substitution list; replace with the - // parameter from the existential type. - let reg = ty::EarlyBoundRegion { - def_id: p.def_id, - index: p.index, - name: p.name, - }; - trace!("replace {:?} with {:?}", region, reg); - return self.tcx() - .global_tcx() - .mk_region(ty::ReEarlyBound(reg)); - } - } - } - trace!("opaque_defn: {:#?}", opaque_defn); - trace!("generics: {:#?}", generics); - self.tcx() - .sess - .struct_span_err( - span, - "non-defining existential type use in defining scope", - ) - .span_label( - span, - format!( - "lifetime `{}` is part of concrete type but not used \ - in parameter list of existential type", - region, - ), - ) - .emit(); - self.tcx().global_tcx().mk_region(ty::ReStatic) - } - } - }, - ct_op: |ct| { - trace!("checking const {:?}", ct); - // Find a const parameter - if let ConstValue::Param(..) = ct.val { - // look it up in the substitution list - assert_eq!(opaque_defn.substs.len(), generics.params.len()); - for (subst, param) in opaque_defn.substs.iter() - .zip(&generics.params) { - if let UnpackedKind::Const(subst) = subst.unpack() { - if subst == ct { - // found it in the substitution list, replace with the - // parameter from the existential type - return self.tcx() - .global_tcx() - .mk_const_param(param.index, param.name, ct.ty); - } - } - } - self.tcx() - .sess - .struct_span_err( - span, - &format!( - "const parameter `{}` is part of concrete type but not \ - used in parameter list for existential type", - ct, - ), - ) - .emit(); - return self.tcx().consts.err; - } - ct - } - }) - }; + // Prevent: + // * `fn foo() -> Foo` + // * `fn foo() -> Foo` + // from being defining. + + // Also replace all generic params with the ones from the existential type + // definition so that + // ```rust + // existential type Foo: 'static; + // fn foo() -> Foo { .. } + // ``` + // figures out the concrete type with `U`, but the stored type is with `T`. + let definition_ty = self.fcx.infer_opaque_definition_from_instantiation( + def_id, opaque_defn, instantiated_ty, span); if let ty::Opaque(defin_ty_def_id, _substs) = definition_ty.sty { if def_id == defin_ty_def_id { diff --git a/src/test/ui/impl-trait/issue-55872-1.rs b/src/test/ui/impl-trait/issue-55872-1.rs new file mode 100644 index 0000000000000..5095b26f8a4e0 --- /dev/null +++ b/src/test/ui/impl-trait/issue-55872-1.rs @@ -0,0 +1,22 @@ +// ignore-tidy-linelength +#![feature(existential_type)] + +pub trait Bar +{ + type E: Copy; + + fn foo() -> Self::E; +} + +impl Bar for S { + existential type E: Copy; + //~^ ERROR the trait bound `S: std::marker::Copy` is not satisfied in `(S, T)` [E0277] + //~^^ ERROR the trait bound `T: std::marker::Copy` is not satisfied in `(S, T)` [E0277] + + fn foo() -> Self::E { + //~^ ERROR type parameter `T` is part of concrete type but not used in parameter list for existential type + (S::default(), T::default()) + } +} + +fn main() {} diff --git a/src/test/ui/impl-trait/issue-55872-1.stderr b/src/test/ui/impl-trait/issue-55872-1.stderr new file mode 100644 index 0000000000000..04b4d2d4a50a5 --- /dev/null +++ b/src/test/ui/impl-trait/issue-55872-1.stderr @@ -0,0 +1,33 @@ +error[E0277]: the trait bound `S: std::marker::Copy` is not satisfied in `(S, T)` + --> $DIR/issue-55872-1.rs:12:5 + | +LL | existential type E: Copy; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ within `(S, T)`, the trait `std::marker::Copy` is not implemented for `S` + | + = help: consider adding a `where S: std::marker::Copy` bound + = note: required because it appears within the type `(S, T)` + = note: the return type of a function must have a statically known size + +error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied in `(S, T)` + --> $DIR/issue-55872-1.rs:12:5 + | +LL | existential type E: Copy; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ within `(S, T)`, the trait `std::marker::Copy` is not implemented for `T` + | + = help: consider adding a `where T: std::marker::Copy` bound + = note: required because it appears within the type `(S, T)` + = note: the return type of a function must have a statically known size + +error: type parameter `T` is part of concrete type but not used in parameter list for existential type + --> $DIR/issue-55872-1.rs:16:37 + | +LL | fn foo() -> Self::E { + | _____________________________________^ +LL | | +LL | | (S::default(), T::default()) +LL | | } + | |_____^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/impl-trait/issue-55872-2.rs b/src/test/ui/impl-trait/issue-55872-2.rs new file mode 100644 index 0000000000000..2bdeb14bdc3e6 --- /dev/null +++ b/src/test/ui/impl-trait/issue-55872-2.rs @@ -0,0 +1,20 @@ +// edition:2018 +// ignore-tidy-linelength +#![feature(async_await, existential_type)] + +pub trait Bar { + type E: Copy; + + fn foo() -> Self::E; +} + +impl Bar for S { + existential type E: Copy; + //~^ ERROR the trait bound `impl std::future::Future: std::marker::Copy` is not satisfied [E0277] + fn foo() -> Self::E { + //~^ ERROR type parameter `T` is part of concrete type but not used in parameter list for existential type + async {} + } +} + +fn main() {} diff --git a/src/test/ui/impl-trait/issue-55872-2.stderr b/src/test/ui/impl-trait/issue-55872-2.stderr new file mode 100644 index 0000000000000..2505a82ee23cb --- /dev/null +++ b/src/test/ui/impl-trait/issue-55872-2.stderr @@ -0,0 +1,21 @@ +error[E0277]: the trait bound `impl std::future::Future: std::marker::Copy` is not satisfied + --> $DIR/issue-55872-2.rs:12:5 + | +LL | existential type E: Copy; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `impl std::future::Future` + | + = note: the return type of a function must have a statically known size + +error: type parameter `T` is part of concrete type but not used in parameter list for existential type + --> $DIR/issue-55872-2.rs:14:28 + | +LL | fn foo() -> Self::E { + | ____________________________^ +LL | | +LL | | async {} +LL | | } + | |_____^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/impl-trait/issue-55872.rs b/src/test/ui/impl-trait/issue-55872.rs new file mode 100644 index 0000000000000..95604545c37f1 --- /dev/null +++ b/src/test/ui/impl-trait/issue-55872.rs @@ -0,0 +1,19 @@ +// ignore-tidy-linelength +#![feature(existential_type)] + +pub trait Bar { + type E: Copy; + + fn foo() -> Self::E; +} + +impl Bar for S { + existential type E: Copy; + + fn foo() -> Self::E { + //~^ ERROR type parameter `T` is part of concrete type but not used in parameter list for existential type + || () + } +} + +fn main() {} diff --git a/src/test/ui/impl-trait/issue-55872.stderr b/src/test/ui/impl-trait/issue-55872.stderr new file mode 100644 index 0000000000000..487f276e317e9 --- /dev/null +++ b/src/test/ui/impl-trait/issue-55872.stderr @@ -0,0 +1,12 @@ +error: type parameter `T` is part of concrete type but not used in parameter list for existential type + --> $DIR/issue-55872.rs:13:28 + | +LL | fn foo() -> Self::E { + | ____________________________^ +LL | | +LL | | || () +LL | | } + | |_____^ + +error: aborting due to previous error +