diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 8207057fd0aae..21ed2ab017ee2 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -722,8 +722,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> { FnKind::ItemFn(..) => { ItemRibKind } - FnKind::Method(_, sig, _, _) => { - MethodRibKind(!sig.decl.has_self()) + FnKind::Method(_, _, _, _) => { + TraitOrImplItemRibKind } FnKind::Closure(_) => ClosureRibKind(node_id), }; @@ -811,12 +811,10 @@ enum RibKind<'a> { ClosureRibKind(NodeId /* func id */), // We passed through an impl or trait and are now in one of its - // methods. Allow references to ty params that impl or trait + // methods or associated types. Allow references to ty params that impl or trait // binds. Disallow any other upvars (including other ty params that are // upvars). - // - // The boolean value represents the fact that this method is static or not. - MethodRibKind(bool), + TraitOrImplItemRibKind, // We passed through an item scope. Disallow upvars. ItemRibKind, @@ -1873,34 +1871,33 @@ impl<'a> Resolver<'a> { for trait_item in trait_items { this.check_proc_macro_attrs(&trait_item.attrs); - match trait_item.node { - TraitItemKind::Const(ref ty, ref default) => { - this.visit_ty(ty); - - // Only impose the restrictions of - // ConstRibKind for an actual constant - // expression in a provided default. - if let Some(ref expr) = *default{ - this.with_constant_rib(|this| { - this.visit_expr(expr); - }); + let type_parameters = HasTypeParameters(&trait_item.generics, + TraitOrImplItemRibKind); + this.with_type_parameter_rib(type_parameters, |this| { + match trait_item.node { + TraitItemKind::Const(ref ty, ref default) => { + this.visit_ty(ty); + + // Only impose the restrictions of + // ConstRibKind for an actual constant + // expression in a provided default. + if let Some(ref expr) = *default{ + this.with_constant_rib(|this| { + this.visit_expr(expr); + }); + } } - } - TraitItemKind::Method(ref sig, _) => { - let type_parameters = - HasTypeParameters(&trait_item.generics, - MethodRibKind(!sig.decl.has_self())); - this.with_type_parameter_rib(type_parameters, |this| { + TraitItemKind::Method(_, _) => { visit::walk_trait_item(this, trait_item) - }); - } - TraitItemKind::Type(..) => { - this.with_type_parameter_rib(NoTypeParameters, |this| { + } + TraitItemKind::Type(..) => { visit::walk_trait_item(this, trait_item) - }); - } - TraitItemKind::Macro(_) => panic!("unexpanded macro in resolve!"), - }; + } + TraitItemKind::Macro(_) => { + panic!("unexpanded macro in resolve!") + } + }; + }); } }); }); @@ -2084,48 +2081,48 @@ impl<'a> Resolver<'a> { for impl_item in impl_items { this.check_proc_macro_attrs(&impl_item.attrs); this.resolve_visibility(&impl_item.vis); - match impl_item.node { - ImplItemKind::Const(..) => { - // If this is a trait impl, ensure the const - // exists in trait - this.check_trait_item(impl_item.ident, - ValueNS, - impl_item.span, - |n, s| ResolutionError::ConstNotMemberOfTrait(n, s)); - this.with_constant_rib(|this| - visit::walk_impl_item(this, impl_item) - ); - } - ImplItemKind::Method(ref sig, _) => { - // If this is a trait impl, ensure the method - // exists in trait - this.check_trait_item(impl_item.ident, - ValueNS, - impl_item.span, - |n, s| ResolutionError::MethodNotMemberOfTrait(n, s)); - - // We also need a new scope for the method- - // specific type parameters. - let type_parameters = - HasTypeParameters(&impl_item.generics, - MethodRibKind(!sig.decl.has_self())); - this.with_type_parameter_rib(type_parameters, |this| { - visit::walk_impl_item(this, impl_item); - }); - } - ImplItemKind::Type(ref ty) => { - // If this is a trait impl, ensure the type - // exists in trait - this.check_trait_item(impl_item.ident, - TypeNS, - impl_item.span, - |n, s| ResolutionError::TypeNotMemberOfTrait(n, s)); - this.visit_ty(ty); + // We also need a new scope for the impl item type parameters. + let type_parameters = HasTypeParameters(&impl_item.generics, + TraitOrImplItemRibKind); + this.with_type_parameter_rib(type_parameters, |this| { + use self::ResolutionError::*; + match impl_item.node { + ImplItemKind::Const(..) => { + // If this is a trait impl, ensure the const + // exists in trait + this.check_trait_item(impl_item.ident, + ValueNS, + impl_item.span, + |n, s| ConstNotMemberOfTrait(n, s)); + this.with_constant_rib(|this| + visit::walk_impl_item(this, impl_item) + ); + } + ImplItemKind::Method(_, _) => { + // If this is a trait impl, ensure the method + // exists in trait + this.check_trait_item(impl_item.ident, + ValueNS, + impl_item.span, + |n, s| MethodNotMemberOfTrait(n, s)); + + visit::walk_impl_item(this, impl_item); + } + ImplItemKind::Type(ref ty) => { + // If this is a trait impl, ensure the type + // exists in trait + this.check_trait_item(impl_item.ident, + TypeNS, + impl_item.span, + |n, s| TypeNotMemberOfTrait(n, s)); + + this.visit_ty(ty); + } + ImplItemKind::Macro(_) => + panic!("unexpanded macro in resolve!"), } - ImplItemKind::Macro(_) => - panic!("unexpanded macro in resolve!"), - } + }); } }); }); @@ -3015,7 +3012,7 @@ impl<'a> Resolver<'a> { seen.insert(node_id, depth); } } - ItemRibKind | MethodRibKind(_) => { + ItemRibKind | TraitOrImplItemRibKind => { // This was an attempt to access an upvar inside a // named function item. This is not allowed, so we // report an error. @@ -3039,7 +3036,7 @@ impl<'a> Resolver<'a> { Def::TyParam(..) | Def::SelfTy(..) => { for rib in ribs { match rib.kind { - NormalRibKind | MethodRibKind(_) | ClosureRibKind(..) | + NormalRibKind | TraitOrImplItemRibKind | ClosureRibKind(..) | ModuleRibKind(..) | MacroDefinition(..) | ForwardTyParamBanRibKind | ConstantItemRibKind => { // Nothing to do. Continue. diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 5c7450baa2f7d..83c7efc3d7ab3 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -420,6 +420,9 @@ declare_features! ( // #![wasm_import_memory] attribute (active, wasm_import_memory, "1.22.0", None), + + // generic associated types (RFC 1598) + (active, generic_associated_types, "1.23.0", Some(44265)), ); declare_features! ( @@ -1607,9 +1610,17 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate_feature_post!(&self, const_fn, ti.span, "const fn is unstable"); } } - ast::TraitItemKind::Type(_, Some(_)) => { - gate_feature_post!(&self, associated_type_defaults, ti.span, - "associated type defaults are unstable"); + ast::TraitItemKind::Type(_, ref default) => { + // We use two if statements instead of something like match guards so that both + // of these errors can be emitted if both cases apply. + if default.is_some() { + gate_feature_post!(&self, associated_type_defaults, ti.span, + "associated type defaults are unstable"); + } + if ti.generics.is_parameterized() { + gate_feature_post!(&self, generic_associated_types, ti.span, + "generic associated types are unstable"); + } } _ => {} } @@ -1629,6 +1640,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate_feature_post!(&self, const_fn, ii.span, "const fn is unstable"); } } + ast::ImplItemKind::Type(_) if ii.generics.is_parameterized() => { + gate_feature_post!(&self, generic_associated_types, ii.span, + "generic associated types are unstable"); + } _ => {} } visit::walk_impl_item(self, ii); diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index c1819307928ba..01b462e898164 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1293,9 +1293,10 @@ impl<'a> Parser<'a> { let lo = self.span; let (name, node, generics) = if self.eat_keyword(keywords::Type) { - let TyParam {ident, bounds, default, ..} = self.parse_ty_param(vec![])?; + let (generics, TyParam {ident, bounds, default, ..}) = + self.parse_trait_item_assoc_ty(vec![])?; self.expect(&token::Semi)?; - (ident, TraitItemKind::Type(bounds, default), ast::Generics::default()) + (ident, TraitItemKind::Type(bounds, default), generics) } else if self.is_const_item() { self.expect_keyword(keywords::Const)?; let ident = self.parse_ident()?; @@ -4433,6 +4434,38 @@ impl<'a> Parser<'a> { }) } + /// Parses the following grammar: + /// TraitItemAssocTy = Ident ["<"...">"] [":" [TyParamBounds]] ["where" ...] ["=" Ty] + fn parse_trait_item_assoc_ty(&mut self, preceding_attrs: Vec) + -> PResult<'a, (ast::Generics, TyParam)> { + let span = self.span; + let ident = self.parse_ident()?; + let mut generics = self.parse_generics()?; + + // Parse optional colon and param bounds. + let bounds = if self.eat(&token::Colon) { + self.parse_ty_param_bounds()? + } else { + Vec::new() + }; + generics.where_clause = self.parse_where_clause()?; + + let default = if self.eat(&token::Eq) { + Some(self.parse_ty()?) + } else { + None + }; + + Ok((generics, TyParam { + attrs: preceding_attrs.into(), + ident, + id: ast::DUMMY_NODE_ID, + bounds, + default, + span, + })) + } + /// Parses (possibly empty) list of lifetime and type parameters, possibly including /// trailing comma and erroneous trailing attributes. pub fn parse_generic_params(&mut self) -> PResult<'a, (Vec, Vec)> { @@ -4974,12 +5007,18 @@ impl<'a> Parser<'a> { let vis = self.parse_visibility(false)?; let defaultness = self.parse_defaultness()?; let (name, node, generics) = if self.eat_keyword(keywords::Type) { + // This parses the grammar: + // ImplItemAssocTy = Ident ["<"...">"] ["where" ...] "=" Ty ";" let name = self.parse_ident()?; + let mut generics = self.parse_generics()?; + generics.where_clause = self.parse_where_clause()?; self.expect(&token::Eq)?; let typ = self.parse_ty()?; self.expect(&token::Semi)?; - (name, ast::ImplItemKind::Type(typ), ast::Generics::default()) + (name, ast::ImplItemKind::Type(typ), generics) } else if self.is_const_item() { + // This parses the grammar: + // ImplItemConst = "const" Ident ":" Ty "=" Expr ";" self.expect_keyword(keywords::Const)?; let name = self.parse_ident()?; self.expect(&token::Colon)?; diff --git a/src/test/compile-fail/feature-gate-generic_associated_types.rs b/src/test/compile-fail/feature-gate-generic_associated_types.rs new file mode 100644 index 0000000000000..a8fc8226f316a --- /dev/null +++ b/src/test/compile-fail/feature-gate-generic_associated_types.rs @@ -0,0 +1,17 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::ops::Deref; + +trait PointerFamily { + type Pointer: Deref; +} + +fn main() {} diff --git a/src/test/run-pass/rfc1598-generic-associated-types/construct_with_other_type.rs b/src/test/run-pass/rfc1598-generic-associated-types/construct_with_other_type.rs new file mode 100644 index 0000000000000..81475c6888d3f --- /dev/null +++ b/src/test/run-pass/rfc1598-generic-associated-types/construct_with_other_type.rs @@ -0,0 +1,25 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generic_associated_types)] + +trait Foo { + type Bar<'a, 'b>; +} + +trait Baz { + type Quux<'a>; +} + +impl Baz for T where T: Foo { + type Quux<'a> = ::Bar<'a, 'static>; +} + +fn main() {} diff --git a/src/test/run-pass/rfc1598-generic-associated-types/generic-associated-types-where.rs b/src/test/run-pass/rfc1598-generic-associated-types/generic-associated-types-where.rs new file mode 100644 index 0000000000000..cad8a96b8f081 --- /dev/null +++ b/src/test/run-pass/rfc1598-generic-associated-types/generic-associated-types-where.rs @@ -0,0 +1,32 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generic_associated_types)] + +// Checking the interaction with this other feature +#![feature(associated_type_defaults)] + +use std::fmt::{Display, Debug}; + +trait Foo { + type Assoc where Self: Sized; + type Assoc2 where T: Display; + type WithDefault where T: Debug = Iterator; +} + +struct Bar; + +impl Foo for Bar { + type Assoc = usize; + type Assoc2 = Vec; + type WithDefault<'a, T> = &'a Iterator; +} + +fn main() {} diff --git a/src/test/run-pass/rfc1598-generic-associated-types/iterable.rs b/src/test/run-pass/rfc1598-generic-associated-types/iterable.rs new file mode 100644 index 0000000000000..40b1d1312927c --- /dev/null +++ b/src/test/run-pass/rfc1598-generic-associated-types/iterable.rs @@ -0,0 +1,20 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generic_associated_types)] + +trait Iterable { + type Item<'a>; + type Iter<'a>: Iterator>; + + fn iter<'a>(&'a self) -> Self::Iter<'a>; +} + +fn main() {} diff --git a/src/test/run-pass/rfc1598-generic-associated-types/pointer_family.rs b/src/test/run-pass/rfc1598-generic-associated-types/pointer_family.rs new file mode 100644 index 0000000000000..0d0f1396969f3 --- /dev/null +++ b/src/test/run-pass/rfc1598-generic-associated-types/pointer_family.rs @@ -0,0 +1,44 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generic_associated_types)] + +use std::rc::Rc; +use std::sync::Arc; +use std::ops::Deref; + +trait PointerFamily { + type Pointer: Deref; + fn new(value: T) -> Self::Pointer; +} + +struct ArcFamily; + +impl PointerFamily for ArcFamily { + type Pointer = Arc; + fn new(value: T) -> Self::Pointer { + Arc::new(value) + } +} + +struct RcFamily; + +impl PointerFamily for RcFamily { + type Pointer = Rc; + fn new(value: T) -> Self::Pointer { + Rc::new(value) + } +} + +struct Foo { + bar: P::Pointer, +} + +fn main() {} diff --git a/src/test/run-pass/rfc1598-generic-associated-types/streaming_iterator.rs b/src/test/run-pass/rfc1598-generic-associated-types/streaming_iterator.rs new file mode 100644 index 0000000000000..58ec14d10756e --- /dev/null +++ b/src/test/run-pass/rfc1598-generic-associated-types/streaming_iterator.rs @@ -0,0 +1,32 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generic_associated_types)] + +use std::fmt::Display; + +trait StreamingIterator { + type Item<'a>; + // Applying the lifetime parameter `'a` to `Self::Item` inside the trait. + fn next<'a>(&'a self) -> Option>; +} + +struct Foo { + // Applying a concrete lifetime to the constructor outside the trait. + bar: ::Item<'static>, +} + +// Users can bound parameters by the type constructed by that trait's associated type constructor +// of a trait using HRTB. Both type equality bounds and trait bounds of this kind are valid: +//FIXME(sunjay): This next line should parse and be valid +//fn foo StreamingIterator=&'a [i32]>>(iter: T) { /* ... */ } +fn foo(iter: T) where T: StreamingIterator, for<'a> T::Item<'a>: Display { /* ... */ } + +fn main() {} diff --git a/src/test/run-pass/rfc1598-generic-associated-types/whitespace-before-generics.rs b/src/test/run-pass/rfc1598-generic-associated-types/whitespace-before-generics.rs new file mode 100644 index 0000000000000..892a925f3d569 --- /dev/null +++ b/src/test/run-pass/rfc1598-generic-associated-types/whitespace-before-generics.rs @@ -0,0 +1,35 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generic_associated_types)] + +// Checking the interaction with this other feature +#![feature(associated_type_defaults)] + +use std::fmt::{Display, Debug}; + +trait Foo { + type Assoc where Self: Sized; + type Assoc2 where T: Display; + type WithDefault = Iterator where T: Debug; + // No generics on this associated type + type NoGenerics; +} + +struct Bar; + +impl Foo for Bar { + type Assoc = usize; + type Assoc2 = Vec; + type WithDefault<'a, T> = &'a Iterator; + type NoGenerics = f64; +} + +fn main() {} diff --git a/src/test/ui/rfc1598-generic-associated-types/empty_generics.rs b/src/test/ui/rfc1598-generic-associated-types/empty_generics.rs new file mode 100644 index 0000000000000..a80875d28b3a7 --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/empty_generics.rs @@ -0,0 +1,17 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generic_associated_types)] + +trait Foo { + type Bar<,>; +} + +fn main() {} diff --git a/src/test/ui/rfc1598-generic-associated-types/empty_generics.stderr b/src/test/ui/rfc1598-generic-associated-types/empty_generics.stderr new file mode 100644 index 0000000000000..de0c1e310bcb8 --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/empty_generics.stderr @@ -0,0 +1,8 @@ +error: expected one of `>`, identifier, or lifetime, found `,` + --> $DIR/empty_generics.rs:14:14 + | +14 | type Bar<,>; + | ^ expected one of `>`, identifier, or lifetime here + +error: aborting due to previous error + diff --git a/src/test/ui/rfc1598-generic-associated-types/generic_associated_types_equals.rs b/src/test/ui/rfc1598-generic-associated-types/generic_associated_types_equals.rs new file mode 100644 index 0000000000000..6cb2aaf47ae37 --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/generic_associated_types_equals.rs @@ -0,0 +1,18 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generic_associated_types)] + +trait Foo { + type Bar; + type X where T = f64; +} + +fn main() {} diff --git a/src/test/ui/rfc1598-generic-associated-types/generic_associated_types_equals.stderr b/src/test/ui/rfc1598-generic-associated-types/generic_associated_types_equals.stderr new file mode 100644 index 0000000000000..152c239d8f749 --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/generic_associated_types_equals.stderr @@ -0,0 +1,24 @@ +error: equality constraints are not yet supported in where clauses (#20041) + --> $DIR/generic_associated_types_equals.rs:15:21 + | +15 | type X where T = f64; + | ^^^^^^^ + +error[E0412]: cannot find type `T` in this scope + --> $DIR/generic_associated_types_equals.rs:15:21 + | +15 | type X where T = f64; + | ^ not found in this scope + +error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions. + --> $DIR/generic_associated_types_equals.rs:14:14 + | +14 | type Bar; + | ^ + | + = note: #[deny(invalid_type_param_default)] on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #36887 + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/rfc1598-generic-associated-types/parse/in-trait-impl.rs b/src/test/ui/rfc1598-generic-associated-types/parse/in-trait-impl.rs new file mode 100644 index 0000000000000..bb4a285ec71ea --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/parse/in-trait-impl.rs @@ -0,0 +1,19 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Zparse-only + +#![feature(generic_associated_types)] + +impl Baz for T where T: Foo { + type Quux<'a> = ::Bar<'a, 'static>; +} + +fn main() {} diff --git a/src/test/ui/rfc1598-generic-associated-types/parse/in-trait.rs b/src/test/ui/rfc1598-generic-associated-types/parse/in-trait.rs new file mode 100644 index 0000000000000..3c3f20b9ff601 --- /dev/null +++ b/src/test/ui/rfc1598-generic-associated-types/parse/in-trait.rs @@ -0,0 +1,25 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Zparse-only + +#![feature(generic_associated_types)] + +trait Foo { + type Bar<'a>; + type Bar<'a, 'b>; + type Bar<'a, 'b,>; + type Bar<'a, 'b, T>; + type Bar<'a, 'b, T, U>; + type Bar<'a, 'b, T, U,>; + type Bar<'a, 'b, T: Debug, U,>; +} + +fn main() {}