From 9f3dfd29a21f1bdc26720703f79d3fabdc7471de Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Fri, 21 Feb 2020 23:00:27 +0100 Subject: [PATCH] parse: allow `type Foo: Ord` syntactically. --- src/librustc_ast_lowering/item.rs | 18 ++--- src/librustc_ast_lowering/lib.rs | 2 +- src/librustc_ast_passes/ast_validation.rs | 7 ++ src/librustc_ast_passes/feature_gate.rs | 2 +- src/librustc_ast_pretty/pprust.rs | 16 ++--- src/librustc_parse/parser/item.rs | 32 ++++----- src/librustc_resolve/build_reduced_graph.rs | 4 +- src/librustc_resolve/late.rs | 2 +- src/librustc_save_analysis/dump_visitor.rs | 9 ++- src/librustc_save_analysis/sig.rs | 7 +- src/libsyntax/ast.rs | 4 +- src/libsyntax/mut_visit.rs | 5 +- src/libsyntax/visit.rs | 7 +- src/test/ui/parser/bounds-lifetime-where.rs | 2 +- .../ui/parser/bounds-lifetime-where.stderr | 4 +- .../item-free-type-bounds-semantic-fail.rs | 20 ++++++ ...item-free-type-bounds-semantic-fail.stderr | 67 +++++++++++++++++++ .../item-free-type-bounds-syntactic-pass.rs | 13 ++++ 18 files changed, 160 insertions(+), 61 deletions(-) create mode 100644 src/test/ui/parser/item-free-type-bounds-semantic-fail.rs create mode 100644 src/test/ui/parser/item-free-type-bounds-semantic-fail.stderr create mode 100644 src/test/ui/parser/item-free-type-bounds-syntactic-pass.rs diff --git a/src/librustc_ast_lowering/item.rs b/src/librustc_ast_lowering/item.rs index ad7221b16b2..1a19fab0265 100644 --- a/src/librustc_ast_lowering/item.rs +++ b/src/librustc_ast_lowering/item.rs @@ -297,28 +297,28 @@ impl<'hir> LoweringContext<'_, 'hir> { ItemKind::Mod(ref m) => hir::ItemKind::Mod(self.lower_mod(m)), ItemKind::ForeignMod(ref nm) => hir::ItemKind::ForeignMod(self.lower_foreign_mod(nm)), ItemKind::GlobalAsm(ref ga) => hir::ItemKind::GlobalAsm(self.lower_global_asm(ga)), - ItemKind::TyAlias(ref ty, ref generics) => match ty.kind.opaque_top_hack() { + ItemKind::TyAlias(ref generics, _, Some(ref ty)) => match ty.kind.opaque_top_hack() { None => { let ty = self.lower_ty(ty, ImplTraitContext::disallowed()); let generics = self.lower_generics(generics, ImplTraitContext::disallowed()); hir::ItemKind::TyAlias(ty, generics) } Some(bounds) => { + let ctx = || ImplTraitContext::OpaqueTy(None, hir::OpaqueTyOrigin::Misc); let ty = hir::OpaqueTy { - generics: self.lower_generics( - generics, - ImplTraitContext::OpaqueTy(None, hir::OpaqueTyOrigin::Misc), - ), - bounds: self.lower_param_bounds( - bounds, - ImplTraitContext::OpaqueTy(None, hir::OpaqueTyOrigin::Misc), - ), + generics: self.lower_generics(generics, ctx()), + bounds: self.lower_param_bounds(bounds, ctx()), impl_trait_fn: None, origin: hir::OpaqueTyOrigin::TypeAlias, }; hir::ItemKind::OpaqueTy(ty) } }, + ItemKind::TyAlias(ref generics, _, None) => { + let ty = self.arena.alloc(self.ty(span, hir::TyKind::Err)); + let generics = self.lower_generics(generics, ImplTraitContext::disallowed()); + hir::ItemKind::TyAlias(ty, generics) + } ItemKind::Enum(ref enum_definition, ref generics) => hir::ItemKind::Enum( hir::EnumDef { variants: self.arena.alloc_from_iter( diff --git a/src/librustc_ast_lowering/lib.rs b/src/librustc_ast_lowering/lib.rs index ac4ca30382f..9bb46009fe6 100644 --- a/src/librustc_ast_lowering/lib.rs +++ b/src/librustc_ast_lowering/lib.rs @@ -462,7 +462,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ItemKind::Struct(_, ref generics) | ItemKind::Union(_, ref generics) | ItemKind::Enum(_, ref generics) - | ItemKind::TyAlias(_, ref generics) + | ItemKind::TyAlias(ref generics, ..) | ItemKind::Trait(_, _, ref generics, ..) => { let def_id = self.lctx.resolver.definitions().local_def_id(item.id); let count = generics diff --git a/src/librustc_ast_passes/ast_validation.rs b/src/librustc_ast_passes/ast_validation.rs index 1194269e0ee..a9844a7059e 100644 --- a/src/librustc_ast_passes/ast_validation.rs +++ b/src/librustc_ast_passes/ast_validation.rs @@ -969,6 +969,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> { let msg = "free static item without body"; self.error_item_without_body(item.span, "static", msg, " = ;"); } + ItemKind::TyAlias(_, ref bounds, ref body) => { + if body.is_none() { + let msg = "free type alias without body"; + self.error_item_without_body(item.span, "type", msg, " = ;"); + } + self.check_type_no_bounds(bounds, "this context"); + } _ => {} } diff --git a/src/librustc_ast_passes/feature_gate.rs b/src/librustc_ast_passes/feature_gate.rs index 3c924847a73..5bddae0d49e 100644 --- a/src/librustc_ast_passes/feature_gate.rs +++ b/src/librustc_ast_passes/feature_gate.rs @@ -372,7 +372,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate_feature_post!(&self, decl_macro, i.span, msg); } - ast::ItemKind::TyAlias(ref ty, ..) => self.check_impl_trait(&ty), + ast::ItemKind::TyAlias(_, _, Some(ref ty)) => self.check_impl_trait(&ty), _ => {} } diff --git a/src/librustc_ast_pretty/pprust.rs b/src/librustc_ast_pretty/pprust.rs index 27cef8502a1..9b7bb574946 100644 --- a/src/librustc_ast_pretty/pprust.rs +++ b/src/librustc_ast_pretty/pprust.rs @@ -1185,18 +1185,10 @@ impl<'a> State<'a> { self.s.word(ga.asm.to_string()); self.end(); } - ast::ItemKind::TyAlias(ref ty, ref generics) => { - self.head(visibility_qualified(&item.vis, "type")); - self.print_ident(item.ident); - self.print_generic_params(&generics.params); - self.end(); // end the inner ibox - - self.print_where_clause(&generics.where_clause); - self.s.space(); - self.word_space("="); - self.print_type(ty); - self.s.word(";"); - self.end(); // end the outer ibox + ast::ItemKind::TyAlias(ref generics, ref bounds, ref ty) => { + let def = ast::Defaultness::Final; + let ty = ty.as_deref(); + self.print_associated_type(item.ident, generics, bounds, ty, &item.vis, def); } ast::ItemKind::Enum(ref enum_definition, ref params) => { self.print_enum_def(enum_definition, params, item.ident, item.span, &item.vis); diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs index 4dcde2f92db..328cf11c532 100644 --- a/src/librustc_parse/parser/item.rs +++ b/src/librustc_parse/parser/item.rs @@ -156,8 +156,7 @@ impl<'a> Parser<'a> { self.parse_item_mod(attrs)? } else if self.eat_keyword(kw::Type) { // TYPE ITEM - let (ident, ty, generics) = self.parse_type_alias()?; - (ident, ItemKind::TyAlias(ty, generics)) + self.parse_type_alias()? } else if self.eat_keyword(kw::Enum) { // ENUM ITEM self.parse_item_enum()? @@ -676,7 +675,10 @@ impl<'a> Parser<'a> { vis: &Visibility, ) -> PResult<'a, (Ident, AssocItemKind)> { if self.eat_keyword(kw::Type) { - self.parse_assoc_ty() + match self.parse_type_alias()? { + (ident, ItemKind::TyAlias(a, b, c)) => Ok((ident, AssocItemKind::TyAlias(a, b, c))), + _ => unreachable!(), + } } else if self.check_fn_front_matter() { let (ident, sig, generics, body) = self.parse_fn(at_end, attrs, req_name)?; Ok((ident, AssocItemKind::Fn(sig, generics, body))) @@ -700,10 +702,12 @@ impl<'a> Parser<'a> { } } - /// Parses the following grammar: - /// - /// AssocTy = Ident ["<"...">"] [":" [GenericBounds]] ["where" ...] ["=" Ty] - fn parse_assoc_ty(&mut self) -> PResult<'a, (Ident, AssocItemKind)> { + /// Parses a `type` alias with the following grammar: + /// ``` + /// TypeAlias = "type" Ident Generics {":" GenericBounds}? {"=" Ty}? ";" ; + /// ``` + /// The `"type"` has already been eaten. + fn parse_type_alias(&mut self) -> PResult<'a, (Ident, ItemKind)> { let ident = self.parse_ident()?; let mut generics = self.parse_generics()?; @@ -715,7 +719,7 @@ impl<'a> Parser<'a> { let default = if self.eat(&token::Eq) { Some(self.parse_ty()?) } else { None }; self.expect_semi()?; - Ok((ident, AssocItemKind::TyAlias(generics, bounds, default))) + Ok((ident, ItemKind::TyAlias(generics, bounds, default))) } /// Parses a `UseTree`. @@ -989,18 +993,6 @@ impl<'a> Parser<'a> { P(Ty { kind: TyKind::Infer, span: id.span, id: ast::DUMMY_NODE_ID }) } - /// Parses the grammar: - /// Ident ["<"...">"] ["where" ...] ("=" | ":") Ty ";" - fn parse_type_alias(&mut self) -> PResult<'a, (Ident, P, Generics)> { - let ident = self.parse_ident()?; - let mut tps = self.parse_generics()?; - tps.where_clause = self.parse_where_clause()?; - self.expect(&token::Eq)?; - let ty = self.parse_ty()?; - self.expect_semi()?; - Ok((ident, ty, tps)) - } - /// Parses an enum declaration. fn parse_item_enum(&mut self) -> PResult<'a, ItemInfo> { let id = self.parse_ident()?; diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 1f622b80e8e..383bfe18fd0 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -718,8 +718,8 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } // These items live in the type namespace. - ItemKind::TyAlias(ref ty, _) => { - let def_kind = match ty.kind.opaque_top_hack() { + ItemKind::TyAlias(_, _, ref ty) => { + let def_kind = match ty.as_deref().and_then(|ty| ty.kind.opaque_top_hack()) { None => DefKind::TyAlias, Some(_) => DefKind::OpaqueTy, }; diff --git a/src/librustc_resolve/late.rs b/src/librustc_resolve/late.rs index 73601cd2ee7..74628e6e5a0 100644 --- a/src/librustc_resolve/late.rs +++ b/src/librustc_resolve/late.rs @@ -797,7 +797,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { debug!("(resolving item) resolving {} ({:?})", name, item.kind); match item.kind { - ItemKind::TyAlias(_, ref generics) | ItemKind::Fn(_, ref generics, _) => { + ItemKind::TyAlias(ref generics, _, _) | ItemKind::Fn(_, ref generics, _) => { self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| { visit::walk_item(this, item) }); diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs index db7733e7241..53dd6d28f89 100644 --- a/src/librustc_save_analysis/dump_visitor.rs +++ b/src/librustc_save_analysis/dump_visitor.rs @@ -1311,12 +1311,15 @@ impl<'l, 'tcx> Visitor<'l> for DumpVisitor<'l, 'tcx> { self.process_mod(item); visit::walk_mod(self, m); } - TyAlias(ref ty, ref ty_params) => { + TyAlias(ref ty_params, _, ref ty) => { let qualname = format!( "::{}", self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)) ); - let value = ty_to_string(&ty); + let value = match ty { + Some(ty) => ty_to_string(&ty), + None => "_".to_string(), + }; if !self.span.filter_generated(item.ident.span) { let span = self.span_from_span(item.ident.span); let id = id_from_node_id(item.id, &self.save_ctxt); @@ -1341,7 +1344,7 @@ impl<'l, 'tcx> Visitor<'l> for DumpVisitor<'l, 'tcx> { ); } - self.visit_ty(&ty); + walk_list!(self, visit_ty, ty); self.process_generic_params(ty_params, &qualname, item.id); } Mac(_) => (), diff --git a/src/librustc_save_analysis/sig.rs b/src/librustc_save_analysis/sig.rs index a2c61db4b7c..2c07ed0571b 100644 --- a/src/librustc_save_analysis/sig.rs +++ b/src/librustc_save_analysis/sig.rs @@ -423,12 +423,15 @@ impl Sig for ast::Item { Ok(Signature { text, defs, refs: vec![] }) } - ast::ItemKind::TyAlias(ref ty, ref generics) => { + ast::ItemKind::TyAlias(ref generics, _, ref ty) => { let text = "type ".to_owned(); let mut sig = name_and_generics(text, offset, generics, self.id, self.ident, scx)?; sig.text.push_str(" = "); - let ty = ty.make(offset + sig.text.len(), id, scx)?; + let ty = match ty { + Some(ty) => ty.make(offset + sig.text.len(), id, scx)?, + None => Err("Ty")?, + }; sig.text.push_str(&ty.text); sig.text.push(';'); diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 9ae3010a0f6..849950e939a 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -2524,7 +2524,7 @@ pub enum ItemKind { /// A type alias (`type`). /// /// E.g., `type Foo = Bar;`. - TyAlias(P, Generics), + TyAlias(Generics, GenericBounds, Option>), /// An enum definition (`enum`). /// /// E.g., `enum Foo { C, D }`. @@ -2594,7 +2594,7 @@ impl ItemKind { pub fn generics(&self) -> Option<&Generics> { match self { Self::Fn(_, generics, _) - | Self::TyAlias(_, generics) + | Self::TyAlias(generics, ..) | Self::Enum(_, generics) | Self::Struct(_, generics) | Self::Union(_, generics) diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs index 92f20b719f8..02f790dfbb4 100644 --- a/src/libsyntax/mut_visit.rs +++ b/src/libsyntax/mut_visit.rs @@ -902,9 +902,10 @@ pub fn noop_visit_item_kind(kind: &mut ItemKind, vis: &mut T) { ItemKind::Mod(m) => vis.visit_mod(m), ItemKind::ForeignMod(nm) => vis.visit_foreign_mod(nm), ItemKind::GlobalAsm(_ga) => {} - ItemKind::TyAlias(ty, generics) => { - vis.visit_ty(ty); + ItemKind::TyAlias(generics, bounds, ty) => { vis.visit_generics(generics); + visit_bounds(bounds, vis); + visit_opt(ty, |ty| vis.visit_ty(ty)); } ItemKind::Enum(EnumDef { variants }, generics) => { variants.flat_map_in_place(|variant| vis.flat_map_variant(variant)); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index dedd42fe0f6..bd35918dba7 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -312,9 +312,10 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) { walk_list!(visitor, visit_foreign_item, &foreign_module.items); } ItemKind::GlobalAsm(ref ga) => visitor.visit_global_asm(ga), - ItemKind::TyAlias(ref typ, ref generics) => { - visitor.visit_ty(typ); - visitor.visit_generics(generics) + ItemKind::TyAlias(ref generics, ref bounds, ref ty) => { + visitor.visit_generics(generics); + walk_list!(visitor, visit_param_bound, bounds); + walk_list!(visitor, visit_ty, ty); } ItemKind::Enum(ref enum_definition, ref generics) => { visitor.visit_generics(generics); diff --git a/src/test/ui/parser/bounds-lifetime-where.rs b/src/test/ui/parser/bounds-lifetime-where.rs index acb04e7859b..e60cc153e67 100644 --- a/src/test/ui/parser/bounds-lifetime-where.rs +++ b/src/test/ui/parser/bounds-lifetime-where.rs @@ -5,6 +5,6 @@ type A where 'a:, = u8; // OK type A where 'a: 'b + 'c = u8; // OK type A where = u8; // OK type A where 'a: 'b + = u8; // OK -type A where , = u8; //~ ERROR expected one of `=`, lifetime, or type, found `,` +type A where , = u8; //~ ERROR expected one of `;`, `=`, lifetime, or type, found `,` fn main() {} diff --git a/src/test/ui/parser/bounds-lifetime-where.stderr b/src/test/ui/parser/bounds-lifetime-where.stderr index 05cebd6d351..950fa46c66b 100644 --- a/src/test/ui/parser/bounds-lifetime-where.stderr +++ b/src/test/ui/parser/bounds-lifetime-where.stderr @@ -1,8 +1,8 @@ -error: expected one of `=`, lifetime, or type, found `,` +error: expected one of `;`, `=`, lifetime, or type, found `,` --> $DIR/bounds-lifetime-where.rs:8:14 | LL | type A where , = u8; - | ^ expected one of `=`, lifetime, or type + | ^ expected one of `;`, `=`, lifetime, or type error: aborting due to previous error diff --git a/src/test/ui/parser/item-free-type-bounds-semantic-fail.rs b/src/test/ui/parser/item-free-type-bounds-semantic-fail.rs new file mode 100644 index 00000000000..9db4111fbab --- /dev/null +++ b/src/test/ui/parser/item-free-type-bounds-semantic-fail.rs @@ -0,0 +1,20 @@ +fn main() {} + +fn semantics() { + type A: Ord; + //~^ ERROR bounds on `type`s in this context have no effect + //~| ERROR free type alias without body + type B: Ord = u8; + //~^ ERROR bounds on `type`s in this context have no effect + type C: Ord where 'static: 'static = u8; + //~^ ERROR bounds on `type`s in this context have no effect + type D<_T>: Ord; + //~^ ERROR bounds on `type`s in this context have no effect + //~| ERROR free type alias without body + type E<_T>: Ord = u8; + //~^ ERROR bounds on `type`s in this context have no effect + //~| ERROR type parameter `_T` is unused + type F<_T>: Ord where 'static: 'static = u8; + //~^ ERROR bounds on `type`s in this context have no effect + //~| ERROR type parameter `_T` is unused +} diff --git a/src/test/ui/parser/item-free-type-bounds-semantic-fail.stderr b/src/test/ui/parser/item-free-type-bounds-semantic-fail.stderr new file mode 100644 index 00000000000..1b086512891 --- /dev/null +++ b/src/test/ui/parser/item-free-type-bounds-semantic-fail.stderr @@ -0,0 +1,67 @@ +error: free type alias without body + --> $DIR/item-free-type-bounds-semantic-fail.rs:4:5 + | +LL | type A: Ord; + | ^^^^^^^^^^^- + | | + | help: provide a definition for the type: `= ;` + +error: bounds on `type`s in this context have no effect + --> $DIR/item-free-type-bounds-semantic-fail.rs:4:13 + | +LL | type A: Ord; + | ^^^ + +error: bounds on `type`s in this context have no effect + --> $DIR/item-free-type-bounds-semantic-fail.rs:7:13 + | +LL | type B: Ord = u8; + | ^^^ + +error: bounds on `type`s in this context have no effect + --> $DIR/item-free-type-bounds-semantic-fail.rs:9:13 + | +LL | type C: Ord where 'static: 'static = u8; + | ^^^ + +error: free type alias without body + --> $DIR/item-free-type-bounds-semantic-fail.rs:11:5 + | +LL | type D<_T>: Ord; + | ^^^^^^^^^^^^^^^- + | | + | help: provide a definition for the type: `= ;` + +error: bounds on `type`s in this context have no effect + --> $DIR/item-free-type-bounds-semantic-fail.rs:11:17 + | +LL | type D<_T>: Ord; + | ^^^ + +error: bounds on `type`s in this context have no effect + --> $DIR/item-free-type-bounds-semantic-fail.rs:14:17 + | +LL | type E<_T>: Ord = u8; + | ^^^ + +error: bounds on `type`s in this context have no effect + --> $DIR/item-free-type-bounds-semantic-fail.rs:17:17 + | +LL | type F<_T>: Ord where 'static: 'static = u8; + | ^^^ + +error[E0091]: type parameter `_T` is unused + --> $DIR/item-free-type-bounds-semantic-fail.rs:14:12 + | +LL | type E<_T>: Ord = u8; + | ^^ unused type parameter + +error[E0091]: type parameter `_T` is unused + --> $DIR/item-free-type-bounds-semantic-fail.rs:17:12 + | +LL | type F<_T>: Ord where 'static: 'static = u8; + | ^^ unused type parameter + +error: aborting due to 10 previous errors + +For more information about this error, try `rustc --explain E0091`. diff --git a/src/test/ui/parser/item-free-type-bounds-syntactic-pass.rs b/src/test/ui/parser/item-free-type-bounds-syntactic-pass.rs new file mode 100644 index 00000000000..58fc926d08f --- /dev/null +++ b/src/test/ui/parser/item-free-type-bounds-syntactic-pass.rs @@ -0,0 +1,13 @@ +// check-pass + +fn main() {} + +#[cfg(FALSE)] +fn syntax() { + type A: Ord; + type B: Ord = u8; + type C: Ord where 'static: 'static = u8; + type D<_T>: Ord; + type E<_T>: Ord = u8; + type F<_T>: Ord where 'static: 'static = u8; +}