From ea0d99829da705377b7b690062401f772f0babe8 Mon Sep 17 00:00:00 2001 From: varkor Date: Tue, 5 Feb 2019 16:50:55 +0100 Subject: [PATCH] Add resolution errors for const generics Co-Authored-By: Gabriel Smith --- src/librustc_resolve/lib.rs | 158 ++++++++++++++++++++++++++++-------- src/libsyntax/ast.rs | 20 +++++ 2 files changed, 146 insertions(+), 32 deletions(-) diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index b166b1be02f45..934bb0f03c4f5 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -51,6 +51,7 @@ use syntax::ast::{FnDecl, ForeignItem, ForeignItemKind, GenericParamKind, Generi use syntax::ast::{Item, ItemKind, ImplItem, ImplItemKind}; use syntax::ast::{Label, Local, Mutability, Pat, PatKind, Path}; use syntax::ast::{QSelf, TraitItemKind, TraitRef, Ty, TyKind}; +use syntax::ast::ParamKindOrd; use syntax::ptr::P; use syntax::{span_err, struct_span_err, unwrap_or, walk_list}; @@ -142,10 +143,11 @@ impl Ord for BindingError { } enum ResolutionError<'a> { - /// error E0401: can't use type parameters from outer function - TypeParametersFromOuterFunction(Def), - /// error E0403: the name is already used for a type parameter in this type parameter list - NameAlreadyUsedInTypeParameterList(Name, &'a Span), + /// error E0401: can't use type or const parameters from outer function + ParametersFromOuterFunction(Def, ParamKindOrd), + /// error E0403: the name is already used for a type/const parameter in this list of + /// generic parameters + NameAlreadyUsedInParameterList(Name, &'a Span), /// error E0407: method is not a member of trait MethodNotMemberOfTrait(Name, &'a str), /// error E0437: type is not a member of trait @@ -177,7 +179,9 @@ enum ResolutionError<'a> { /// error E0530: X bindings cannot shadow Ys BindingShadowsSomethingUnacceptable(&'a str, Name, &'a NameBinding<'a>), /// error E0128: type parameters with a default cannot use forward declared identifiers - ForwardDeclaredTyParam, + ForwardDeclaredTyParam, // FIXME(const_generics:defaults) + /// error E0670: const parameter cannot depend on type parameter + ConstParamDependentOnTypeParam, } /// Combines an error with provided span and emits it @@ -195,12 +199,14 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver<'_>, resolution_error: ResolutionError<'a>) -> DiagnosticBuilder<'sess> { match resolution_error { - ResolutionError::TypeParametersFromOuterFunction(outer_def) => { + ResolutionError::ParametersFromOuterFunction(outer_def, kind) => { let mut err = struct_span_err!(resolver.session, - span, - E0401, - "can't use type parameters from outer function"); - err.span_label(span, "use of type variable from outer function"); + span, + E0401, + "can't use {} parameters from outer function", + kind, + ); + err.span_label(span, format!("use of {} variable from outer function", kind)); let cm = resolver.session.source_map(); match outer_def { @@ -224,20 +230,25 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver<'_>, } return err; }, - Def::TyParam(typaram_defid) => { - if let Some(typaram_span) = resolver.definitions.opt_span(typaram_defid) { - err.span_label(typaram_span, "type variable from outer function"); + Def::TyParam(def_id) => { + if let Some(span) = resolver.definitions.opt_span(def_id) { + err.span_label(span, "type variable from outer function"); } - }, + } + Def::ConstParam(def_id) => { + if let Some(span) = resolver.definitions.opt_span(def_id) { + err.span_label(span, "const variable from outer function"); + } + } _ => { - bug!("TypeParametersFromOuterFunction should only be used with Def::SelfTy or \ - Def::TyParam") + bug!("TypeParametersFromOuterFunction should only be used with Def::SelfTy, \ + Def::TyParam or Def::ConstParam"); } } // Try to retrieve the span of the function signature and generate a new message with - // a local type parameter - let sugg_msg = "try using a local type parameter instead"; + // a local type or const parameter. + let sugg_msg = &format!("try using a local {} parameter instead", kind); if let Some((sugg_span, new_snippet)) = cm.generate_local_type_param_snippet(span) { // Suggest the modification to the user err.span_suggestion( @@ -247,19 +258,20 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver<'_>, Applicability::MachineApplicable, ); } else if let Some(sp) = cm.generate_fn_name_span(span) { - err.span_label(sp, "try adding a local type parameter in this method instead"); + err.span_label(sp, + format!("try adding a local {} parameter in this method instead", kind)); } else { - err.help("try using a local type parameter instead"); + err.help(&format!("try using a local {} parameter instead", kind)); } err } - ResolutionError::NameAlreadyUsedInTypeParameterList(name, first_use_span) => { + ResolutionError::NameAlreadyUsedInParameterList(name, first_use_span) => { let mut err = struct_span_err!(resolver.session, span, E0403, - "the name `{}` is already used for a type parameter \ - in this type parameter list", + "the name `{}` is already used for a generic \ + parameter in this list of generic parameters", name); err.span_label(span, "already used"); err.span_label(first_use_span.clone(), format!("first use of `{}`", name)); @@ -416,6 +428,12 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver<'_>, span, "defaulted type parameters cannot be forward declared".to_string()); err } + ResolutionError::ConstParamDependentOnTypeParam => { + let mut err = struct_span_err!(resolver.session, span, E0670, + "const parameters cannot depend on type parameters"); + err.span_label(span, format!("const parameter depends on type parameter")); + err + } } } @@ -546,7 +564,7 @@ impl<'a> PathSource<'a> { Def::Struct(..) | Def::Union(..) | Def::Enum(..) | Def::Trait(..) | Def::TraitAlias(..) | Def::TyAlias(..) | Def::AssociatedTy(..) | Def::PrimTy(..) | Def::TyParam(..) | - Def::SelfTy(..) | Def::Existential(..) | + Def::SelfTy(..) | Def::Existential(..) | Def::ConstParam(..) | Def::ForeignTy(..) => true, _ => false, }, @@ -564,7 +582,7 @@ impl<'a> PathSource<'a> { Def::VariantCtor(_, CtorKind::Const) | Def::VariantCtor(_, CtorKind::Fn) | Def::Const(..) | Def::Static(..) | Def::Local(..) | Def::Upvar(..) | Def::Fn(..) | Def::Method(..) | Def::AssociatedConst(..) | - Def::SelfCtor(..) => true, + Def::SelfCtor(..) | Def::ConstParam(..) => true, _ => false, }, PathSource::Pat => match def { @@ -855,6 +873,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> { self.label_ribs.pop(); self.ribs[ValueNS].pop(); } + fn visit_generics(&mut self, generics: &'tcx Generics) { // For type parameter defaults, we have to ban access // to following type parameters, as the Substs can only @@ -865,6 +884,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> { let mut found_default = false; default_ban_rib.bindings.extend(generics.params.iter() .filter_map(|param| match param.kind { + GenericParamKind::Const { .. } | GenericParamKind::Lifetime { .. } => None, GenericParamKind::Type { ref default, .. } => { found_default |= default.is_some(); @@ -876,6 +896,16 @@ impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> { } })); + // We also ban access to type parameters for use as the types of const parameters. + let mut const_ty_param_ban_rib = Rib::new(TyParamAsConstParamTy); + const_ty_param_ban_rib.bindings.extend(generics.params.iter() + .filter(|param| if let GenericParamKind::Type { .. } = param.kind { + true + } else { + false + }) + .map(|param| (Ident::with_empty_ctxt(param.ident.name), Def::Err))); + for param in &generics.params { match param.kind { GenericParamKind::Lifetime { .. } => self.visit_generic_param(param), @@ -893,6 +923,17 @@ impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> { // Allow all following defaults to refer to this type parameter. default_ban_rib.bindings.remove(&Ident::with_empty_ctxt(param.ident.name)); } + GenericParamKind::Const { ref ty } => { + self.ribs[TypeNS].push(const_ty_param_ban_rib); + + for bound in ¶m.bounds { + self.visit_param_bound(bound); + } + + self.visit_ty(ty); + + const_ty_param_ban_rib = self.ribs[TypeNS].pop().unwrap(); + } } } for p in &generics.where_clause.predicates { @@ -944,6 +985,9 @@ enum RibKind<'a> { /// from the default of a type parameter because they're not declared /// before said type parameter. Also see the `visit_generics` override. ForwardTyParamBanRibKind, + + /// We forbid the use of type parameters as the types of const parameters. + TyParamAsConstParamTy, } /// One local scope. @@ -2535,7 +2579,7 @@ impl<'a> Resolver<'a> { if seen_bindings.contains_key(&ident) { let span = seen_bindings.get(&ident).unwrap(); - let err = ResolutionError::NameAlreadyUsedInTypeParameterList( + let err = ResolutionError::NameAlreadyUsedInParameterList( ident.name, span, ); @@ -2548,6 +2592,24 @@ impl<'a> Resolver<'a> { function_type_rib.bindings.insert(ident, def); self.record_def(param.id, PathResolution::new(def)); } + GenericParamKind::Const { .. } => { + let ident = param.ident.modern(); + debug!("with_type_parameter_rib: {}", param.id); + + if seen_bindings.contains_key(&ident) { + let span = seen_bindings.get(&ident).unwrap(); + let err = ResolutionError::NameAlreadyUsedInParameterList( + ident.name, + span, + ); + resolve_error(self, param.ident.span, err); + } + seen_bindings.entry(ident).or_insert(param.ident.span); + + let def = Def::ConstParam(self.definitions.local_def_id(param.id)); + function_type_rib.bindings.insert(ident, def); + self.record_def(param.id, PathResolution::new(def)); + } } } self.ribs[TypeNS].push(function_type_rib); @@ -4106,6 +4168,15 @@ impl<'a> Resolver<'a> { return Def::Err; } + // An invalid use of a type parameter as the type of a const parameter. + if let TyParamAsConstParamTy = self.ribs[ns][rib_index].kind { + if record_used { + resolve_error(self, span, ResolutionError::ConstParamDependentOnTypeParam); + } + assert_eq!(def, Def::Err); + return Def::Err; + } + match def { Def::Upvar(..) => { span_bug!(span, "unexpected {:?} in bindings", def) @@ -4114,7 +4185,7 @@ impl<'a> Resolver<'a> { for rib in ribs { match rib.kind { NormalRibKind | ModuleRibKind(..) | MacroDefinition(..) | - ForwardTyParamBanRibKind => { + ForwardTyParamBanRibKind | TyParamAsConstParamTy => { // Nothing to do. Continue. } ClosureRibKind(function_id) => { @@ -4167,21 +4238,44 @@ impl<'a> Resolver<'a> { match rib.kind { NormalRibKind | TraitOrImplItemRibKind | ClosureRibKind(..) | ModuleRibKind(..) | MacroDefinition(..) | ForwardTyParamBanRibKind | - ConstantItemRibKind => { + ConstantItemRibKind | TyParamAsConstParamTy => { // Nothing to do. Continue. } ItemRibKind => { - // This was an attempt to use a type parameter outside - // its scope. + // This was an attempt to use a type parameter outside its scope. if record_used { - resolve_error(self, span, - ResolutionError::TypeParametersFromOuterFunction(def)); + resolve_error( + self, + span, + ResolutionError::ParametersFromOuterFunction( + def, + ParamKindOrd::Type, + ), + ); } return Def::Err; } } } } + Def::ConstParam(..) => { + for rib in ribs { + if let ItemRibKind = rib.kind { + // This was an attempt to use a const parameter outside its scope. + if record_used { + resolve_error( + self, + span, + ResolutionError::ParametersFromOuterFunction( + def, + ParamKindOrd::Const, + ), + ); + } + return Def::Err; + } + } + } _ => {} } def diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 57a3bf86b0e51..ab3883d2b9b9b 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -307,6 +307,26 @@ impl GenericBound { pub type GenericBounds = Vec; +/// Specifies the enforced ordering for generic parameters. In the future, +/// if we wanted to relax this order, we could override `PartialEq` and +/// `PartialOrd`, to allow the kinds to be unordered. +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] +pub enum ParamKindOrd { + Lifetime, + Type, + Const, +} + +impl fmt::Display for ParamKindOrd { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + ParamKindOrd::Lifetime => "lifetime".fmt(f), + ParamKindOrd::Type => "type".fmt(f), + ParamKindOrd::Const => "const".fmt(f), + } + } +} + #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] pub enum GenericParamKind { /// A lifetime definition (e.g., `'a: 'b + 'c + 'd`).