about summary refs log tree commit diff
diff options
context:
space:
mode:
authorvarkor <github@varkor.com>2018-08-08 00:01:47 +0100
committervarkor <github@varkor.com>2018-08-19 20:02:34 +0100
commit68b0e7dd99ff374adf1babc481e415bad133cba7 (patch)
treebfbd8b71187e004ed51e4f1916f27e5648844fe7
parent49c45734c0d1a038e44767bdf9cd6721652d5002 (diff)
downloadrust-68b0e7dd99ff374adf1babc481e415bad133cba7.tar.gz
rust-68b0e7dd99ff374adf1babc481e415bad133cba7.zip
Refactor generic argument count check in method/confirm.rs
-rw-r--r--src/librustc/hir/mod.rs7
-rw-r--r--src/librustc_typeck/astconv.rs288
-rw-r--r--src/librustc_typeck/check/method/confirm.rs22
-rw-r--r--src/librustc_typeck/check/mod.rs2
4 files changed, 195 insertions, 124 deletions
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index 991b80ad3e9..8fb2bb63760 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -401,6 +401,13 @@ impl GenericArg {
             GenericArg::Type(t) => t.span,
         }
     }
+
+    pub fn id(&self) -> NodeId {
+        match self {
+            GenericArg::Lifetime(l) => l.id,
+            GenericArg::Type(t) => t.id,
+        }
+    }
 }
 
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index cd297d5c653..2e59c5959fb 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -30,11 +30,12 @@ use require_c_abi_if_variadic;
 use util::common::ErrorReported;
 use util::nodemap::{FxHashSet, FxHashMap};
 use errors::{FatalError, DiagnosticId};
+use lint;
 
 use std::iter;
 use syntax::ast;
 use syntax::feature_gate::{GateIssue, emit_feature_err};
-use syntax_pos::Span;
+use syntax_pos::{Span, MultiSpan};
 
 pub trait AstConv<'gcx, 'tcx> {
     fn tcx<'a>(&'a self) -> TyCtxt<'a, 'gcx, 'tcx>;
@@ -172,21 +173,164 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
         -> &'tcx Substs<'tcx>
     {
 
-        let (substs, assoc_bindings) =
-            item_segment.with_generic_args(|generic_args| {
-                self.create_substs_for_ast_path(
-                    span,
-                    def_id,
-                    generic_args,
-                    item_segment.infer_types,
-                    None)
-            });
+        let (substs, assoc_bindings) = item_segment.with_generic_args(|generic_args| {
+            self.create_substs_for_ast_path(
+                span,
+                def_id,
+                generic_args,
+                item_segment.infer_types,
+                None,
+            )
+        });
 
-        assoc_bindings.first().map(|b| self.prohibit_projection(b.span));
+        assoc_bindings.first().map(|b| Self::prohibit_assoc_ty_binding(self.tcx(), b.span));
 
         substs
     }
 
+    /// Check that the correct number of generic arguments have been provided.
+    /// This is used both for type declarations and function calls.
+    pub fn check_generic_arg_count(
+        tcx: TyCtxt,
+        span: Span,
+        def: &ty::Generics,
+        args: &hir::GenericArgs,
+        is_declaration: bool,
+        is_method_call: bool,
+        has_self: bool,
+        infer_types: bool,
+    ) -> bool {
+        // 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.
+        let param_counts = def.own_counts();
+        let arg_counts = args.own_counts();
+        let infer_lifetimes = !is_declaration && arg_counts.lifetimes == 0;
+
+        let mut defaults: ty::GenericParamCount = Default::default();
+        for param in &def.params {
+            match param.kind {
+                GenericParamDefKind::Lifetime => {}
+                GenericParamDefKind::Type { has_default, .. } => {
+                    defaults.types += has_default as usize
+                }
+            };
+        }
+
+        if !is_declaration && !args.bindings.is_empty() {
+            AstConv::prohibit_assoc_ty_binding(tcx, args.bindings[0].span);
+        }
+
+        // Prohibit explicit lifetime arguments if late-bound lifetime parameters are present.
+        if !infer_lifetimes {
+            if let Some(span_late) = def.has_late_bound_regions {
+                let msg = "cannot specify lifetime arguments explicitly \
+                           if late bound lifetime parameters are present";
+                let note = "the late bound lifetime parameter is introduced here";
+                let span = args.args[0].span();
+                if !is_method_call && arg_counts.lifetimes != param_counts.lifetimes {
+                    let mut err = tcx.sess.struct_span_err(span, msg);
+                    err.span_note(span_late, note);
+                    err.emit();
+                    return true;
+                } else {
+                    let mut multispan = MultiSpan::from_span(span);
+                    multispan.push_span_label(span_late, note.to_string());
+                    tcx.lint_node(lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS,
+                                  args.args[0].id(), multispan, msg);
+                    return false;
+                }
+            }
+        }
+
+        let check_kind_count = |error_code_less: &str,
+                                error_code_more: &str,
+                                kind,
+                                required,
+                                permitted,
+                                provided| {
+            // We enforce the following: `required` <= `provided` <= `permitted`.
+            // For kinds without defaults (i.e. lifetimes), `required == permitted`.
+            // For other kinds (i.e. types), `permitted` may be greater than `required`.
+            if required <= provided && provided <= permitted {
+                return false;
+            }
+
+            // 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, suppress_error) = if required != permitted {
+                if provided < required {
+                    (required, "at least ", false)
+                } else { // provided > permitted
+                    (permitted, "at most ", true)
+                }
+            } else {
+                (required, "", false)
+            };
+            let label = if required == permitted && provided > permitted {
+                let diff = provided - permitted;
+                format!(
+                    "{}unexpected {} argument{}",
+                    if diff != 1 { format!("{} ", diff) } else { String::new() },
+                    kind,
+                    if diff != 1 { "s" } else { "" },
+                )
+            } else {
+                format!(
+                    "expected {}{} {} argument{}",
+                    quantifier,
+                    bound,
+                    kind,
+                    if required != 1 { "s" } else { "" },
+                )
+            };
+
+            tcx.sess.struct_span_err_with_code(
+                span,
+                &format!(
+                    "wrong number of {} arguments: expected {}{}, found {}",
+                    kind,
+                    quantifier,
+                    bound,
+                    provided,
+                ),
+                DiagnosticId::Error({
+                    if provided <= permitted {
+                        error_code_less
+                    } else {
+                        error_code_more
+                    }
+                }.into())
+            ).span_label(span, label).emit();
+
+            suppress_error
+        };
+
+        if !infer_lifetimes || arg_counts.lifetimes > param_counts.lifetimes {
+            check_kind_count(
+                "E0107",
+                "E0107",
+                "lifetime",
+                param_counts.lifetimes,
+                param_counts.lifetimes,
+                arg_counts.lifetimes,
+            );
+        }
+        if !infer_types
+            || arg_counts.types > param_counts.types - defaults.types - has_self as usize {
+            check_kind_count(
+                "E0243",
+                "E0244", // FIXME: E0243 and E0244 should be unified.
+                "type",
+                param_counts.types - defaults.types - has_self as usize,
+                param_counts.types - has_self as usize,
+                arg_counts.types,
+            )
+        } else {
+            false
+        }
+    }
+
     /// Creates the relevant generic argument substitutions
     /// corresponding to a set of generic parameters.
     pub fn create_substs_for_generic_args<'a, 'b, A, P, I>(
@@ -355,7 +499,16 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
         assert_eq!(generic_params.has_self, self_ty.is_some());
 
         let has_self = generic_params.has_self;
-        check_generic_arg_count(tcx, span, &generic_params, &generic_args, has_self, infer_types);
+        Self::check_generic_arg_count(
+            self.tcx(),
+            span,
+            &generic_params,
+            &generic_args,
+            true, // `is_declaration`
+            false, // `is_method_call` (irrelevant here)
+            has_self,
+            infer_types,
+        );
 
         let is_object = self_ty.map_or(false, |ty| ty.sty == TRAIT_OBJECT_DUMMY_SELF);
         let default_needs_object_self = |param: &ty::GenericParamDef| {
@@ -548,7 +701,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
                                                  trait_def_id,
                                                  self_ty,
                                                  trait_segment);
-        assoc_bindings.first().map(|b| self.prohibit_projection(b.span));
+        assoc_bindings.first().map(|b| AstConv::prohibit_assoc_ty_binding(self.tcx(), b.span));
         ty::TraitRef::new(trait_def_id, substs)
     }
 
@@ -1113,15 +1266,15 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
                     }
                 }
                 for binding in &generic_args.bindings {
-                    self.prohibit_projection(binding.span);
+                    Self::prohibit_assoc_ty_binding(self.tcx(), binding.span);
                     break;
                 }
             })
         }
     }
 
-    pub fn prohibit_projection(&self, span: Span) {
-        let mut err = struct_span_err!(self.tcx().sess, span, E0229,
+    pub fn prohibit_assoc_ty_binding(tcx: TyCtxt, span: Span) {
+        let mut err = struct_span_err!(tcx.sess, span, E0229,
                                        "associated type bindings are not allowed here");
         err.span_label(span, "associated type not allowed here").emit();
     }
@@ -1497,109 +1650,6 @@ fn split_auto_traits<'a, 'b, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
     (auto_traits, trait_bounds)
 }
 
-pub fn check_generic_arg_count(
-    tcx: TyCtxt,
-    span: Span,
-    def: &ty::Generics,
-    args: &hir::GenericArgs,
-    has_self: bool,
-    infer_types: bool,
-) {
-    // 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.
-    let param_counts = def.own_counts();
-    let arg_counts = args.own_counts();
-
-    let mut defaults: ty::GenericParamCount = Default::default();
-    for param in &def.params {
-        match param.kind {
-            GenericParamDefKind::Lifetime => {}
-            GenericParamDefKind::Type { has_default, .. } => defaults.types += has_default as usize,
-        };
-    }
-
-    let check_kind_count = |error_code_less: &str,
-                            error_code_more: &str,
-                            kind,
-                            required,
-                            permitted,
-                            provided| {
-        // We enforce the following: `required` <= `provided` <= `permitted`.
-        // For kinds without defaults (i.e. lifetimes), `required == permitted`.
-        // For other kinds (i.e. types), `permitted` may be greater than `required`.
-        if required <= provided && provided <= permitted {
-            return;
-        }
-
-        // 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 label = if required == permitted && provided > permitted {
-            let diff = provided - permitted;
-            format!(
-                "{}unexpected {} argument{}",
-                if diff != 1 { format!("{} ", diff) } else { String::new() },
-                kind,
-                if diff != 1 { "s" } else { "" },
-            )
-        } else {
-            format!(
-                "expected {}{} {} argument{}",
-                quantifier,
-                bound,
-                kind,
-                if required != 1 { "s" } else { "" },
-            )
-        };
-
-        tcx.sess.struct_span_err_with_code(
-            span,
-            &format!(
-                "wrong number of {} arguments: expected {}{}, found {}",
-                kind,
-                quantifier,
-                bound,
-                provided,
-            ),
-            DiagnosticId::Error({
-                if provided <= permitted {
-                    error_code_less
-                } else {
-                    error_code_more
-                }
-            }.into())
-        ).span_label(span, label).emit();
-    };
-
-    check_kind_count(
-        "E0107",
-        "E0107",
-        "lifetime",
-        param_counts.lifetimes,
-        param_counts.lifetimes,
-        arg_counts.lifetimes,
-    );
-    if !infer_types || arg_counts.types > param_counts.types - defaults.types - has_self as usize {
-        check_kind_count(
-            "E0243",
-            "E0244", // FIXME: E0243 and E0244 should be unified.
-            "type",
-            param_counts.types - defaults.types - has_self as usize,
-            param_counts.types - has_self as usize,
-            arg_counts.types,
-        );
-    }
-}
-
 // A helper struct for conveniently grouping a set of bounds which we pass to
 // and return from functions in multiple places.
 #[derive(PartialEq, Eq, Clone, Debug)]
diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs
index 739c2013950..49225680432 100644
--- a/src/librustc_typeck/check/method/confirm.rs
+++ b/src/librustc_typeck/check/method/confirm.rs
@@ -14,6 +14,7 @@ use astconv::AstConv;
 use check::{FnCtxt, PlaceOp, callee, Needs};
 use hir::GenericArg;
 use hir::def_id::DefId;
+use hir::HirVec;
 use rustc::ty::subst::Substs;
 use rustc::traits;
 use rustc::ty::{self, Ty, GenericParamDefKind};
@@ -22,8 +23,9 @@ use rustc::ty::adjustment::{Adjustment, Adjust, OverloadedDeref};
 use rustc::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
 use rustc::ty::fold::TypeFoldable;
 use rustc::infer::{self, InferOk};
-use syntax_pos::Span;
 use rustc::hir;
+use syntax_pos::Span;
+use syntax::ptr::P;
 
 use std::ops::Deref;
 
@@ -315,9 +317,21 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
         // If they were not explicitly supplied, just construct fresh
         // variables.
         let method_generics = self.tcx.generics_of(pick.item.def_id);
-        let supress_mismatch = self.fcx.check_impl_trait(self.span, segment, &method_generics);
-        self.fcx.check_generic_arg_count(self.span, &segment, &method_generics, true,
-                                         supress_mismatch);
+        let suppress_mismatch = self.fcx.check_impl_trait(self.span, segment, &method_generics);
+        AstConv::check_generic_arg_count(
+            self.tcx,
+            self.span,
+            &method_generics,
+            &segment.args.clone().unwrap_or_else(|| P(hir::GenericArgs {
+                args: HirVec::new(), bindings: HirVec::new(), parenthesized: false,
+            })),
+            false, // `is_declaration`
+            true, // `is_method_call`
+            method_generics.parent.is_none() && method_generics.has_self,
+            segment.infer_types || suppress_mismatch,
+        );
+        // self.fcx.check_generic_arg_count(self.span, &segment, &method_generics, true,
+                                        //  supress_mismatch);
 
         // Create subst for early-bound lifetime parameters, combining
         // parameters from the type and those from the method.
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index f37ea819122..ae5c0fc6fb1 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -5168,7 +5168,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         }
 
         if !bindings.is_empty() {
-            AstConv::prohibit_projection(self, bindings[0].span);
+            AstConv::prohibit_assoc_ty_binding(self.tcx, bindings[0].span);
         }
 
         let infer_lifetimes = lifetimes.len() == 0;