typeck: disallow impl Trait outside of return types of functions and impl methods.

This commit is contained in:
Eduard Burtescu 2016-08-06 03:11:17 +03:00
parent ef11d4e3c7
commit 1ef7ddfda3
5 changed files with 174 additions and 43 deletions

View File

@ -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);
}
}
}

View File

@ -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
};

View File

@ -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

View File

@ -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
}

View File

@ -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()
}
}