Skip to content

Generic Associated Types in Trait Paths - Ast part #79266

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1845,6 +1845,7 @@ impl UintTy {
pub struct AssocTyConstraint {
pub id: NodeId,
pub ident: Ident,
pub gen_args: Option<GenericArgs>,
pub kind: AssocTyConstraintKind,
pub span: Span,
}
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -441,11 +441,14 @@ pub fn noop_flat_map_arm<T: MutVisitor>(mut arm: Arm, vis: &mut T) -> SmallVec<[
}

pub fn noop_visit_ty_constraint<T: MutVisitor>(
AssocTyConstraint { id, ident, kind, span }: &mut AssocTyConstraint,
AssocTyConstraint { id, ident, gen_args, kind, span }: &mut AssocTyConstraint,
vis: &mut T,
) {
vis.visit_id(id);
vis.visit_ident(ident);
if let Some(ref mut gen_args) = gen_args {
vis.visit_generic_args(gen_args);
}
match kind {
AssocTyConstraintKind::Equality { ref mut ty } => {
vis.visit_ty(ty);
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,9 @@ pub fn walk_assoc_ty_constraint<'a, V: Visitor<'a>>(
constraint: &'a AssocTyConstraint,
) {
visitor.visit_ident(constraint.ident);
if let Some(ref gen_args) = constraint.gen_args {
visitor.visit_generic_args(gen_args.span(), gen_args);
}
match constraint.kind {
AssocTyConstraintKind::Equality { ref ty } => {
visitor.visit_ty(ty);
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1038,6 +1038,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
) -> hir::TypeBinding<'hir> {
debug!("lower_assoc_ty_constraint(constraint={:?}, itctx={:?})", constraint, itctx);

if let Some(ref gen_args) = constraint.gen_args {
self.sess.span_fatal(
gen_args.span(),
"generic associated types in trait paths are currently not implemented",
);
}

let kind = match constraint.kind {
AssocTyConstraintKind::Equality { ref ty } => {
hir::TypeBindingKind::Equality { ty: self.lower_ty(ty, itctx) }
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1372,16 +1372,18 @@ fn deny_equality_constraints(
if param.ident == *ident {
let param = ident;
match &full_path.segments[qself.position..] {
[PathSegment { ident, .. }] => {
[PathSegment { ident, args, .. }] => {
// Make a new `Path` from `foo::Bar` to `Foo<Bar = RhsTy>`.
let mut assoc_path = full_path.clone();
// Remove `Bar` from `Foo::Bar`.
assoc_path.segments.pop();
let len = assoc_path.segments.len() - 1;
let gen_args = args.as_ref().map(|p| (**p).clone());
// Build `<Bar = RhsTy>`.
let arg = AngleBracketedArg::Constraint(AssocTyConstraint {
id: rustc_ast::node_id::DUMMY_NODE_ID,
ident: *ident,
gen_args,
kind: AssocTyConstraintKind::Equality {
ty: predicate.rhs_ty.clone(),
},
Expand Down
109 changes: 83 additions & 26 deletions compiler/rustc_parse/src/parser/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ use super::{Parser, TokenType};
use crate::maybe_whole;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Token};
use rustc_ast::{
self as ast, AngleBracketedArg, AngleBracketedArgs, GenericArg, ParenthesizedArgs,
};
use rustc_ast::{self as ast, AngleBracketedArg, AngleBracketedArgs, ParenthesizedArgs};
use rustc_ast::{AnonConst, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode};
use rustc_ast::{GenericArg, GenericArgs};
use rustc_ast::{Path, PathSegment, QSelf};
use rustc_errors::{pluralize, Applicability, PResult};
use rustc_span::source_map::{BytePos, Span};
Expand Down Expand Up @@ -414,32 +413,40 @@ impl<'a> Parser<'a> {

/// Parses a single argument in the angle arguments `<...>` of a path segment.
fn parse_angle_arg(&mut self) -> PResult<'a, Option<AngleBracketedArg>> {
if self.check_ident() && self.look_ahead(1, |t| matches!(t.kind, token::Eq | token::Colon))
{
// Parse associated type constraint.
let lo = self.token.span;
let ident = self.parse_ident()?;
let kind = if self.eat(&token::Eq) {
let ty = self.parse_assoc_equality_term(ident, self.prev_token.span)?;
AssocTyConstraintKind::Equality { ty }
} else if self.eat(&token::Colon) {
let bounds = self.parse_generic_bounds(Some(self.prev_token.span))?;
AssocTyConstraintKind::Bound { bounds }
} else {
unreachable!();
};
let lo = self.token.span;
let arg = self.parse_generic_arg()?;
match arg {
Some(arg) => {
if self.check(&token::Colon) | self.check(&token::Eq) {
let (ident, gen_args) = self.get_ident_from_generic_arg(arg, lo)?;
let kind = if self.eat(&token::Colon) {
// Parse associated type constraint bound.

let bounds = self.parse_generic_bounds(Some(self.prev_token.span))?;
AssocTyConstraintKind::Bound { bounds }
} else if self.eat(&token::Eq) {
// Parse associated type equality constraint

let ty = self.parse_assoc_equality_term(ident, self.prev_token.span)?;
AssocTyConstraintKind::Equality { ty }
} else {
unreachable!();
};

let span = lo.to(self.prev_token.span);
let span = lo.to(self.prev_token.span);

// Gate associated type bounds, e.g., `Iterator<Item: Ord>`.
if let AssocTyConstraintKind::Bound { .. } = kind {
self.sess.gated_spans.gate(sym::associated_type_bounds, span);
// Gate associated type bounds, e.g., `Iterator<Item: Ord>`.
if let AssocTyConstraintKind::Bound { .. } = kind {
self.sess.gated_spans.gate(sym::associated_type_bounds, span);
}
let constraint =
AssocTyConstraint { id: ast::DUMMY_NODE_ID, ident, gen_args, kind, span };
Ok(Some(AngleBracketedArg::Constraint(constraint)))
} else {
Ok(Some(AngleBracketedArg::Arg(arg)))
}
}

let constraint = AssocTyConstraint { id: ast::DUMMY_NODE_ID, ident, kind, span };
Ok(Some(AngleBracketedArg::Constraint(constraint)))
} else {
Ok(self.parse_generic_arg()?.map(AngleBracketedArg::Arg))
_ => Ok(None),
}
}

Expand Down Expand Up @@ -534,4 +541,54 @@ impl<'a> Parser<'a> {
};
Ok(Some(arg))
}

fn get_ident_from_generic_arg(
&self,
gen_arg: GenericArg,
lo: Span,
) -> PResult<'a, (Ident, Option<GenericArgs>)> {
let gen_arg_span = gen_arg.span();
match gen_arg {
GenericArg::Type(t) => match t.into_inner().kind {
ast::TyKind::Path(qself, mut path) => {
if let Some(qself) = qself {
let mut err = self.struct_span_err(
gen_arg_span,
"qualified paths cannot be used in associated type constraints",
);
err.span_label(
qself.path_span,
"not allowed in associated type constraints",
);
return Err(err);
}
if path.segments.len() == 1 {
let path_seg = path.segments.remove(0);
let ident = path_seg.ident;
let gen_args = path_seg.args.map(|args| args.into_inner());
return Ok((ident, gen_args));
}
let err = self.struct_span_err(
path.span,
"paths with multiple segments cannot be used in associated type constraints",
);
return Err(err);
}
_ => {
let span = lo.to(self.prev_token.span);
let err = self.struct_span_err(
span,
"only path types can be used in associated type constraints",
);
return Err(err);
}
},
_ => {
let span = lo.to(self.prev_token.span);
let err = self
.struct_span_err(span, "only types can be used in associated type constraints");
return Err(err);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#![feature(generic_associated_types)]
//~^ WARNING: the feature `generic_associated_types` is incomplete

trait X {
type Y<'a>;
}

fn f1<'a>(arg : Box<dyn X<Y = B = &'a ()>>) {}
//~^ ERROR: expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `=`

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error: expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `=`
--> $DIR/trait-path-expected-token.rs:8:33
|
LL | fn f1<'a>(arg : Box<dyn X<Y = B = &'a ()>>) {}
| ^ expected one of 7 possible tokens

warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/trait-path-expected-token.rs:1:12
|
LL | #![feature(generic_associated_types)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information

error: aborting due to previous error; 1 warning emitted

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#![feature(generic_associated_types)]
//~^ WARNING: the feature `generic_associated_types` is incomplete

mod error1 {
trait X {
type Y<'a>;
}

fn f1<'a>(arg : Box<dyn X< 1 = 32 >>) {}
//~^ ERROR: expected expression, found `)`
}

mod error2 {

trait X {
type Y<'a>;
}

fn f2<'a>(arg : Box<dyn X< { 1 } = 32 >>) {}
//~^ ERROR: only types can be used in associated type constraints
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
error: expected expression, found `)`
--> $DIR/trait-path-expressions.rs:9:39
|
LL | fn f1<'a>(arg : Box<dyn X< 1 = 32 >>) {}
| - ^ expected expression
| |
| while parsing a const generic argument starting here

error: only types can be used in associated type constraints
--> $DIR/trait-path-expressions.rs:19:30
|
LL | fn f2<'a>(arg : Box<dyn X< { 1 } = 32 >>) {}
| ^^^^^

warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/trait-path-expressions.rs:1:12
|
LL | #![feature(generic_associated_types)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information

error: aborting due to 2 previous errors; 1 warning emitted

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#![feature(generic_associated_types)]
//~^ WARNING: the feature `generic_associated_types` is incomplete

trait X {
type Y<'a>;
}

const _: () = {
fn f1<'a>(arg : Box<dyn X< : 32 >>) {}
//~^ ERROR: expected one of `>`, const, lifetime, or type, found `:`
//~| ERROR: expected parameter name, found `>`
//~| ERROR: expected one of `!`, `)`, `+`, `,`, or `::`, found `>`
//~| ERROR: constant provided when a type was expected
};

const _: () = {
fn f1<'a>(arg : Box<dyn X< = 32 >>) {}
//~^ ERROR: expected one of `>`, const, lifetime, or type, found `=`
};

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
error: expected one of `>`, const, lifetime, or type, found `:`
--> $DIR/trait-path-missing-gen_arg.rs:9:30
|
LL | fn f1<'a>(arg : Box<dyn X< : 32 >>) {}
| ^ expected one of `>`, const, lifetime, or type
|
help: expressions must be enclosed in braces to be used as const generic arguments
|
LL | fn f1<'a>(arg : Box<{ dyn X< : 32 } >>) {}
| ^ ^

error: expected parameter name, found `>`
--> $DIR/trait-path-missing-gen_arg.rs:9:36
|
LL | fn f1<'a>(arg : Box<dyn X< : 32 >>) {}
| ^ expected parameter name

error: expected one of `!`, `)`, `+`, `,`, or `::`, found `>`
--> $DIR/trait-path-missing-gen_arg.rs:9:36
|
LL | fn f1<'a>(arg : Box<dyn X< : 32 >>) {}
| ^
| |
| expected one of `!`, `)`, `+`, `,`, or `::`
| help: missing `,`

error: expected one of `>`, const, lifetime, or type, found `=`
--> $DIR/trait-path-missing-gen_arg.rs:17:30
|
LL | fn f1<'a>(arg : Box<dyn X< = 32 >>) {}
| ^ expected one of `>`, const, lifetime, or type

warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/trait-path-missing-gen_arg.rs:1:12
|
LL | #![feature(generic_associated_types)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information

error[E0747]: constant provided when a type was expected
--> $DIR/trait-path-missing-gen_arg.rs:9:23
|
LL | fn f1<'a>(arg : Box<dyn X< : 32 >>) {}
| ^^^^^^^^^^^

error: aborting due to 5 previous errors; 1 warning emitted

For more information about this error, try `rustc --explain E0747`.
35 changes: 35 additions & 0 deletions src/test/ui/generic-associated-types/parse/trait-path-segments.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#![feature(generic_associated_types)]
//~^ WARNING: the feature `generic_associated_types` is incomplete

const _: () = {
trait X {
type Y<'a>;
}

fn f1<'a>(arg : Box<dyn X<X::Y = u32>>) {}
//~^ ERROR: paths with multiple segments cannot be used in associated type constraints
};

const _: () = {
trait X {
type Y<'a>;
}

trait Z {}

impl<T : X<<Self as X>::Y<'a> = &'a u32>> Z for T {}
//~^ ERROR: qualified paths cannot be used in associated type constraints
};

const _: () = {
trait X {
type Y<'a>;
}

trait Z {}

impl<T : X<X::Y<'a> = &'a u32>> Z for T {}
//~^ ERROR: paths with multiple segments cannot be used in associated type constraints
};

fn main() {}
Loading