diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index d17ef3a6c9a..dae9f945ce0 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -931,7 +931,17 @@ pub enum GenericParamDefKind { Const, } -#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)] +impl GenericParamDefKind { + pub fn descr(&self) -> &'static str { + match self { + GenericParamDefKind::Lifetime => "lifetime", + GenericParamDefKind::Type { .. } => "type", + GenericParamDefKind::Const => "constant", + } + } +} + +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] pub struct GenericParamDef { pub name: Symbol, pub def_id: DefId, diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 59dd41e9d56..03ff1b8a317 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -17,17 +17,6 @@ use std::fmt; use std::rc::Rc; use std::sync::Arc; -impl fmt::Debug for ty::GenericParamDef { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let type_name = match self.kind { - ty::GenericParamDefKind::Lifetime => "Lifetime", - ty::GenericParamDefKind::Type { .. } => "Type", - ty::GenericParamDefKind::Const => "Const", - }; - write!(f, "{}({}, {:?}, {})", type_name, self.name, self.def_id, self.index) - } -} - impl fmt::Debug for ty::TraitDef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ty::tls::with(|tcx| { diff --git a/src/librustc_ast_passes/ast_validation.rs b/src/librustc_ast_passes/ast_validation.rs index 44fed6dee66..0463ad7fb7b 100644 --- a/src/librustc_ast_passes/ast_validation.rs +++ b/src/librustc_ast_passes/ast_validation.rs @@ -8,7 +8,7 @@ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{error_code, struct_span_err, Applicability, FatalError}; +use rustc_errors::{error_code, struct_span_err, Applicability}; use rustc_parse::validate_attr; use rustc_session::lint::builtin::PATTERNS_IN_FNS_WITHOUT_BODY; use rustc_session::lint::LintBuffer; @@ -596,23 +596,15 @@ impl<'a> AstValidator<'a> { } } -enum GenericPosition { - Param, - Arg, -} - -fn validate_generics_order<'a>( +fn validate_generic_param_order<'a>( sess: &Session, handler: &rustc_errors::Handler, generics: impl Iterator, Span, Option)>, - pos: GenericPosition, span: Span, ) { let mut max_param: Option = None; let mut out_of_order = FxHashMap::default(); let mut param_idents = vec![]; - let mut found_type = false; - let mut found_const = false; for (kind, bounds, span, ident) in generics { if let Some(ident) = ident { @@ -626,11 +618,6 @@ fn validate_generics_order<'a>( } Some(_) | None => *max_param = Some(kind), }; - match kind { - ParamKindOrd::Type => found_type = true, - ParamKindOrd::Const => found_const = true, - _ => {} - } } let mut ordered_params = "<".to_string(); @@ -653,42 +640,26 @@ fn validate_generics_order<'a>( } 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.clone(), - &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, + let mut err = + handler.struct_span_err( + spans.clone(), &format!( - "reorder the {}s: lifetimes, then types{}", - pos_str, - if sess.features_untracked().const_generics { ", then consts" } else { "" }, + "{} parameters must be declared prior to {} parameters", + param_ord, max_param, ), - ordered_params.clone(), - Applicability::MachineApplicable, ); - } + err.span_suggestion( + span, + &format!( + "reorder the parameters: lifetimes, then types{}", + if sess.features_untracked().const_generics { ", then consts" } else { "" }, + ), + ordered_params.clone(), + Applicability::MachineApplicable, + ); err.emit(); } - - // FIXME(const_generics): we shouldn't have to abort here at all, but we currently get ICEs - // if we don't. Const parameters and type parameters can currently conflict if they - // are out-of-order. - if !out_of_order.is_empty() && found_type && found_const { - FatalError.raise(); - } } impl<'a> Visitor<'a> for AstValidator<'a> { @@ -1016,24 +987,6 @@ 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.session, - self.err_handler(), - data.args.iter().map(|arg| { - ( - match arg { - GenericArg::Lifetime(..) => ParamKindOrd::Lifetime, - GenericArg::Type(..) => ParamKindOrd::Type, - GenericArg::Const(..) => ParamKindOrd::Const, - }, - None, - arg.span(), - None, - ) - }), - GenericPosition::Arg, - generic_args.span(), - ); // Type bindings such as `Item = impl Debug` in `Iterator` // are allowed to contain nested `impl Trait`. @@ -1070,7 +1023,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } } - validate_generics_order( + validate_generic_param_order( self.session, self.err_handler(), generics.params.iter().map(|param| { @@ -1085,7 +1038,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> { }; (kind, Some(&*param.bounds), param.ident.span, ident) }), - GenericPosition::Param, generics.span, ); diff --git a/src/librustc_error_codes/error_codes.rs b/src/librustc_error_codes/error_codes.rs index ba43b29538d..91a7b6c8958 100644 --- a/src/librustc_error_codes/error_codes.rs +++ b/src/librustc_error_codes/error_codes.rs @@ -417,6 +417,7 @@ E0743: include_str!("./error_codes/E0743.md"), E0744: include_str!("./error_codes/E0744.md"), E0745: include_str!("./error_codes/E0745.md"), E0746: include_str!("./error_codes/E0746.md"), +E0747: include_str!("./error_codes/E0747.md"), ; // E0006, // merged with E0005 // E0008, // cannot bind by-move into a pattern guard diff --git a/src/librustc_error_codes/error_codes/E0747.md b/src/librustc_error_codes/error_codes/E0747.md new file mode 100644 index 00000000000..df1afbfef46 --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0747.md @@ -0,0 +1,20 @@ +Generic arguments must be provided in the same order as the corresponding +generic parameters are declared. + +Erroneous code example: + +```compile_fail,E0747 +struct S<'a, T>(&'a T); + +type X = S<(), 'static>; // error: the type argument is provided before the + // lifetime argument +``` + +The argument order should be changed to match the parameter declaration +order, as in the following. + +``` +struct S<'a, T>(&'a T); + +type X = S<'static, ()>; // ok +``` diff --git a/src/librustc_hir/hir.rs b/src/librustc_hir/hir.rs index 6d2f5ba6baf..2e99998c627 100644 --- a/src/librustc_hir/hir.rs +++ b/src/librustc_hir/hir.rs @@ -298,6 +298,14 @@ impl GenericArg<'_> { _ => false, } } + + pub fn descr(&self) -> &'static str { + match self { + GenericArg::Lifetime(_) => "lifetime", + GenericArg::Type(_) => "type", + GenericArg::Const(_) => "constant", + } + } } #[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 78c05a51e4f..49f38d86d91 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -12,7 +12,7 @@ use crate::middle::resolve_lifetime as rl; use crate::require_c_abi_if_c_variadic; use crate::util::common::ErrorReported; use rustc::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; -use rustc::session::parse::feature_err; +use rustc::session::{parse::feature_err, Session}; use rustc::ty::subst::{self, InternalSubsts, Subst, SubstsRef}; use rustc::ty::{self, Const, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; use rustc::ty::{GenericParamDef, GenericParamDefKind}; @@ -132,6 +132,15 @@ enum GenericArgPosition { MethodCall, } +/// A marker denoting that the generic arguments that were +/// provided did not match the respective generic parameters. +pub struct GenericArgCountMismatch { + /// Indicates whether a fatal error was reported (`Some`), or just a lint (`None`). + pub reported: Option, + /// A list of spans of arguments provided that were not valid. + pub invalid_args: Vec, +} + impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { pub fn ast_region_to_region( &self, @@ -262,7 +271,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { def: &ty::Generics, seg: &hir::PathSegment<'_>, is_method_call: bool, - ) -> bool { + ) -> Result<(), GenericArgCountMismatch> { let empty_args = hir::GenericArgs::none(); let suppress_mismatch = Self::check_impl_trait(tcx, seg, &def); Self::check_generic_arg_count( @@ -274,7 +283,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { def.parent.is_none() && def.has_self, // `has_self` seg.infer_args || suppress_mismatch, // `infer_args` ) - .0 } /// Checks that the correct number of generic arguments have been provided. @@ -287,7 +295,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { position: GenericArgPosition, has_self: bool, infer_args: bool, - ) -> (bool, Option>) { + ) -> Result<(), GenericArgCountMismatch> { // At this stage we are guaranteed that the generic arguments are in the correct order, e.g. // that lifetimes will proceed types. So it suffices to check the number of each generic // arguments in order to validate them with respect to the generic parameters. @@ -313,7 +321,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } // Prohibit explicit lifetime arguments if late-bound lifetime parameters are present. - let mut reported_late_bound_region_err = None; + let mut explicit_lifetimes = Ok(()); if !infer_lifetimes { if let Some(span_late) = def.has_late_bound_regions { let msg = "cannot specify lifetime arguments explicitly \ @@ -323,11 +331,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if position == GenericArgPosition::Value && arg_counts.lifetimes != param_counts.lifetimes { + explicit_lifetimes = Err(true); let mut err = tcx.sess.struct_span_err(span, msg); err.span_note(span_late, note); err.emit(); - reported_late_bound_region_err = Some(true); } else { + explicit_lifetimes = Err(false); let mut multispan = MultiSpan::from_span(span); multispan.push_span_label(span_late, note.to_string()); tcx.struct_span_lint_hir( @@ -336,112 +345,136 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { multispan, |lint| lint.build(msg).emit(), ); - reported_late_bound_region_err = Some(false); } } } - let check_kind_count = |kind, required, permitted, provided, offset| { - debug!( - "check_kind_count: kind: {} required: {} permitted: {} provided: {} offset: {}", - kind, required, permitted, provided, offset - ); - // We enforce the following: `required` <= `provided` <= `permitted`. - // For kinds without defaults (e.g.., lifetimes), `required == permitted`. - // For other kinds (i.e., types), `permitted` may be greater than `required`. - if required <= provided && provided <= permitted { - return (reported_late_bound_region_err.unwrap_or(false), None); - } - - // Unfortunately lifetime and type parameter mismatches are typically styled - // differently in diagnostics, which means we have a few cases to consider here. - let (bound, quantifier) = if required != permitted { - if provided < required { - (required, "at least ") - } else { - // provided > permitted - (permitted, "at most ") + let check_kind_count = + |kind, required, permitted, provided, offset, unexpected_spans: &mut Vec| { + debug!( + "check_kind_count: kind: {} required: {} permitted: {} provided: {} offset: {}", + kind, required, permitted, provided, offset + ); + // We enforce the following: `required` <= `provided` <= `permitted`. + // For kinds without defaults (e.g.., lifetimes), `required == permitted`. + // For other kinds (i.e., types), `permitted` may be greater than `required`. + if required <= provided && provided <= permitted { + return Ok(()); } - } else { - (required, "") - }; - let mut potential_assoc_types: Option> = None; - let (spans, label) = if required == permitted && provided > permitted { - // In the case when the user has provided too many arguments, - // we want to point to the unexpected arguments. - let spans: Vec = args.args[offset + permitted..offset + provided] - .iter() - .map(|arg| arg.span()) - .collect(); - potential_assoc_types = Some(spans.clone()); - (spans, format!("unexpected {} argument", kind)) - } else { - ( - vec![span], - format!( - "expected {}{} {} argument{}", - quantifier, - bound, - kind, - pluralize!(bound), + // Unfortunately lifetime and type parameter mismatches are typically styled + // differently in diagnostics, which means we have a few cases to consider here. + let (bound, quantifier) = if required != permitted { + if provided < required { + (required, "at least ") + } else { + // provided > permitted + (permitted, "at most ") + } + } else { + (required, "") + }; + + let (spans, label) = if required == permitted && provided > permitted { + // In the case when the user has provided too many arguments, + // we want to point to the unexpected arguments. + let spans: Vec = args.args[offset + permitted..offset + provided] + .iter() + .map(|arg| arg.span()) + .collect(); + unexpected_spans.extend(spans.clone()); + (spans, format!("unexpected {} argument", kind)) + } else { + ( + vec![span], + format!( + "expected {}{} {} argument{}", + quantifier, + bound, + kind, + pluralize!(bound), + ), + ) + }; + + let mut err = tcx.sess.struct_span_err_with_code( + spans.clone(), + &format!( + "wrong number of {} arguments: expected {}{}, found {}", + kind, quantifier, bound, provided, ), - ) + DiagnosticId::Error("E0107".into()), + ); + for span in spans { + err.span_label(span, label.as_str()); + } + err.emit(); + + Err(true) }; - let mut err = tcx.sess.struct_span_err_with_code( - spans.clone(), - &format!( - "wrong number of {} arguments: expected {}{}, found {}", - kind, quantifier, bound, provided, - ), - DiagnosticId::Error("E0107".into()), - ); - for span in spans { - err.span_label(span, label.as_str()); - } - err.emit(); + let mut arg_count_correct = explicit_lifetimes; + let mut unexpected_spans = vec![]; - ( - provided > required, // `suppress_error` - potential_assoc_types, - ) - }; - - if reported_late_bound_region_err.is_none() + if arg_count_correct.is_ok() && (!infer_lifetimes || arg_counts.lifetimes > param_counts.lifetimes) { - check_kind_count( + arg_count_correct = check_kind_count( "lifetime", param_counts.lifetimes, param_counts.lifetimes, arg_counts.lifetimes, 0, - ); + &mut unexpected_spans, + ) + .and(arg_count_correct); } // FIXME(const_generics:defaults) if !infer_args || arg_counts.consts > param_counts.consts { - check_kind_count( + arg_count_correct = check_kind_count( "const", param_counts.consts, param_counts.consts, arg_counts.consts, arg_counts.lifetimes + arg_counts.types, - ); + &mut unexpected_spans, + ) + .and(arg_count_correct); } // Note that type errors are currently be emitted *after* const errors. if !infer_args || arg_counts.types > param_counts.types - defaults.types - has_self as usize { - check_kind_count( + arg_count_correct = check_kind_count( "type", param_counts.types - defaults.types - has_self as usize, param_counts.types - has_self as usize, arg_counts.types, arg_counts.lifetimes, + &mut unexpected_spans, ) - } else { - (reported_late_bound_region_err.unwrap_or(false), None) + .and(arg_count_correct); } + + arg_count_correct.map_err(|reported_err| GenericArgCountMismatch { + reported: if reported_err { Some(ErrorReported) } else { None }, + invalid_args: unexpected_spans, + }) + } + + /// Report an error that a generic argument did not match the generic parameter that was + /// expected. + fn generic_arg_mismatch_err(sess: &Session, arg: &GenericArg<'_>, kind: &'static str) { + let mut err = struct_span_err!( + sess, + arg.span(), + E0747, + "{} provided when a {} was expected", + arg.descr(), + kind, + ); + // This note will be true as long as generic parameters are strictly ordered by their kind. + err.note(&format!("{} arguments must be provided before {} arguments", kind, arg.descr())); + err.emit(); } /// Creates the relevant generic argument substitutions @@ -479,6 +512,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { parent_substs: &[subst::GenericArg<'tcx>], has_self: bool, self_ty: Option>, + arg_count_correct: bool, args_for_def_id: impl Fn(DefId) -> (Option<&'b GenericArgs<'b>>, bool), provided_kind: impl Fn(&GenericParamDef, &GenericArg<'_>) -> subst::GenericArg<'tcx>, mut inferred_kind: impl FnMut( @@ -502,7 +536,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // methods in `subst.rs`, so that we can iterate over the arguments and // parameters in lock-step linearly, instead of trying to match each pair. let mut substs: SmallVec<[subst::GenericArg<'tcx>; 8]> = SmallVec::with_capacity(count); - // Iterate over each segment of the path. while let Some((def_id, defs)) = stack.pop() { let mut params = defs.params.iter().peekable(); @@ -539,6 +572,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let mut args = generic_args.iter().flat_map(|generic_args| generic_args.args.iter()).peekable(); + // If we encounter a type or const when we expect a lifetime, we infer the lifetimes. + // If we later encounter a lifetime, we know that the arguments were provided in the + // wrong order. `force_infer_lt` records the type or const that forced lifetimes to be + // inferred, so we can use it for diagnostics later. + let mut force_infer_lt = None; + loop { // We're going to iterate through the generic arguments that the user // provided, matching them with the generic parameters we expect. @@ -559,30 +598,58 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // We expected a lifetime argument, but got a type or const // argument. That means we're inferring the lifetimes. substs.push(inferred_kind(None, param, infer_args)); + force_infer_lt = Some(arg); params.next(); } - (_, _) => { + (_, kind) => { // We expected one kind of parameter, but the user provided - // another. This is an error, but we need to handle it - // gracefully so we can report sensible errors. - // In this case, we're simply going to infer this argument. - args.next(); + // another. This is an error. However, if we already know that + // the arguments don't match up with the parameters, we won't issue + // an additional error, as the user already knows what's wrong. + if arg_count_correct { + Self::generic_arg_mismatch_err(tcx.sess, arg, kind.descr()); + } + + // We've reported the error, but we want to make sure that this + // problem doesn't bubble down and create additional, irrelevant + // errors. In this case, we're simply going to ignore the argument + // and any following arguments. The rest of the parameters will be + // inferred. + while args.next().is_some() {} } } } - (Some(_), None) => { + + (Some(&arg), None) => { // We should never be able to reach this point with well-formed input. - // Getting to this point means the user supplied more arguments than - // there are parameters. - args.next(); + // There are two situations in which we can encounter this issue. + // + // 1. The number of arguments is incorrect. In this case, an error + // will already have been emitted, and we can ignore it. This case + // also occurs when late-bound lifetime parameters are present, yet + // the lifetime arguments have also been explicitly specified by the + // user. + // 2. We've inferred some lifetimes, which have been provided later (i.e. + // after a type or const). We want to throw an error in this case. + + if arg_count_correct { + let kind = arg.descr(); + assert_eq!(kind, "lifetime"); + let provided = + force_infer_lt.expect("lifetimes ought to have been inferred"); + Self::generic_arg_mismatch_err(tcx.sess, provided, kind); + } + + break; } + (None, Some(¶m)) => { // If there are fewer arguments than parameters, it means // we're inferring the remaining arguments. substs.push(inferred_kind(Some(&substs), param, infer_args)); - args.next(); params.next(); } + (None, None) => break, } } @@ -630,7 +697,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { generic_args: &'a hir::GenericArgs<'_>, infer_args: bool, self_ty: Option>, - ) -> (SubstsRef<'tcx>, Vec>, Option>) { + ) -> (SubstsRef<'tcx>, Vec>, Result<(), GenericArgCountMismatch>) + { // If the type is parameterized by this region, then replace this // region with the current anon region binding (in other words, // whatever & would get replaced with). @@ -656,7 +724,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { assert!(self_ty.is_none() && parent_substs.is_empty()); } - let (_, potential_assoc_types) = Self::check_generic_arg_count( + let arg_count_correct = Self::check_generic_arg_count( tcx, span, &generic_params, @@ -689,8 +757,16 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { parent_substs, self_ty.is_some(), self_ty, + arg_count_correct.is_ok(), // Provide the generic args, and whether types should be inferred. - |_| (Some(generic_args), infer_args), + |did| { + if did == def_id { + (Some(generic_args), infer_args) + } else { + // The last component of this tuple is unimportant. + (None, false) + } + }, // Provide substitutions for parameters for which (valid) arguments have been provided. |param, arg| match (¶m.kind, arg) { (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { @@ -794,7 +870,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { generic_params, self_ty, substs ); - (substs, assoc_bindings, potential_assoc_types) + (substs, assoc_bindings, arg_count_correct) } crate fn create_substs_for_associated_item( @@ -925,7 +1001,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self_ty: Ty<'tcx>, bounds: &mut Bounds<'tcx>, speculative: bool, - ) -> Option> { + ) -> Result<(), GenericArgCountMismatch> { let trait_def_id = trait_ref.trait_def_id(); debug!("instantiate_poly_trait_ref({:?}, def_id={:?})", trait_ref, trait_def_id); @@ -942,7 +1018,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } else { trait_ref.path.span }; - let (substs, assoc_bindings, potential_assoc_types) = self.create_substs_for_ast_trait_ref( + let (substs, assoc_bindings, arg_count_correct) = self.create_substs_for_ast_trait_ref( path_span, trait_def_id, self_ty, @@ -971,7 +1047,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { "instantiate_poly_trait_ref({:?}, bounds={:?}) -> {:?}", trait_ref, bounds, poly_trait_ref ); - potential_assoc_types + + arg_count_correct } /// Given a trait bound like `Debug`, applies that trait bound the given self-type to construct @@ -999,7 +1076,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { constness: Constness, self_ty: Ty<'tcx>, bounds: &mut Bounds<'tcx>, - ) -> Option> { + ) -> Result<(), GenericArgCountMismatch> { self.instantiate_poly_trait_ref_inner( &poly_trait_ref.trait_ref, poly_trait_ref.span, @@ -1088,7 +1165,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { trait_def_id: DefId, self_ty: Ty<'tcx>, trait_segment: &'a hir::PathSegment<'a>, - ) -> (SubstsRef<'tcx>, Vec>, Option>) { + ) -> (SubstsRef<'tcx>, Vec>, Result<(), GenericArgCountMismatch>) + { debug!("create_substs_for_ast_trait_ref(trait_segment={:?})", trait_segment); self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment); @@ -1433,13 +1511,16 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let mut potential_assoc_types = Vec::new(); let dummy_self = self.tcx().types.trait_object_dummy_self; for trait_bound in trait_bounds.iter().rev() { - let cur_potential_assoc_types = self.instantiate_poly_trait_ref( + if let Err(GenericArgCountMismatch { + invalid_args: cur_potential_assoc_types, .. + }) = self.instantiate_poly_trait_ref( trait_bound, Constness::NotConst, dummy_self, &mut bounds, - ); - potential_assoc_types.extend(cur_potential_assoc_types.into_iter().flatten()); + ) { + potential_assoc_types.extend(cur_potential_assoc_types.into_iter()); + } } // Expand trait aliases recursively and check that only one regular (non-auto) trait diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index 17842be9a43..108affe5a86 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -299,7 +299,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // If they were not explicitly supplied, just construct fresh // variables. let generics = self.tcx.generics_of(pick.item.def_id); - AstConv::check_generic_arg_count_for_call( + let arg_count_correct = AstConv::check_generic_arg_count_for_call( self.tcx, self.span, &generics, &seg, true, // `is_method_call` ); @@ -313,10 +313,16 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { parent_substs, false, None, + arg_count_correct.is_ok(), // Provide the generic args, and whether types should be inferred. - |_| { - // The last argument of the returned tuple here is unimportant. - if let Some(ref data) = seg.args { (Some(data), false) } else { (None, false) } + |def_id| { + // The last component of the returned tuple here is unimportant. + if def_id == pick.item.def_id { + if let Some(ref data) = seg.args { + return (Some(data), false); + } + } + (None, false) }, // Provide substitutions for parameters for which (valid) arguments have been provided. |param, arg| match (¶m.kind, arg) { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 4ab5d8f9ad3..3c71e8bf6b8 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -87,7 +87,7 @@ mod upvar; mod wfcheck; pub mod writeback; -use crate::astconv::{AstConv, PathSeg}; +use crate::astconv::{AstConv, GenericArgCountMismatch, PathSeg}; use crate::middle::lang_items; use rustc::hir::map::blocks::FnLikeNode; use rustc::hir::map::Map; @@ -5431,10 +5431,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // parameter internally, but we don't allow users to specify the // parameter's value explicitly, so we have to do some error- // checking here. - let suppress_errors = AstConv::check_generic_arg_count_for_call( - tcx, span, &generics, &seg, false, // `is_method_call` - ); - if suppress_errors { + if let Err(GenericArgCountMismatch { reported: Some(ErrorReported), .. }) = + AstConv::check_generic_arg_count_for_call( + tcx, span, &generics, &seg, false, // `is_method_call` + ) + { infer_args_for_err.insert(index); self.set_tainted_by_errors(); // See issue #53251. } @@ -5499,6 +5500,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &[][..], has_self, self_ty, + infer_args_for_err.is_empty(), // Provide the generic args, and whether types should be inferred. |def_id| { if let Some(&PathSeg(_, index)) = diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 61aa8e51cb0..1fade1b5ca6 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1270,7 +1270,7 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::Generics { let object_lifetime_defaults = tcx.object_lifetime_defaults(hir_id); - // Now create the real type parameters. + // Now create the real type and const parameters. let type_start = own_start - has_self as u32 + params.len() as u32; let mut i = 0; params.extend(ast_generics.params.iter().filter_map(|param| { diff --git a/src/test/ui/const-generics/const-arg-type-arg-misordered.rs b/src/test/ui/const-generics/const-arg-type-arg-misordered.rs new file mode 100644 index 00000000000..f024eb6a957 --- /dev/null +++ b/src/test/ui/const-generics/const-arg-type-arg-misordered.rs @@ -0,0 +1,10 @@ +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash + +type Array = [T; N]; + +fn foo() -> Array { //~ ERROR constant provided when a type was expected + unimplemented!() +} + +fn main() {} diff --git a/src/test/ui/const-generics/const-arg-type-arg-misordered.stderr b/src/test/ui/const-generics/const-arg-type-arg-misordered.stderr new file mode 100644 index 00000000000..150a6011c2c --- /dev/null +++ b/src/test/ui/const-generics/const-arg-type-arg-misordered.stderr @@ -0,0 +1,19 @@ +warning: the feature `const_generics` is incomplete and may cause the compiler to crash + --> $DIR/const-arg-type-arg-misordered.rs:1:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + +error[E0747]: constant provided when a type was expected + --> $DIR/const-arg-type-arg-misordered.rs:6:35 + | +LL | fn foo() -> Array { + | ^ + | + = note: type arguments must be provided before constant arguments + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0747`. diff --git a/src/test/ui/const-generics/const-param-after-const-literal-arg.rs b/src/test/ui/const-generics/const-param-after-const-literal-arg.rs new file mode 100644 index 00000000000..19c4120eb2f --- /dev/null +++ b/src/test/ui/const-generics/const-param-after-const-literal-arg.rs @@ -0,0 +1,10 @@ +// check-pass + +#![allow(incomplete_features)] +#![feature(const_generics)] + +struct Foo; + +impl Foo<1, A> {} // ok + +fn main() {} diff --git a/src/test/ui/const-generics/const-param-before-other-params.rs b/src/test/ui/const-generics/const-param-before-other-params.rs index 5bdbfd8ff1f..756e961ce91 100644 --- a/src/test/ui/const-generics/const-param-before-other-params.rs +++ b/src/test/ui/const-generics/const-param-before-other-params.rs @@ -1,3 +1,4 @@ +#![allow(incomplete_features)] #![feature(const_generics)] fn bar(_: &'a ()) { diff --git a/src/test/ui/const-generics/const-param-before-other-params.stderr b/src/test/ui/const-generics/const-param-before-other-params.stderr index 87622f7e500..9b18b8c79ed 100644 --- a/src/test/ui/const-generics/const-param-before-other-params.stderr +++ b/src/test/ui/const-generics/const-param-before-other-params.stderr @@ -1,11 +1,11 @@ error: lifetime parameters must be declared prior to const parameters - --> $DIR/const-param-before-other-params.rs:3:21 + --> $DIR/const-param-before-other-params.rs:4:21 | LL | fn bar(_: &'a ()) { | --------------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, const X: ()>` error: type parameters must be declared prior to const parameters - --> $DIR/const-param-before-other-params.rs:7:21 + --> $DIR/const-param-before-other-params.rs:8:21 | LL | fn foo(_: &T) { | --------------^- help: reorder the parameters: lifetimes, then types, then consts: `` diff --git a/src/test/ui/generic/generic-arg-mismatch-recover.rs b/src/test/ui/generic/generic-arg-mismatch-recover.rs index f4e15fbebce..3e5e2e601f5 100644 --- a/src/test/ui/generic/generic-arg-mismatch-recover.rs +++ b/src/test/ui/generic/generic-arg-mismatch-recover.rs @@ -4,7 +4,6 @@ struct Bar<'a>(&'a ()); fn main() { Foo::<'static, 'static, ()>(&0); //~ ERROR wrong number of lifetime arguments - //~^ ERROR mismatched types Bar::<'static, 'static, ()>(&()); //~ ERROR wrong number of lifetime arguments //~^ ERROR wrong number of type arguments diff --git a/src/test/ui/generic/generic-arg-mismatch-recover.stderr b/src/test/ui/generic/generic-arg-mismatch-recover.stderr index 4b86212e486..99adb352685 100644 --- a/src/test/ui/generic/generic-arg-mismatch-recover.stderr +++ b/src/test/ui/generic/generic-arg-mismatch-recover.stderr @@ -4,28 +4,18 @@ error[E0107]: wrong number of lifetime arguments: expected 1, found 2 LL | Foo::<'static, 'static, ()>(&0); | ^^^^^^^ unexpected lifetime argument -error[E0308]: mismatched types - --> $DIR/generic-arg-mismatch-recover.rs:6:33 - | -LL | Foo::<'static, 'static, ()>(&0); - | ^^ expected `()`, found integer - | - = note: expected reference `&'static ()` - found reference `&{integer}` - error[E0107]: wrong number of lifetime arguments: expected 1, found 2 - --> $DIR/generic-arg-mismatch-recover.rs:9:20 + --> $DIR/generic-arg-mismatch-recover.rs:8:20 | LL | Bar::<'static, 'static, ()>(&()); | ^^^^^^^ unexpected lifetime argument error[E0107]: wrong number of type arguments: expected 0, found 1 - --> $DIR/generic-arg-mismatch-recover.rs:9:29 + --> $DIR/generic-arg-mismatch-recover.rs:8:29 | LL | Bar::<'static, 'static, ()>(&()); | ^^ unexpected type argument -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0107, E0308. -For more information about an error, try `rustc --explain E0107`. +For more information about this error, try `rustc --explain E0107`. diff --git a/src/test/ui/parser/issue-14303-fncall.rs b/src/test/ui/parser/issue-14303-fncall.rs index 39694198cdb..46ece84d69e 100644 --- a/src/test/ui/parser/issue-14303-fncall.rs +++ b/src/test/ui/parser/issue-14303-fncall.rs @@ -11,7 +11,7 @@ fn foo<'a, 'b>(start: &'a usize, end: &'a usize) { let _x = (*start..*end) .map(|x| S { a: start, b: end }) .collect::>>(); - //~^ ERROR lifetime arguments must be declared prior to type arguments + //~^ ERROR type provided when a lifetime was expected } fn main() {} diff --git a/src/test/ui/parser/issue-14303-fncall.stderr b/src/test/ui/parser/issue-14303-fncall.stderr index 8ef9f1a1a6c..10954223713 100644 --- a/src/test/ui/parser/issue-14303-fncall.stderr +++ b/src/test/ui/parser/issue-14303-fncall.stderr @@ -1,8 +1,11 @@ -error: lifetime arguments must be declared prior to type arguments - --> $DIR/issue-14303-fncall.rs:13:29 +error[E0747]: type provided when a lifetime was expected + --> $DIR/issue-14303-fncall.rs:13:26 | LL | .collect::>>(); - | ^^ + | ^ + | + = note: lifetime arguments must be provided before type arguments error: aborting due to previous error +For more information about this error, try `rustc --explain E0747`. diff --git a/src/test/ui/parser/issue-14303-path.rs b/src/test/ui/parser/issue-14303-path.rs index 386d19859e4..89ef914aba2 100644 --- a/src/test/ui/parser/issue-14303-path.rs +++ b/src/test/ui/parser/issue-14303-path.rs @@ -8,6 +8,6 @@ mod foo { } fn bar<'a, 'b, 'c, T>(x: foo::X<'a, T, 'b, 'c>) {} -//~^ ERROR lifetime arguments must be declared prior to type arguments +//~^ ERROR type provided when a lifetime was expected fn main() {} diff --git a/src/test/ui/parser/issue-14303-path.stderr b/src/test/ui/parser/issue-14303-path.stderr index 19f2995ebee..c1ad2332b5b 100644 --- a/src/test/ui/parser/issue-14303-path.stderr +++ b/src/test/ui/parser/issue-14303-path.stderr @@ -1,8 +1,11 @@ -error: lifetime arguments must be declared prior to type arguments - --> $DIR/issue-14303-path.rs:10:40 +error[E0747]: type provided when a lifetime was expected + --> $DIR/issue-14303-path.rs:10:37 | LL | fn bar<'a, 'b, 'c, T>(x: foo::X<'a, T, 'b, 'c>) {} - | ^^ ^^ + | ^ + | + = note: lifetime arguments must be provided before type arguments error: aborting due to previous error +For more information about this error, try `rustc --explain E0747`. diff --git a/src/test/ui/suggestions/suggest-move-types.rs b/src/test/ui/suggestions/suggest-move-types.rs index 890950ea08c..6505a97de6e 100644 --- a/src/test/ui/suggestions/suggest-move-types.rs +++ b/src/test/ui/suggestions/suggest-move-types.rs @@ -33,7 +33,7 @@ struct A> { //~ ERROR associated type bindings must be declar struct Al<'a, T, M: OneWithLifetime> { //~^ ERROR associated type bindings must be declared after generic parameters -//~^^ ERROR lifetime arguments must be declared prior to type arguments +//~^^ ERROR type provided when a lifetime was expected m: M, t: &'a T, } @@ -47,7 +47,7 @@ struct B> { //~ ERROR associated ty struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { //~^ ERROR associated type bindings must be declared after generic parameters -//~^^ ERROR lifetime arguments must be declared prior to type arguments +//~^^ ERROR type provided when a lifetime was expected m: M, t: &'a T, u: &'b U, @@ -63,7 +63,7 @@ struct C> { //~ ERROR associated ty struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { //~^ ERROR associated type bindings must be declared after generic parameters -//~^^ ERROR lifetime arguments must be declared prior to type arguments +//~^^ ERROR lifetime provided when a type was expected m: M, t: &'a T, u: &'b U, @@ -79,7 +79,7 @@ struct D> { //~ ERROR associated ty struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { //~^ ERROR associated type bindings must be declared after generic parameters -//~^^ ERROR lifetime arguments must be declared prior to type arguments +//~^^ ERROR lifetime provided when a type was expected m: M, t: &'a T, u: &'b U, diff --git a/src/test/ui/suggestions/suggest-move-types.stderr b/src/test/ui/suggestions/suggest-move-types.stderr index 552fb78cd3f..ac91813f928 100644 --- a/src/test/ui/suggestions/suggest-move-types.stderr +++ b/src/test/ui/suggestions/suggest-move-types.stderr @@ -74,29 +74,38 @@ LL | struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime $DIR/suggest-move-types.rs:34:46 +error[E0747]: type provided when a lifetime was expected + --> $DIR/suggest-move-types.rs:34:43 | LL | struct Al<'a, T, M: OneWithLifetime> { - | ^^ + | ^ + | + = note: lifetime arguments must be provided before type arguments -error: lifetime arguments must be declared prior to type arguments - --> $DIR/suggest-move-types.rs:48:80 +error[E0747]: type provided when a lifetime was expected + --> $DIR/suggest-move-types.rs:48:71 | LL | struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { - | ^^ ^^ ^^ + | ^ + | + = note: lifetime arguments must be provided before type arguments -error: lifetime arguments must be declared prior to type arguments +error[E0747]: lifetime provided when a type was expected --> $DIR/suggest-move-types.rs:64:56 | LL | struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { - | ^^ ^^ ^^ + | ^^ + | + = note: type arguments must be provided before lifetime arguments -error: lifetime arguments must be declared prior to type arguments +error[E0747]: lifetime provided when a type was expected --> $DIR/suggest-move-types.rs:80:56 | LL | struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { - | ^^ ^^ ^^ + | ^^ + | + = note: type arguments must be provided before lifetime arguments error: aborting due to 12 previous errors +For more information about this error, try `rustc --explain E0747`. diff --git a/src/test/ui/traits/trait-object-vs-lifetime.rs b/src/test/ui/traits/trait-object-vs-lifetime.rs index e0ff7349483..e885cd2f68a 100644 --- a/src/test/ui/traits/trait-object-vs-lifetime.rs +++ b/src/test/ui/traits/trait-object-vs-lifetime.rs @@ -12,6 +12,6 @@ fn main() { //~^ ERROR wrong number of lifetime arguments: expected 1, found 2 //~| ERROR wrong number of type arguments: expected 1, found 0 let _: S; - //~^ ERROR lifetime arguments must be declared prior to type arguments + //~^ ERROR type provided when a lifetime was expected //~| ERROR at least one trait is required for an object type } diff --git a/src/test/ui/traits/trait-object-vs-lifetime.stderr b/src/test/ui/traits/trait-object-vs-lifetime.stderr index be1958770a4..04529fb8cfa 100644 --- a/src/test/ui/traits/trait-object-vs-lifetime.stderr +++ b/src/test/ui/traits/trait-object-vs-lifetime.stderr @@ -1,9 +1,3 @@ -error: lifetime arguments must be declared prior to type arguments - --> $DIR/trait-object-vs-lifetime.rs:14:29 - | -LL | let _: S; - | ^^^^^^^ - error[E0224]: at least one trait is required for an object type --> $DIR/trait-object-vs-lifetime.rs:9:23 | @@ -28,6 +22,15 @@ error[E0224]: at least one trait is required for an object type LL | let _: S; | ^^^^^^^^^^^^^ +error[E0747]: type provided when a lifetime was expected + --> $DIR/trait-object-vs-lifetime.rs:14:14 + | +LL | let _: S; + | ^^^^^^^^^^^^^ + | + = note: lifetime arguments must be provided before type arguments + error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0107`. +Some errors have detailed explanations: E0107, E0747. +For more information about an error, try `rustc --explain E0107`.