Skip to content

Commit

Permalink
Auto merge of #58191 - varkor:const-generics-ast, r=petrochenkov
Browse files Browse the repository at this point in the history
Add const generics to the AST

This is mostly split out from #53645 in an effort to make progress merging const generics piecewise instead of in one go.

cc @yodaldevoid, @petrochenkov

r? @eddyb
  • Loading branch information
bors committed Feb 8, 2019
2 parents d173180 + f2fe71c commit 43e04fb
Show file tree
Hide file tree
Showing 116 changed files with 1,085 additions and 709 deletions.
5 changes: 4 additions & 1 deletion src/librustc/hir/def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ pub enum Def {
AssociatedExistential(DefId),
PrimTy(hir::PrimTy),
TyParam(DefId),
ConstParam(DefId),
SelfTy(Option<DefId> /* trait */, Option<DefId> /* impl */),
ToolMod, // e.g., `rustfmt` in `#[rustfmt::skip]`

Expand Down Expand Up @@ -265,7 +266,8 @@ impl Def {
Def::Fn(id) | Def::Mod(id) | Def::Static(id, _) |
Def::Variant(id) | Def::VariantCtor(id, ..) | Def::Enum(id) |
Def::TyAlias(id) | Def::TraitAlias(id) |
Def::AssociatedTy(id) | Def::TyParam(id) | Def::Struct(id) | Def::StructCtor(id, ..) |
Def::AssociatedTy(id) | Def::TyParam(id) | Def::ConstParam(id) | Def::Struct(id) |
Def::StructCtor(id, ..) |
Def::Union(id) | Def::Trait(id) | Def::Method(id) | Def::Const(id) |
Def::AssociatedConst(id) | Def::Macro(id, ..) |
Def::Existential(id) | Def::AssociatedExistential(id) | Def::ForeignTy(id) => {
Expand Down Expand Up @@ -322,6 +324,7 @@ impl Def {
Def::Const(..) => "constant",
Def::AssociatedConst(..) => "associated constant",
Def::TyParam(..) => "type parameter",
Def::ConstParam(..) => "const parameter",
Def::PrimTy(..) => "builtin type",
Def::Local(..) => "local variable",
Def::Upvar(..) => "closure capture",
Expand Down
79 changes: 47 additions & 32 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1157,6 +1157,15 @@ impl<'a> LoweringContext<'a> {
match arg {
ast::GenericArg::Lifetime(lt) => GenericArg::Lifetime(self.lower_lifetime(&lt)),
ast::GenericArg::Type(ty) => GenericArg::Type(self.lower_ty_direct(&ty, itctx)),
ast::GenericArg::Const(ct) => {
// FIXME(const_generics): const generics are not yet defined in the HIR.
self.sess.struct_span_err(
ct.value.span,
"const generics in any position are currently unsupported",
).emit();
self.sess.abort_if_errors();
bug!();
}
}
}

Expand Down Expand Up @@ -2441,7 +2450,7 @@ impl<'a> LoweringContext<'a> {
|this| this.lower_param_bounds(&param.bounds, itctx.reborrow()),
);

match param.kind {
let (name, kind) = match param.kind {
GenericParamKind::Lifetime => {
let was_collecting_in_band = self.is_collecting_in_band_lifetimes;
self.is_collecting_in_band_lifetimes = false;
Expand All @@ -2457,22 +2466,14 @@ impl<'a> LoweringContext<'a> {
| hir::LifetimeName::Static => hir::ParamName::Plain(lt.name.ident()),
hir::LifetimeName::Error => ParamName::Error,
};
let param = hir::GenericParam {
id: lt.id,
hir_id: lt.hir_id,
name: param_name,
span: lt.span,
pure_wrt_drop: attr::contains_name(&param.attrs, "may_dangle"),
attrs: self.lower_attrs(&param.attrs),
bounds,
kind: hir::GenericParamKind::Lifetime {
kind: hir::LifetimeParamKind::Explicit,
}

let kind = hir::GenericParamKind::Lifetime {
kind: hir::LifetimeParamKind::Explicit
};

self.is_collecting_in_band_lifetimes = was_collecting_in_band;

param
(param_name, kind)
}
GenericParamKind::Type { ref default, .. } => {
// Don't expose `Self` (recovered "keyword used as ident" parse error).
Expand All @@ -2491,27 +2492,41 @@ impl<'a> LoweringContext<'a> {
.chain(params)
.collect();
}
let LoweredNodeId { node_id, hir_id } = self.lower_node_id(param.id);

hir::GenericParam {
id: node_id,
hir_id,
name: hir::ParamName::Plain(ident),
pure_wrt_drop: attr::contains_name(&param.attrs, "may_dangle"),
attrs: self.lower_attrs(&param.attrs),
bounds,
span: ident.span,
kind: hir::GenericParamKind::Type {
default: default.as_ref().map(|x| {
self.lower_ty(x, ImplTraitContext::disallowed())
}),
synthetic: param.attrs.iter()
.filter(|attr| attr.check_name("rustc_synthetic"))
.map(|_| hir::SyntheticTyParamKind::ImplTrait)
.next(),
}
}
let kind = hir::GenericParamKind::Type {
default: default.as_ref().map(|x| {
self.lower_ty(x, ImplTraitContext::disallowed())
}),
synthetic: param.attrs.iter()
.filter(|attr| attr.check_name("rustc_synthetic"))
.map(|_| hir::SyntheticTyParamKind::ImplTrait)
.next(),
};

(hir::ParamName::Plain(ident), kind)
}
GenericParamKind::Const { .. } => {
// FIXME(const_generics): const generics are not yet defined in the HIR.
self.sess.struct_span_err(
param.ident.span,
"const generics in any position are currently unsupported",
).emit();
self.sess.abort_if_errors();
bug!();
}
};

let LoweredNodeId { node_id, hir_id } = self.lower_node_id(param.id);

hir::GenericParam {
id: node_id,
hir_id,
name,
span: param.ident.span,
pure_wrt_drop: attr::contains_name(&param.attrs, "may_dangle"),
attrs: self.lower_attrs(&param.attrs),
bounds,
kind,
}
}

Expand Down
1 change: 1 addition & 0 deletions src/librustc/hir/map/def_collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
let def_path_data = match param.kind {
GenericParamKind::Lifetime { .. } => DefPathData::LifetimeParam(name),
GenericParamKind::Type { .. } => DefPathData::TypeParam(name),
GenericParamKind::Const { .. } => DefPathData::ConstParam(name),
};
self.create_def(param.id, def_path_data, REGULAR_SPACE, param.ident.span);

Expand Down
8 changes: 6 additions & 2 deletions src/librustc/hir/map/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,10 +356,12 @@ pub enum DefPathData {
/// A closure expression
ClosureExpr,
// Subportions of items
/// A type parameter (generic parameter)
/// A type (generic) parameter
TypeParam(InternedString),
/// A lifetime definition
/// A lifetime (generic) parameter
LifetimeParam(InternedString),
/// A const (generic) parameter
ConstParam(InternedString),
/// A variant of a enum
EnumVariant(InternedString),
/// A struct field
Expand Down Expand Up @@ -641,6 +643,7 @@ impl DefPathData {
MacroDef(name) |
TypeParam(name) |
LifetimeParam(name) |
ConstParam(name) |
EnumVariant(name) |
Field(name) |
GlobalMetaData(name) => Some(name),
Expand Down Expand Up @@ -669,6 +672,7 @@ impl DefPathData {
MacroDef(name) |
TypeParam(name) |
LifetimeParam(name) |
ConstParam(name) |
EnumVariant(name) |
Field(name) |
GlobalMetaData(name) => {
Expand Down
1 change: 1 addition & 0 deletions src/librustc/ich/impls_hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1046,6 +1046,7 @@ impl_stable_hash_for!(enum hir::def::Def {
AssociatedExistential(def_id),
PrimTy(prim_ty),
TyParam(def_id),
ConstParam(def_id),
SelfTy(trait_def_id, impl_def_id),
ForeignTy(def_id),
Fn(def_id),
Expand Down
1 change: 1 addition & 0 deletions src/librustc/ty/item_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
data @ DefPathData::Module(..) |
data @ DefPathData::TypeParam(..) |
data @ DefPathData::LifetimeParam(..) |
data @ DefPathData::ConstParam(..) |
data @ DefPathData::EnumVariant(..) |
data @ DefPathData::Field(..) |
data @ DefPathData::AnonConst |
Expand Down
1 change: 1 addition & 0 deletions src/librustc/util/ppaux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ impl PrintContext {
DefPathData::ClosureExpr |
DefPathData::TypeParam(_) |
DefPathData::LifetimeParam(_) |
DefPathData::ConstParam(_) |
DefPathData::Field(_) |
DefPathData::StructCtor |
DefPathData::AnonConst |
Expand Down
134 changes: 118 additions & 16 deletions src/librustc_passes/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
// or type checking or some other kind of complex analysis.

use std::mem;
use syntax::print::pprust;
use rustc::lint;
use rustc::session::Session;
use rustc_data_structures::fx::FxHashMap;
use syntax::ast::*;
use syntax::attr;
use syntax::source_map::Spanned;
Expand Down Expand Up @@ -271,7 +273,74 @@ impl<'a> AstValidator<'a> {
_ => None,
}
}
}

enum GenericPosition {
Param,
Arg,
}

fn validate_generics_order<'a>(
handler: &errors::Handler,
generics: impl Iterator<Item = (ParamKindOrd, Span, Option<String>)>,
pos: GenericPosition,
span: Span,
) {
let mut max_param: Option<ParamKindOrd> = None;
let mut out_of_order = FxHashMap::default();
let mut param_idents = vec![];

for (kind, span, ident) in generics {
if let Some(ident) = ident {
param_idents.push((kind, param_idents.len(), ident));
}
let max_param = &mut max_param;
match max_param {
Some(max_param) if *max_param > kind => {
let entry = out_of_order.entry(kind).or_insert((*max_param, vec![]));
entry.1.push(span);
}
Some(_) | None => *max_param = Some(kind),
};
}

let mut ordered_params = "<".to_string();
if !out_of_order.is_empty() {
param_idents.sort_by_key(|&(po, i, _)| (po, i));
let mut first = true;
for (_, _, ident) in param_idents {
if !first {
ordered_params += ", ";
}
ordered_params += &ident;
first = false;
}
}
ordered_params += ">";

let pos_str = match pos {
GenericPosition::Param => "parameter",
GenericPosition::Arg => "argument",
};

for (param_ord, (max_param, spans)) in out_of_order {
let mut err = handler.struct_span_err(spans,
&format!(
"{} {pos}s must be declared prior to {} {pos}s",
param_ord,
max_param,
pos = pos_str,
));
if let GenericPosition::Param = pos {
err.span_suggestion(
span,
&format!("reorder the {}s: lifetimes, then types, then consts", pos_str),
ordered_params.clone(),
Applicability::MachineApplicable,
);
}
err.emit();
}
}

impl<'a> Visitor<'a> for AstValidator<'a> {
Expand Down Expand Up @@ -412,6 +481,26 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
.note("only trait implementations may be annotated with default").emit();
}
}
ItemKind::Fn(_, header, ref generics, _) => {
// We currently do not permit const generics in `const fn`, as
// this is tantamount to allowing compile-time dependent typing.
if header.constness.node == Constness::Const {
// Look for const generics and error if we find any.
for param in &generics.params {
match param.kind {
GenericParamKind::Const { .. } => {
self.err_handler()
.struct_span_err(
item.span,
"const parameters are not permitted in `const fn`",
)
.emit();
}
_ => {}
}
}
}
}
ItemKind::ForeignMod(..) => {
self.invalid_visibility(
&item.vis,
Expand Down Expand Up @@ -508,6 +597,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
match *generic_args {
GenericArgs::AngleBracketed(ref data) => {
walk_list!(self, visit_generic_arg, &data.args);
validate_generics_order(self.err_handler(), data.args.iter().map(|arg| {
(match arg {
GenericArg::Lifetime(..) => ParamKindOrd::Lifetime,
GenericArg::Type(..) => ParamKindOrd::Type,
GenericArg::Const(..) => ParamKindOrd::Const,
}, arg.span(), None)
}), GenericPosition::Arg, generic_args.span());
// Type bindings such as `Item=impl Debug` in `Iterator<Item=Debug>`
// are allowed to contain nested `impl Trait`.
self.with_impl_trait(None, |this| {
Expand All @@ -526,34 +622,40 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}

fn visit_generics(&mut self, generics: &'a Generics) {
let mut seen_non_lifetime_param = false;
let mut seen_default = None;
let mut prev_ty_default = None;
for param in &generics.params {
match (&param.kind, seen_non_lifetime_param) {
(GenericParamKind::Lifetime { .. }, true) => {
if let GenericParamKind::Type { ref default, .. } = param.kind {
if default.is_some() {
prev_ty_default = Some(param.ident.span);
} else if let Some(span) = prev_ty_default {
self.err_handler()
.span_err(param.ident.span, "lifetime parameters must be leading");
},
(GenericParamKind::Lifetime { .. }, false) => {}
(GenericParamKind::Type { ref default, .. }, _) => {
seen_non_lifetime_param = true;
if default.is_some() {
seen_default = Some(param.ident.span);
} else if let Some(span) = seen_default {
self.err_handler()
.span_err(span, "type parameters with a default must be trailing");
break;
}
.span_err(span, "type parameters with a default must be trailing");
break;
}
}
}

validate_generics_order(self.err_handler(), generics.params.iter().map(|param| {
let span = param.ident.span;
let ident = Some(param.ident.to_string());
match &param.kind {
GenericParamKind::Lifetime { .. } => (ParamKindOrd::Lifetime, span, ident),
GenericParamKind::Type { .. } => (ParamKindOrd::Type, span, ident),
GenericParamKind::Const { ref ty } => {
let ty = pprust::ty_to_string(ty);
(ParamKindOrd::Const, span, Some(format!("const {}: {}", param.ident, ty)))
}
}
}), GenericPosition::Param, generics.span);

for predicate in &generics.where_clause.predicates {
if let WherePredicate::EqPredicate(ref predicate) = *predicate {
self.err_handler()
.span_err(predicate.span, "equality constraints are not yet \
supported in where clauses (see #20041)");
}
}

visit::walk_generics(self, generics)
}

Expand Down
4 changes: 2 additions & 2 deletions src/librustc_resolve/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,8 +414,8 @@ https://doc.rust-lang.org/reference.html#use-declarations
"##,

E0401: r##"
Inner items do not inherit type parameters from the functions they are embedded
in.
Inner items do not inherit type or const parameters from the functions
they are embedded in.
Erroneous code example:
Expand Down
Loading

0 comments on commit 43e04fb

Please sign in to comment.