Skip to content
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

Desugar parenthesized generic arguments in HIR #43532

Merged
merged 1 commit into from
Aug 24, 2017
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
15 changes: 15 additions & 0 deletions src/librustc/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,21 @@ attributes:
See also https://doc.rust-lang.org/book/first-edition/no-stdlib.html
"##,

E0214: r##"
A generic type was described using parentheses rather than angle brackets. For
example:

```compile_fail,E0214
fn main() {
let v: Vec(&str) = vec!["foo"];
}
```

This is not currently supported: `v` should be defined as `Vec<&str>`.
Parentheses are currently only used with generic types when defining parameters
for `Fn`-family traits.
"##,

E0261: r##"
When using a lifetime like `'a` in a type, it must be declared before being
used.
Expand Down
14 changes: 3 additions & 11 deletions src/librustc/hir/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -617,17 +617,9 @@ pub fn walk_path_segment<'v, V: Visitor<'v>>(visitor: &mut V,
pub fn walk_path_parameters<'v, V: Visitor<'v>>(visitor: &mut V,
_path_span: Span,
path_parameters: &'v PathParameters) {
match *path_parameters {
AngleBracketedParameters(ref data) => {
walk_list!(visitor, visit_ty, &data.types);
walk_list!(visitor, visit_lifetime, &data.lifetimes);
walk_list!(visitor, visit_assoc_type_binding, &data.bindings);
}
ParenthesizedParameters(ref data) => {
walk_list!(visitor, visit_ty, &data.inputs);
walk_list!(visitor, visit_ty, &data.output);
}
}
walk_list!(visitor, visit_lifetime, &path_parameters.lifetimes);
walk_list!(visitor, visit_ty, &path_parameters.types);
walk_list!(visitor, visit_assoc_type_binding, &path_parameters.bindings);
}

pub fn walk_assoc_type_binding<'v, V: Visitor<'v>>(visitor: &mut V,
Expand Down
98 changes: 73 additions & 25 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,16 @@ use hir::map::{Definitions, DefKey, REGULAR_SPACE};
use hir::map::definitions::DefPathData;
use hir::def_id::{DefIndex, DefId, CRATE_DEF_INDEX};
use hir::def::{Def, PathResolution};
use lint::builtin::PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES;
use rustc_data_structures::indexed_vec::IndexVec;
use session::Session;
use util::common::FN_OUTPUT_NAME;
use util::nodemap::{DefIdMap, FxHashMap, NodeMap};

use std::collections::BTreeMap;
use std::fmt::Debug;
use std::iter;
use std::mem;

use syntax::attr;
use syntax::ast::*;
use syntax::errors;
Expand Down Expand Up @@ -160,6 +161,12 @@ struct LoweredNodeId {
hir_id: hir::HirId,
}

enum ParenthesizedGenericArgs {
Ok,
Warn,
Err,
}

impl<'a> LoweringContext<'a> {
fn lower_crate(mut self, c: &Crate) -> hir::Crate {
/// Full-crate AST visitor that inserts into a fresh
Expand Down Expand Up @@ -749,6 +756,21 @@ impl<'a> LoweringContext<'a> {
Def::Trait(def_id) if i + 1 == proj_start => Some(def_id),
_ => None
};
let parenthesized_generic_args = match resolution.base_def() {
// `a::b::Trait(Args)`
Def::Trait(..) if i + 1 == proj_start => ParenthesizedGenericArgs::Ok,
// `a::b::Trait(Args)::TraitItem`
Def::Method(..) |
Def::AssociatedConst(..) |
Def::AssociatedTy(..) if i + 2 == proj_start => ParenthesizedGenericArgs::Ok,
// Avoid duplicated errors
Def::Err => ParenthesizedGenericArgs::Ok,
// An error
Def::Struct(..) | Def::Enum(..) | Def::Union(..) | Def::TyAlias(..) |
Def::Variant(..) if i + 1 == proj_start => ParenthesizedGenericArgs::Err,
// A warning for now, for compatibility reasons
_ => ParenthesizedGenericArgs::Warn,
};

let num_lifetimes = type_def_id.map_or(0, |def_id| {
if let Some(&n) = self.type_def_lifetime_params.get(&def_id) {
Expand All @@ -759,7 +781,8 @@ impl<'a> LoweringContext<'a> {
self.type_def_lifetime_params.insert(def_id, n);
n
});
self.lower_path_segment(p.span, segment, param_mode, num_lifetimes)
self.lower_path_segment(p.span, segment, param_mode, num_lifetimes,
parenthesized_generic_args)
}).collect(),
span: p.span,
});
Expand Down Expand Up @@ -794,7 +817,8 @@ impl<'a> LoweringContext<'a> {
// 3. `<<std::vec::Vec<T>>::IntoIter>::Item`
// * final path is `<<<std::vec::Vec<T>>::IntoIter>::Item>::clone`
for (i, segment) in p.segments.iter().enumerate().skip(proj_start) {
let segment = P(self.lower_path_segment(p.span, segment, param_mode, 0));
let segment = P(self.lower_path_segment(p.span, segment, param_mode, 0,
ParenthesizedGenericArgs::Warn));
let qpath = hir::QPath::TypeRelative(ty, segment);

// It's finished, return the extension of the right node type.
Expand Down Expand Up @@ -827,7 +851,8 @@ impl<'a> LoweringContext<'a> {
hir::Path {
def: self.expect_full_def(id),
segments: segments.map(|segment| {
self.lower_path_segment(p.span, segment, param_mode, 0)
self.lower_path_segment(p.span, segment, param_mode, 0,
ParenthesizedGenericArgs::Err)
}).chain(name.map(|name| {
hir::PathSegment {
name,
Expand All @@ -851,29 +876,37 @@ impl<'a> LoweringContext<'a> {
path_span: Span,
segment: &PathSegment,
param_mode: ParamMode,
expected_lifetimes: usize)
expected_lifetimes: usize,
parenthesized_generic_args: ParenthesizedGenericArgs)
-> hir::PathSegment {
let mut parameters = if let Some(ref parameters) = segment.parameters {
let msg = "parenthesized parameters may only be used with a trait";
match **parameters {
PathParameters::AngleBracketed(ref data) => {
let data = self.lower_angle_bracketed_parameter_data(data, param_mode);
hir::AngleBracketedParameters(data)
self.lower_angle_bracketed_parameter_data(data, param_mode)
}
PathParameters::Parenthesized(ref data) => {
hir::ParenthesizedParameters(self.lower_parenthesized_parameter_data(data))
PathParameters::Parenthesized(ref data) => match parenthesized_generic_args {
ParenthesizedGenericArgs::Ok => self.lower_parenthesized_parameter_data(data),
ParenthesizedGenericArgs::Warn => {
self.sess.buffer_lint(PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES,
CRATE_NODE_ID, data.span, msg.into());
hir::PathParameters::none()
}
ParenthesizedGenericArgs::Err => {
struct_span_err!(self.sess, data.span, E0214, "{}", msg)
.span_label(data.span, "only traits may use parentheses").emit();
hir::PathParameters::none()
}
}
}
} else {
let data = self.lower_angle_bracketed_parameter_data(&Default::default(), param_mode);
hir::AngleBracketedParameters(data)
self.lower_angle_bracketed_parameter_data(&Default::default(), param_mode)
};

if let hir::AngleBracketedParameters(ref mut data) = parameters {
if data.lifetimes.is_empty() {
data.lifetimes = (0..expected_lifetimes).map(|_| {
self.elided_lifetime(path_span)
}).collect();
}
if !parameters.parenthesized && parameters.lifetimes.is_empty() {
parameters.lifetimes = (0..expected_lifetimes).map(|_| {
self.elided_lifetime(path_span)
}).collect();
}

hir::PathSegment {
Expand All @@ -885,24 +918,38 @@ impl<'a> LoweringContext<'a> {
fn lower_angle_bracketed_parameter_data(&mut self,
data: &AngleBracketedParameterData,
param_mode: ParamMode)
-> hir::AngleBracketedParameterData {
-> hir::PathParameters {
let &AngleBracketedParameterData { ref lifetimes, ref types, ref bindings, .. } = data;
hir::AngleBracketedParameterData {
hir::PathParameters {
lifetimes: self.lower_lifetimes(lifetimes),
types: types.iter().map(|ty| self.lower_ty(ty)).collect(),
infer_types: types.is_empty() && param_mode == ParamMode::Optional,
bindings: bindings.iter().map(|b| self.lower_ty_binding(b)).collect(),
parenthesized: false,
}
}

fn lower_parenthesized_parameter_data(&mut self,
data: &ParenthesizedParameterData)
-> hir::ParenthesizedParameterData {
-> hir::PathParameters {
let &ParenthesizedParameterData { ref inputs, ref output, span } = data;
hir::ParenthesizedParameterData {
inputs: inputs.iter().map(|ty| self.lower_ty(ty)).collect(),
output: output.as_ref().map(|ty| self.lower_ty(ty)),
span,
let inputs = inputs.iter().map(|ty| self.lower_ty(ty)).collect();
let mk_tup = |this: &mut Self, tys, span| {
P(hir::Ty { node: hir::TyTup(tys), id: this.next_id().node_id, span })
};

hir::PathParameters {
lifetimes: hir::HirVec::new(),
types: hir_vec![mk_tup(self, inputs, span)],
infer_types: false,
bindings: hir_vec![hir::TypeBinding {
id: self.next_id().node_id,
name: Symbol::intern(FN_OUTPUT_NAME),
ty: output.as_ref().map(|ty| self.lower_ty(&ty))
.unwrap_or_else(|| mk_tup(self, hir::HirVec::new(), span)),
span: output.as_ref().map_or(span, |ty| ty.span),
}],
parenthesized: true,
}
}

Expand Down Expand Up @@ -1877,7 +1924,8 @@ impl<'a> LoweringContext<'a> {
hir::ExprCall(f, args.iter().map(|x| self.lower_expr(x)).collect())
}
ExprKind::MethodCall(ref seg, ref args) => {
let hir_seg = self.lower_path_segment(e.span, seg, ParamMode::Optional, 0);
let hir_seg = self.lower_path_segment(e.span, seg, ParamMode::Optional, 0,
ParenthesizedGenericArgs::Err);
let args = args.iter().map(|x| self.lower_expr(x)).collect();
hir::ExprMethodCall(hir_seg, seg.span, args)
}
Expand Down
95 changes: 25 additions & 70 deletions src/librustc/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ pub use self::TyParamBound::*;
pub use self::UnOp::*;
pub use self::UnsafeSource::*;
pub use self::Visibility::{Public, Inherited};
pub use self::PathParameters::*;

use hir::def::Def;
use hir::def_id::{DefId, DefIndex, CRATE_DEF_INDEX};
Expand Down Expand Up @@ -227,65 +226,7 @@ impl PathSegment {
}

#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub enum PathParameters {
/// The `<'a, A,B,C>` in `foo::bar::baz::<'a, A,B,C>`
AngleBracketedParameters(AngleBracketedParameterData),
/// The `(A,B)` and `C` in `Foo(A,B) -> C`
ParenthesizedParameters(ParenthesizedParameterData),
}

impl PathParameters {
pub fn none() -> PathParameters {
AngleBracketedParameters(AngleBracketedParameterData {
lifetimes: HirVec::new(),
types: HirVec::new(),
infer_types: true,
bindings: HirVec::new(),
})
}

/// Returns the types that the user wrote. Note that these do not necessarily map to the type
/// parameters in the parenthesized case.
pub fn types(&self) -> HirVec<&P<Ty>> {
match *self {
AngleBracketedParameters(ref data) => {
data.types.iter().collect()
}
ParenthesizedParameters(ref data) => {
data.inputs
.iter()
.chain(data.output.iter())
.collect()
}
}
}

pub fn lifetimes(&self) -> HirVec<&Lifetime> {
match *self {
AngleBracketedParameters(ref data) => {
data.lifetimes.iter().collect()
}
ParenthesizedParameters(_) => {
HirVec::new()
}
}
}

pub fn bindings(&self) -> HirVec<&TypeBinding> {
match *self {
AngleBracketedParameters(ref data) => {
data.bindings.iter().collect()
}
ParenthesizedParameters(_) => {
HirVec::new()
}
}
}
}

/// A path like `Foo<'a, T>`
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct AngleBracketedParameterData {
pub struct PathParameters {
/// The lifetime parameters for this path segment.
pub lifetimes: HirVec<Lifetime>,
/// The type parameters for this path segment, if present.
Expand All @@ -298,19 +239,33 @@ pub struct AngleBracketedParameterData {
/// Bindings (equality constraints) on associated types, if present.
/// E.g., `Foo<A=Bar>`.
pub bindings: HirVec<TypeBinding>,
/// Were parameters written in parenthesized form `Fn(T) -> U`?
/// This is required mostly for pretty-printing and diagnostics,
/// but also for changing lifetime elision rules to be "function-like".
pub parenthesized: bool,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see, with this here it's probably okay.

}

/// A path like `Foo(A,B) -> C`
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct ParenthesizedParameterData {
/// Overall span
pub span: Span,

/// `(A,B)`
pub inputs: HirVec<P<Ty>>,
impl PathParameters {
pub fn none() -> Self {
Self {
lifetimes: HirVec::new(),
types: HirVec::new(),
infer_types: true,
bindings: HirVec::new(),
parenthesized: false,
}
}

/// `C`
pub output: Option<P<Ty>>,
pub fn inputs(&self) -> &[P<Ty>] {
if self.parenthesized {
if let Some(ref ty) = self.types.get(0) {
if let TyTup(ref tys) = ty.node {
return tys;
}
}
}
bug!("PathParameters::inputs: not a `Fn(T) -> U`");
}
}

/// The AST represents all type param bounds as types.
Expand Down
Loading