Skip to content

Commit

Permalink
Rollup merge of rust-lang#68071 - estebank:ice-67995, r=Centril
Browse files Browse the repository at this point in the history
Extend support of `_` in type parameters

 - Account for `impl Trait<_>`.
 - Provide a reasonable `Span` for empty `Generics` in `impl`s.
 - Account for `fn foo<_>(_: _) {}` to suggest `fn foo<T>(_: T) {}`.
 - Fix rust-lang#67995. Follow up to rust-lang#67597.
  • Loading branch information
Centril authored Jan 10, 2020
2 parents 0d959ad + 6e04cf0 commit 25e57f4
Show file tree
Hide file tree
Showing 6 changed files with 280 additions and 91 deletions.
2 changes: 1 addition & 1 deletion src/librustc_parse/parser/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ impl<'a> Parser<'a> {
self.expect_gt()?;
(params, span_lo.to(self.prev_span))
} else {
(vec![], self.prev_span.between(self.token.span))
(vec![], self.prev_span.shrink_to_hi())
};
Ok(ast::Generics {
params,
Expand Down
6 changes: 5 additions & 1 deletion src/librustc_parse/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,11 @@ impl<'a> Parser<'a> {
let mut generics = if self.choose_generics_over_qpath() {
self.parse_generics()?
} else {
Generics::default()
let mut generics = Generics::default();
// impl A for B {}
// /\ this is where `generics.span` should point when there are no type params.
generics.span = self.prev_span.shrink_to_hi();
generics
};

let constness = if self.eat_keyword(kw::Const) {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_typeck/astconv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2803,7 +2803,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// allowed. `allow_ty_infer` gates this behavior.
crate::collect::placeholder_type_error(
tcx,
ident_span.unwrap_or(DUMMY_SP),
ident_span.map(|sp| sp.shrink_to_hi()).unwrap_or(DUMMY_SP),
generic_params,
visitor.0,
ident_span.is_some(),
Expand Down
36 changes: 28 additions & 8 deletions src/librustc_typeck/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ struct CollectItemTypesVisitor<'tcx> {
/// all already existing generic type parameters to avoid suggesting a name that is already in use.
crate fn placeholder_type_error(
tcx: TyCtxt<'tcx>,
ident_span: Span,
span: Span,
generics: &[hir::GenericParam<'_>],
placeholder_types: Vec<Span>,
suggest: bool,
Expand All @@ -153,7 +153,14 @@ crate fn placeholder_type_error(
let mut sugg: Vec<_> =
placeholder_types.iter().map(|sp| (*sp, type_name.to_string())).collect();
if generics.is_empty() {
sugg.push((ident_span.shrink_to_hi(), format!("<{}>", type_name)));
sugg.push((span, format!("<{}>", type_name)));
} else if let Some(arg) = generics.iter().find(|arg| match arg.name {
hir::ParamName::Plain(Ident { name: kw::Underscore, .. }) => true,
_ => false,
}) {
// Account for `_` already present in cases like `struct S<_>(_);` and suggest
// `struct S<T>(T);` instead of `struct S<_, T>(T);`.
sugg.push((arg.span, format!("{}", type_name)));
} else {
sugg.push((
generics.iter().last().unwrap().span.shrink_to_hi(),
Expand All @@ -175,16 +182,20 @@ fn reject_placeholder_type_signatures_in_item(tcx: TyCtxt<'tcx>, item: &'tcx hir
let (generics, suggest) = match &item.kind {
hir::ItemKind::Union(_, generics)
| hir::ItemKind::Enum(_, generics)
| hir::ItemKind::Struct(_, generics) => (&generics.params[..], true),
hir::ItemKind::TyAlias(_, generics) => (&generics.params[..], false),
| hir::ItemKind::TraitAlias(generics, _)
| hir::ItemKind::Trait(_, _, generics, ..)
| hir::ItemKind::Impl(_, _, _, generics, ..)
| hir::ItemKind::Struct(_, generics) => (generics, true),
hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. })
| hir::ItemKind::TyAlias(_, generics) => (generics, false),
// `static`, `fn` and `const` are handled elsewhere to suggest appropriate type.
_ => return,
};

let mut visitor = PlaceholderHirTyCollector::default();
visitor.visit_item(item);

placeholder_type_error(tcx, item.ident.span, generics, visitor.0, suggest);
placeholder_type_error(tcx, generics.span, &generics.params[..], visitor.0, suggest);
}

impl Visitor<'tcx> for CollectItemTypesVisitor<'tcx> {
Expand Down Expand Up @@ -1798,10 +1809,19 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
/// Whether `ty` is a type with `_` placeholders that can be infered. Used in diagnostics only to
/// use inference to provide suggestions for the appropriate type if possible.
fn is_suggestable_infer_ty(ty: &hir::Ty<'_>) -> bool {
use hir::TyKind::*;
match &ty.kind {
hir::TyKind::Infer => true,
hir::TyKind::Slice(ty) | hir::TyKind::Array(ty, _) => is_suggestable_infer_ty(ty),
hir::TyKind::Tup(tys) => tys.iter().any(|ty| is_suggestable_infer_ty(ty)),
Infer => true,
Slice(ty) | Array(ty, _) => is_suggestable_infer_ty(ty),
Tup(tys) => tys.iter().any(is_suggestable_infer_ty),
Ptr(mut_ty) | Rptr(_, mut_ty) => is_suggestable_infer_ty(mut_ty.ty),
Def(_, generic_args) => generic_args
.iter()
.filter_map(|arg| match arg {
hir::GenericArg::Type(ty) => Some(ty),
_ => None,
})
.any(is_suggestable_infer_ty),
_ => false,
}
}
Expand Down
45 changes: 45 additions & 0 deletions src/test/ui/typeck/typeck_type_placeholder_item.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![feature(type_alias_impl_trait)] // Needed for single test `type Y = impl Trait<_>`
// This test checks that it is not possible to enable global type
// inference by using the `_` type placeholder.

Expand Down Expand Up @@ -42,6 +43,16 @@ impl Test9 {
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures
}

fn test11(x: &usize) -> &_ {
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures
&x
}

unsafe fn test12(x: *const usize) -> *const *const _ {
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures
&x
}

impl Clone for Test9 {
fn clone(&self) -> _ { Test9 }
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures
Expand Down Expand Up @@ -131,3 +142,37 @@ trait T {
fn assoc_fn_test3() -> _;
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures
}

struct BadStruct<_>(_);
//~^ ERROR expected identifier, found reserved identifier `_`
//~| ERROR the type placeholder `_` is not allowed within types on item signatures
trait BadTrait<_> {}
//~^ ERROR expected identifier, found reserved identifier `_`
impl BadTrait<_> for BadStruct<_> {}
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures

fn impl_trait() -> impl BadTrait<_> {
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures
unimplemented!()
}

struct BadStruct1<_, _>(_);
//~^ ERROR expected identifier, found reserved identifier `_`
//~| ERROR expected identifier, found reserved identifier `_`
//~| ERROR the name `_` is already used
//~| ERROR the type placeholder `_` is not allowed within types on item signatures
struct BadStruct2<_, T>(_, T);
//~^ ERROR expected identifier, found reserved identifier `_`
//~| ERROR the type placeholder `_` is not allowed within types on item signatures

type X = Box<_>;
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures

struct Struct;
trait Trait<T> {}
impl Trait<usize> for Struct {}
type Y = impl Trait<_>;
//~^ ERROR the type placeholder `_` is not allowed within types on item signatures
fn foo() -> Y {
Struct
}
Loading

0 comments on commit 25e57f4

Please sign in to comment.