typeck: disallow impl Trait
outside of return types of functions and impl methods.
This commit is contained in:
parent
ef11d4e3c7
commit
1ef7ddfda3
@ -832,7 +832,10 @@ fn insert_late_bound_lifetimes(map: &mut NamedRegionMap,
|
||||
constrained_by_input.visit_ty(&arg.ty);
|
||||
}
|
||||
|
||||
let mut appears_in_output = AllCollector { regions: FnvHashSet() };
|
||||
let mut appears_in_output = AllCollector {
|
||||
regions: FnvHashSet(),
|
||||
impl_trait: false
|
||||
};
|
||||
intravisit::walk_fn_ret_ty(&mut appears_in_output, &decl.output);
|
||||
|
||||
debug!("insert_late_bound_lifetimes: constrained_by_input={:?}",
|
||||
@ -842,7 +845,10 @@ fn insert_late_bound_lifetimes(map: &mut NamedRegionMap,
|
||||
//
|
||||
// Subtle point: because we disallow nested bindings, we can just
|
||||
// ignore binders here and scrape up all names we see.
|
||||
let mut appears_in_where_clause = AllCollector { regions: FnvHashSet() };
|
||||
let mut appears_in_where_clause = AllCollector {
|
||||
regions: FnvHashSet(),
|
||||
impl_trait: false
|
||||
};
|
||||
for ty_param in generics.ty_params.iter() {
|
||||
walk_list!(&mut appears_in_where_clause,
|
||||
visit_ty_param_bound,
|
||||
@ -864,12 +870,16 @@ fn insert_late_bound_lifetimes(map: &mut NamedRegionMap,
|
||||
// Late bound regions are those that:
|
||||
// - appear in the inputs
|
||||
// - do not appear in the where-clauses
|
||||
// - are not implicitly captured by `impl Trait`
|
||||
for lifetime in &generics.lifetimes {
|
||||
let name = lifetime.lifetime.name;
|
||||
|
||||
// appears in the where clauses? early-bound.
|
||||
if appears_in_where_clause.regions.contains(&name) { continue; }
|
||||
|
||||
// any `impl Trait` in the return type? early-bound.
|
||||
if appears_in_output.impl_trait { continue; }
|
||||
|
||||
// does not appear in the inputs, but appears in the return
|
||||
// type? eventually this will be early-bound, but for now we
|
||||
// just mark it so we can issue warnings.
|
||||
@ -932,12 +942,20 @@ fn insert_late_bound_lifetimes(map: &mut NamedRegionMap,
|
||||
|
||||
struct AllCollector {
|
||||
regions: FnvHashSet<ast::Name>,
|
||||
impl_trait: bool
|
||||
}
|
||||
|
||||
impl<'v> Visitor<'v> for AllCollector {
|
||||
fn visit_lifetime(&mut self, lifetime_ref: &'v hir::Lifetime) {
|
||||
self.regions.insert(lifetime_ref.name);
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, ty: &hir::Ty) {
|
||||
if let hir::TyImplTrait(_) = ty.node {
|
||||
self.impl_trait = true;
|
||||
}
|
||||
intravisit::walk_ty(self, ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,6 +65,7 @@ use require_c_abi_if_variadic;
|
||||
use rscope::{self, UnelidableRscope, RegionScope, ElidableRscope,
|
||||
ObjectLifetimeDefaultRscope, ShiftedRscope, BindingRscope,
|
||||
ElisionFailureInfo, ElidedLifetime};
|
||||
use rscope::{AnonTypeScope, MaybeWithAnonTypes};
|
||||
use util::common::{ErrorReported, FN_OUTPUT_NAME};
|
||||
use util::nodemap::{NodeMap, FnvHashSet};
|
||||
|
||||
@ -635,20 +636,21 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
|
||||
|
||||
fn convert_ty_with_lifetime_elision(&self,
|
||||
elided_lifetime: ElidedLifetime,
|
||||
ty: &hir::Ty)
|
||||
ty: &hir::Ty,
|
||||
anon_scope: Option<AnonTypeScope>)
|
||||
-> Ty<'tcx>
|
||||
{
|
||||
match elided_lifetime {
|
||||
Ok(implied_output_region) => {
|
||||
let rb = ElidableRscope::new(implied_output_region);
|
||||
self.ast_ty_to_ty(&rb, ty)
|
||||
self.ast_ty_to_ty(&MaybeWithAnonTypes::new(rb, anon_scope), ty)
|
||||
}
|
||||
Err(param_lifetimes) => {
|
||||
// All regions must be explicitly specified in the output
|
||||
// if the lifetime elision rules do not apply. This saves
|
||||
// the user from potentially-confusing errors.
|
||||
let rb = UnelidableRscope::new(param_lifetimes);
|
||||
self.ast_ty_to_ty(&rb, ty)
|
||||
self.ast_ty_to_ty(&MaybeWithAnonTypes::new(rb, anon_scope), ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -665,7 +667,8 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
|
||||
let region_substs =
|
||||
self.create_region_substs(rscope, span, decl_generics, Vec::new());
|
||||
|
||||
let binding_rscope = BindingRscope::new();
|
||||
let anon_scope = rscope.anon_type_scope();
|
||||
let binding_rscope = MaybeWithAnonTypes::new(BindingRscope::new(), anon_scope);
|
||||
let inputs =
|
||||
data.inputs.iter()
|
||||
.map(|a_t| self.ast_ty_arg_to_ty(&binding_rscope, decl_generics,
|
||||
@ -679,7 +682,9 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
|
||||
|
||||
let (output, output_span) = match data.output {
|
||||
Some(ref output_ty) => {
|
||||
(self.convert_ty_with_lifetime_elision(implied_output_region, &output_ty),
|
||||
(self.convert_ty_with_lifetime_elision(implied_output_region,
|
||||
&output_ty,
|
||||
anon_scope),
|
||||
output_ty.span)
|
||||
}
|
||||
None => {
|
||||
@ -1703,7 +1708,14 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
|
||||
}
|
||||
hir::TyBareFn(ref bf) => {
|
||||
require_c_abi_if_variadic(tcx, &bf.decl, bf.abi, ast_ty.span);
|
||||
let bare_fn_ty = self.ty_of_bare_fn(bf.unsafety, bf.abi, &bf.decl);
|
||||
let anon_scope = rscope.anon_type_scope();
|
||||
let (bare_fn_ty, _) =
|
||||
self.ty_of_method_or_bare_fn(bf.unsafety,
|
||||
bf.abi,
|
||||
None,
|
||||
&bf.decl,
|
||||
anon_scope,
|
||||
anon_scope);
|
||||
|
||||
// Find any late-bound regions declared in return type that do
|
||||
// not appear in the arguments. These are not wellformed.
|
||||
@ -1751,10 +1763,17 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
|
||||
|
||||
// Create the anonymized type.
|
||||
let def_id = tcx.map.local_def_id(ast_ty.id);
|
||||
let substs = tcx.mk_substs(Substs::empty());
|
||||
let substs = if let Some(anon_scope) = rscope.anon_type_scope() {
|
||||
anon_scope.fresh_substs(tcx)
|
||||
} else {
|
||||
span_err!(tcx.sess, ast_ty.span, E0562,
|
||||
"`impl Trait` not allowed outside of function \
|
||||
and inherent method return types");
|
||||
tcx.mk_substs(Substs::empty())
|
||||
};
|
||||
let ty = tcx.mk_anon(tcx.map.local_def_id(ast_ty.id), substs);
|
||||
|
||||
// Collect the bounds, i.e. the `Trait` in `impl Trait`.
|
||||
// Collect the bounds, i.e. the `A+B+'c` in `impl A+B+'c`.
|
||||
let bounds = compute_bounds(self, ty, bounds, SizedByDefault::Yes, ast_ty.span);
|
||||
let predicates = tcx.lift_to_global(&bounds.predicates(tcx, ty)).unwrap();
|
||||
let predicates = ty::GenericPredicates {
|
||||
@ -1828,36 +1847,40 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
|
||||
|
||||
pub fn ty_of_method(&self,
|
||||
sig: &hir::MethodSig,
|
||||
untransformed_self_ty: Ty<'tcx>)
|
||||
untransformed_self_ty: Ty<'tcx>,
|
||||
anon_scope: Option<AnonTypeScope>)
|
||||
-> (&'tcx ty::BareFnTy<'tcx>, ty::ExplicitSelfCategory) {
|
||||
let (bare_fn_ty, optional_explicit_self_category) =
|
||||
self.ty_of_method_or_bare_fn(sig.unsafety,
|
||||
sig.abi,
|
||||
Some(untransformed_self_ty),
|
||||
&sig.decl);
|
||||
(bare_fn_ty, optional_explicit_self_category)
|
||||
self.ty_of_method_or_bare_fn(sig.unsafety,
|
||||
sig.abi,
|
||||
Some(untransformed_self_ty),
|
||||
&sig.decl,
|
||||
None,
|
||||
anon_scope)
|
||||
}
|
||||
|
||||
pub fn ty_of_bare_fn(&self,
|
||||
unsafety: hir::Unsafety,
|
||||
abi: abi::Abi,
|
||||
decl: &hir::FnDecl)
|
||||
decl: &hir::FnDecl,
|
||||
anon_scope: Option<AnonTypeScope>)
|
||||
-> &'tcx ty::BareFnTy<'tcx> {
|
||||
self.ty_of_method_or_bare_fn(unsafety, abi, None, decl).0
|
||||
self.ty_of_method_or_bare_fn(unsafety, abi, None, decl, None, anon_scope).0
|
||||
}
|
||||
|
||||
fn ty_of_method_or_bare_fn<'a>(&self,
|
||||
unsafety: hir::Unsafety,
|
||||
abi: abi::Abi,
|
||||
opt_untransformed_self_ty: Option<Ty<'tcx>>,
|
||||
decl: &hir::FnDecl)
|
||||
-> (&'tcx ty::BareFnTy<'tcx>, ty::ExplicitSelfCategory)
|
||||
fn ty_of_method_or_bare_fn(&self,
|
||||
unsafety: hir::Unsafety,
|
||||
abi: abi::Abi,
|
||||
opt_untransformed_self_ty: Option<Ty<'tcx>>,
|
||||
decl: &hir::FnDecl,
|
||||
arg_anon_scope: Option<AnonTypeScope>,
|
||||
ret_anon_scope: Option<AnonTypeScope>)
|
||||
-> (&'tcx ty::BareFnTy<'tcx>, ty::ExplicitSelfCategory)
|
||||
{
|
||||
debug!("ty_of_method_or_bare_fn");
|
||||
|
||||
// New region names that appear inside of the arguments of the function
|
||||
// declaration are bound to that function type.
|
||||
let rb = rscope::BindingRscope::new();
|
||||
let rb = MaybeWithAnonTypes::new(BindingRscope::new(), arg_anon_scope);
|
||||
|
||||
// `implied_output_region` is the region that will be assumed for any
|
||||
// region parameters in the return type. In accordance with the rules for
|
||||
@ -1895,7 +1918,8 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
|
||||
let output_ty = match decl.output {
|
||||
hir::Return(ref output) =>
|
||||
ty::FnConverging(self.convert_ty_with_lifetime_elision(implied_output_region,
|
||||
&output)),
|
||||
&output,
|
||||
ret_anon_scope)),
|
||||
hir::DefaultReturn(..) => ty::FnConverging(self.tcx().mk_nil()),
|
||||
hir::NoReturn(..) => ty::FnDiverging
|
||||
};
|
||||
|
@ -564,13 +564,17 @@ fn convert_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
|
||||
let ty_generic_predicates =
|
||||
ty_generic_predicates_for_fn(ccx, &sig.generics, rcvr_ty_predicates);
|
||||
|
||||
let (fty, explicit_self_category) =
|
||||
let (fty, explicit_self_category) = {
|
||||
let anon_scope = match container {
|
||||
ImplContainer(_) => Some(AnonTypeScope::new(&ty_generics)),
|
||||
TraitContainer(_) => None
|
||||
};
|
||||
AstConv::ty_of_method(&ccx.icx(&(rcvr_ty_predicates, &sig.generics)),
|
||||
sig,
|
||||
untransformed_rcvr_ty);
|
||||
sig, untransformed_rcvr_ty, anon_scope)
|
||||
};
|
||||
|
||||
let def_id = ccx.tcx.map.local_def_id(id);
|
||||
let substs = mk_item_substs(ccx, &ty_generics);
|
||||
let substs = mk_item_substs(ccx.tcx, &ty_generics);
|
||||
|
||||
let ty_method = ty::Method::new(name,
|
||||
ty_generics,
|
||||
@ -961,7 +965,7 @@ fn convert_variant_ctor<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
|
||||
.map(|field| field.unsubst_ty())
|
||||
.collect();
|
||||
let def_id = tcx.map.local_def_id(ctor_id);
|
||||
let substs = mk_item_substs(ccx, &scheme.generics);
|
||||
let substs = mk_item_substs(tcx, &scheme.generics);
|
||||
tcx.mk_fn_def(def_id, substs, tcx.mk_bare_fn(ty::BareFnTy {
|
||||
unsafety: hir::Unsafety::Normal,
|
||||
abi: abi::Abi::Rust,
|
||||
@ -1460,9 +1464,10 @@ fn compute_type_scheme_of_item<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
|
||||
}
|
||||
hir::ItemFn(ref decl, unsafety, _, abi, ref generics, _) => {
|
||||
let ty_generics = ty_generics_for_fn(ccx, generics, &ty::Generics::empty());
|
||||
let tofd = AstConv::ty_of_bare_fn(&ccx.icx(generics), unsafety, abi, &decl);
|
||||
let tofd = AstConv::ty_of_bare_fn(&ccx.icx(generics), unsafety, abi, &decl,
|
||||
Some(AnonTypeScope::new(&ty_generics)));
|
||||
let def_id = ccx.tcx.map.local_def_id(it.id);
|
||||
let substs = mk_item_substs(ccx, &ty_generics);
|
||||
let substs = mk_item_substs(tcx, &ty_generics);
|
||||
let ty = tcx.mk_fn_def(def_id, substs, tofd);
|
||||
ty::TypeScheme { ty: ty, generics: ty_generics }
|
||||
}
|
||||
@ -1474,14 +1479,14 @@ fn compute_type_scheme_of_item<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
|
||||
hir::ItemEnum(ref ei, ref generics) => {
|
||||
let def = convert_enum_def(ccx, it, ei);
|
||||
let ty_generics = ty_generics_for_type(ccx, generics);
|
||||
let substs = mk_item_substs(ccx, &ty_generics);
|
||||
let substs = mk_item_substs(tcx, &ty_generics);
|
||||
let t = tcx.mk_enum(def, substs);
|
||||
ty::TypeScheme { ty: t, generics: ty_generics }
|
||||
}
|
||||
hir::ItemStruct(ref si, ref generics) => {
|
||||
let def = convert_struct_def(ccx, it, si);
|
||||
let ty_generics = ty_generics_for_type(ccx, generics);
|
||||
let substs = mk_item_substs(ccx, &ty_generics);
|
||||
let substs = mk_item_substs(tcx, &ty_generics);
|
||||
let t = tcx.mk_struct(def, substs);
|
||||
ty::TypeScheme { ty: t, generics: ty_generics }
|
||||
}
|
||||
@ -2194,7 +2199,7 @@ fn compute_type_scheme_of_foreign_fn_decl<'a, 'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
let substs = mk_item_substs(ccx, &ty_generics);
|
||||
let substs = mk_item_substs(ccx.tcx, &ty_generics);
|
||||
let t_fn = ccx.tcx.mk_fn_def(id, substs, ccx.tcx.mk_bare_fn(ty::BareFnTy {
|
||||
abi: abi,
|
||||
unsafety: hir::Unsafety::Unsafe,
|
||||
@ -2209,19 +2214,19 @@ fn compute_type_scheme_of_foreign_fn_decl<'a, 'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
fn mk_item_substs<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
|
||||
ty_generics: &ty::Generics<'tcx>)
|
||||
-> &'tcx Substs<'tcx>
|
||||
pub fn mk_item_substs<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
ty_generics: &ty::Generics)
|
||||
-> &'tcx Substs<'tcx>
|
||||
{
|
||||
let types =
|
||||
ty_generics.types.map(
|
||||
|def| ccx.tcx.mk_param_from_def(def));
|
||||
|def| tcx.mk_param_from_def(def));
|
||||
|
||||
let regions =
|
||||
ty_generics.regions.map(
|
||||
|def| def.to_early_bound_region());
|
||||
|
||||
ccx.tcx.mk_substs(Substs::new(types, regions))
|
||||
tcx.mk_substs(Substs::new(types, regions))
|
||||
}
|
||||
|
||||
/// Checks that all the type parameters on an impl
|
||||
|
@ -4084,4 +4084,6 @@ register_diagnostics! {
|
||||
E0513, // no type for local variable ..
|
||||
E0521, // redundant default implementations of trait
|
||||
E0533, // `{}` does not name a unit variant, unit struct or a constant
|
||||
E0562, // `impl Trait` not allowed outside of function
|
||||
// and inherent method return types
|
||||
}
|
||||
|
@ -9,7 +9,8 @@
|
||||
// except according to those terms.
|
||||
|
||||
|
||||
use rustc::ty;
|
||||
use rustc::ty::{self, TyCtxt};
|
||||
use rustc::ty::subst::Substs;
|
||||
|
||||
use std::cell::Cell;
|
||||
use syntax_pos::Span;
|
||||
@ -50,6 +51,79 @@ pub trait RegionScope {
|
||||
/// computing `object_lifetime_default` (in particular, in legacy
|
||||
/// modes, it may not be relevant).
|
||||
fn base_object_lifetime_default(&self, span: Span) -> ty::Region;
|
||||
|
||||
/// If this scope allows anonymized types, return the generics in
|
||||
/// scope, that anonymized types will close over. For example,
|
||||
/// if you have a function like:
|
||||
///
|
||||
/// fn foo<'a, T>() -> impl Trait { ... }
|
||||
///
|
||||
/// then, for the rscope that is used when handling the return type,
|
||||
/// `anon_type_scope()` would return a `Some(AnonTypeScope {...})`,
|
||||
/// on which `.fresh_substs(...)` can be used to obtain identity
|
||||
/// Substs for `'a` and `T`, to track them in `TyAnon`. This property
|
||||
/// is controlled by the region scope because it's fine-grained enough
|
||||
/// to allow restriction of anonymized types to the syntactical extent
|
||||
/// of a function's return type.
|
||||
fn anon_type_scope(&self) -> Option<AnonTypeScope> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct AnonTypeScope<'a> {
|
||||
generics: &'a ty::Generics<'a>
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'gcx, 'tcx> AnonTypeScope<'a> {
|
||||
pub fn new(generics: &'a ty::Generics<'a>) -> AnonTypeScope<'a> {
|
||||
AnonTypeScope {
|
||||
generics: generics
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fresh_substs(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> &'tcx Substs<'tcx> {
|
||||
use collect::mk_item_substs;
|
||||
|
||||
mk_item_substs(tcx, self.generics)
|
||||
}
|
||||
}
|
||||
|
||||
/// A scope wrapper which optionally allows anonymized types.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct MaybeWithAnonTypes<'a, R> {
|
||||
base_scope: R,
|
||||
anon_scope: Option<AnonTypeScope<'a>>
|
||||
}
|
||||
|
||||
impl<'a, R: RegionScope> MaybeWithAnonTypes<'a, R> {
|
||||
pub fn new(base_scope: R, anon_scope: Option<AnonTypeScope<'a>>) -> Self {
|
||||
MaybeWithAnonTypes {
|
||||
base_scope: base_scope,
|
||||
anon_scope: anon_scope
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, R: RegionScope> RegionScope for MaybeWithAnonTypes<'a, R> {
|
||||
fn object_lifetime_default(&self, span: Span) -> Option<ty::Region> {
|
||||
self.base_scope.object_lifetime_default(span)
|
||||
}
|
||||
|
||||
fn anon_regions(&self,
|
||||
span: Span,
|
||||
count: usize)
|
||||
-> Result<Vec<ty::Region>, Option<Vec<ElisionFailureInfo>>> {
|
||||
self.base_scope.anon_regions(span, count)
|
||||
}
|
||||
|
||||
fn base_object_lifetime_default(&self, span: Span) -> ty::Region {
|
||||
self.base_scope.base_object_lifetime_default(span)
|
||||
}
|
||||
|
||||
fn anon_type_scope(&self) -> Option<AnonTypeScope> {
|
||||
self.anon_scope
|
||||
}
|
||||
}
|
||||
|
||||
// A scope in which all regions must be explicitly named. This is used
|
||||
@ -221,6 +295,10 @@ impl<'r> RegionScope for ObjectLifetimeDefaultRscope<'r> {
|
||||
{
|
||||
self.base_scope.anon_regions(span, count)
|
||||
}
|
||||
|
||||
fn anon_type_scope(&self) -> Option<AnonTypeScope> {
|
||||
self.base_scope.anon_type_scope()
|
||||
}
|
||||
}
|
||||
|
||||
/// A scope which simply shifts the Debruijn index of other scopes
|
||||
@ -262,4 +340,8 @@ impl<'r> RegionScope for ShiftedRscope<'r> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn anon_type_scope(&self) -> Option<AnonTypeScope> {
|
||||
self.base_scope.anon_type_scope()
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user