Skip to content

Commit

Permalink
Auto merge of #43532 - petrochenkov:pgargs, r=nikomatsakis
Browse files Browse the repository at this point in the history
Desugar parenthesized generic arguments in HIR

Fixes ICE in #43431 and maybe some other similar issues.

r? @eddyb
  • Loading branch information
bors committed Aug 24, 2017

Unverified

This user has not yet uploaded their public signing key.
2 parents 560b6ca + 000f87a commit a12e4f8
Showing 18 changed files with 264 additions and 438 deletions.
15 changes: 15 additions & 0 deletions src/librustc/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -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.
14 changes: 3 additions & 11 deletions src/librustc/hir/intravisit.rs
Original file line number Diff line number Diff line change
@@ -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,
98 changes: 73 additions & 25 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
@@ -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;
@@ -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
@@ -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) {
@@ -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,
});
@@ -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.
@@ -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,
@@ -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 {
@@ -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,
}
}

@@ -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)
}
95 changes: 25 additions & 70 deletions src/librustc/hir/mod.rs
Original file line number Diff line number Diff line change
@@ -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};
@@ -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.
@@ -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,
}

/// 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.
Loading

0 comments on commit a12e4f8

Please sign in to comment.