Refactor mod/check (part i)

This commit is contained in:
varkor 2018-06-25 19:52:50 +01:00
parent 2317abdd01
commit d1a82af235
2 changed files with 175 additions and 111 deletions

View File

@ -315,9 +315,9 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
// If they were not explicitly supplied, just construct fresh // If they were not explicitly supplied, just construct fresh
// variables. // variables.
let method_generics = self.tcx.generics_of(pick.item.def_id); let method_generics = self.tcx.generics_of(pick.item.def_id);
let mut fn_segment = Some((segment, method_generics)); let fn_segment = Some((segment, method_generics));
let supress_mismatch = self.fcx.check_impl_trait(self.span, fn_segment); let supress_mismatch = self.fcx.check_impl_trait(self.span, fn_segment);
self.fcx.check_generic_arg_count(self.span, &mut fn_segment, true, supress_mismatch); self.fcx.check_generic_arg_count(self.span, &segment, &method_generics, true, supress_mismatch);
// Create subst for early-bound lifetime parameters, combining // Create subst for early-bound lifetime parameters, combining
// parameters from the type and those from the method. // parameters from the type and those from the method.

View File

@ -113,7 +113,7 @@ use util::nodemap::{DefIdMap, DefIdSet, FxHashMap, NodeMap};
use std::cell::{Cell, RefCell, Ref, RefMut}; use std::cell::{Cell, RefCell, Ref, RefMut};
use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::Lrc;
use std::collections::hash_map::Entry; use std::collections::{hash_map::Entry, HashSet};
use std::cmp; use std::cmp;
use std::fmt::Display; use std::fmt::Display;
use std::iter; use std::iter;
@ -505,6 +505,9 @@ impl<'gcx, 'tcx> EnclosingBreakables<'gcx, 'tcx> {
} }
} }
#[derive(Debug)]
struct PathSeg(DefId, usize);
pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
body_id: ast::NodeId, body_id: ast::NodeId,
@ -4770,20 +4773,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
err.span_suggestion(span_semi, "consider removing this semicolon", "".to_string()); err.span_suggestion(span_semi, "consider removing this semicolon", "".to_string());
} }
// Instantiates the given path, which must refer to an item with the given fn def_ids_for_path_segments(&self,
// number of type parameters and type. segments: &[hir::PathSegment],
pub fn instantiate_value_path(&self, def: Def)
segments: &[hir::PathSegment], -> Vec<PathSeg> {
opt_self_ty: Option<Ty<'tcx>>,
def: Def,
span: Span,
node_id: ast::NodeId)
-> Ty<'tcx> {
debug!("instantiate_value_path(path={:?}, def={:?}, node_id={})",
segments,
def,
node_id);
// We need to extract the type parameters supplied by the user in // We need to extract the type parameters supplied by the user in
// the path `path`. Due to the current setup, this is a bit of a // the path `path`. Due to the current setup, this is a bit of a
// tricky-process; the problem is that resolve only tells us the // tricky-process; the problem is that resolve only tells us the
@ -4829,8 +4822,119 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// The first step then is to categorize the segments appropriately. // The first step then is to categorize the segments appropriately.
assert!(!segments.is_empty()); assert!(!segments.is_empty());
let last = segments.len() - 1;
let mut path_segs = vec![];
match def {
// Case 1. Reference to a struct/variant constructor.
Def::StructCtor(def_id, ..) |
Def::VariantCtor(def_id, ..) => {
// Everything but the final segment should have no
// parameters at all.
let mut generics = self.tcx.generics_of(def_id);
// Variant and struct constructors use the
// generics of their parent type definition.
let generics_def_id = generics.parent.unwrap_or(def_id);
path_segs.push(PathSeg(generics_def_id, last));
}
// Case 2. Reference to a top-level value.
Def::Fn(def_id) |
Def::Const(def_id) |
Def::Static(def_id, _) => {
path_segs.push(PathSeg(def_id, last));
}
// Case 3. Reference to a method or associated const.
Def::Method(def_id) |
Def::AssociatedConst(def_id) => {
if segments.len() >= 2 {
let generics = self.tcx.generics_of(def_id);
path_segs.push(PathSeg(generics.parent.unwrap(), last - 1));
}
path_segs.push(PathSeg(def_id, last));
}
// Case 4. Local variable, no generics.
Def::Local(..) | Def::Upvar(..) => {}
_ => bug!("unexpected definition: {:?}", def),
}
debug!("path_segs = {:?}", path_segs);
path_segs
}
// Instantiates the given path, which must refer to an item with the given
// number of type parameters and type.
pub fn instantiate_value_path(&self,
segments: &[hir::PathSegment],
opt_self_ty: Option<Ty<'tcx>>,
def: Def,
span: Span,
node_id: ast::NodeId)
-> Ty<'tcx> {
debug!("instantiate_value_path(path={:?}, def={:?}, node_id={})",
segments,
def,
node_id);
let path_segs = self.def_ids_for_path_segments(segments, def);
let mut ufcs_associated = None; let mut ufcs_associated = None;
match def {
Def::Method(def_id) |
Def::AssociatedConst(def_id) => {
let container = self.tcx.associated_item(def_id).container;
match container {
ty::TraitContainer(trait_did) => {
callee::check_legal_trait_for_method_call(self.tcx, span, trait_did)
}
ty::ImplContainer(_) => {}
}
if segments.len() == 1 {
// `<T>::assoc` will end up here, and so can `T::assoc`.
let self_ty = opt_self_ty.expect("UFCS sugared assoc missing Self");
ufcs_associated = Some((container, self_ty));
}
}
_ => {}
}
// Now that we have categorized what space the parameters for each
// segment belong to, let's sort out the parameters that the user
// provided (if any) into their appropriate spaces. We'll also report
// errors if type parameters are provided in an inappropriate place.
let mut generic_segs = HashSet::new();
for PathSeg(_, index) in &path_segs {
generic_segs.insert(index);
}
let segs: Vec<_> = segments
.iter()
.enumerate()
.filter_map(|(index, seg)| {
if !generic_segs.contains(&index) {
Some(seg)
} else {
None
}
})
.cloned()
.collect();
AstConv::prohibit_generics(self, &segs);
match def {
Def::Local(nid) | Def::Upvar(nid, ..) => {
let ty = self.local_ty(span, nid);
let ty = self.normalize_associated_types_in(span, &ty);
self.write_ty(self.tcx.hir.node_to_hir_id(node_id), ty);
return ty;
}
_ => {}
}
let mut type_segment = None; let mut type_segment = None;
let mut fn_segment = None; let mut fn_segment = None;
match def { match def {
@ -4858,52 +4962,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// Case 3. Reference to a method or associated const. // Case 3. Reference to a method or associated const.
Def::Method(def_id) | Def::Method(def_id) |
Def::AssociatedConst(def_id) => { Def::AssociatedConst(def_id) => {
let container = self.tcx.associated_item(def_id).container;
match container {
ty::TraitContainer(trait_did) => {
callee::check_legal_trait_for_method_call(self.tcx, span, trait_did)
}
ty::ImplContainer(_) => {}
}
let generics = self.tcx.generics_of(def_id); let generics = self.tcx.generics_of(def_id);
if segments.len() >= 2 { if segments.len() >= 2 {
let parent_generics = self.tcx.generics_of(generics.parent.unwrap()); let parent_generics = self.tcx.generics_of(generics.parent.unwrap());
type_segment = Some((&segments[segments.len() - 2], parent_generics)); type_segment = Some((&segments[segments.len() - 2], parent_generics));
} else {
// `<T>::assoc` will end up here, and so can `T::assoc`.
let self_ty = opt_self_ty.expect("UFCS sugared assoc missing Self");
ufcs_associated = Some((container, self_ty));
} }
fn_segment = Some((segments.last().unwrap(), generics)); fn_segment = Some((segments.last().unwrap(), generics));
} }
// Case 4. Local variable, no generics. _ => {}
Def::Local(..) | Def::Upvar(..) => {}
_ => bug!("unexpected definition: {:?}", def),
} }
debug!("type_segment={:?} fn_segment={:?}", type_segment, fn_segment); debug!("type_segment={:?} fn_segment={:?}", type_segment, fn_segment);
// Now that we have categorized what space the parameters for each
// segment belong to, let's sort out the parameters that the user
// provided (if any) into their appropriate spaces. We'll also report
// errors if type parameters are provided in an inappropriate place.
let poly_segments = type_segment.is_some() as usize +
fn_segment.is_some() as usize;
AstConv::prohibit_generics(self, &segments[..segments.len() - poly_segments]);
match def {
Def::Local(nid) | Def::Upvar(nid, ..) => {
let ty = self.local_ty(span, nid);
let ty = self.normalize_associated_types_in(span, &ty);
self.write_ty(self.tcx.hir.node_to_hir_id(node_id), ty);
return ty;
}
_ => {}
}
// Now we have to compare the types that the user *actually* // Now we have to compare the types that the user *actually*
// provided against the types that were *expected*. If the user // provided against the types that were *expected*. If the user
// did not provide any types, then we want to substitute inference // did not provide any types, then we want to substitute inference
@ -4911,17 +4982,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// to add defaults. If the user provided *too many* types, that's // to add defaults. If the user provided *too many* types, that's
// a problem. // a problem.
let supress_mismatch = self.check_impl_trait(span, fn_segment); let supress_mismatch = self.check_impl_trait(span, fn_segment);
self.check_generic_arg_count(span, &mut type_segment, false, supress_mismatch); for &PathSeg(def_id, index) in &path_segs {
self.check_generic_arg_count(span, &mut fn_segment, false, supress_mismatch); let generics = self.tcx.generics_of(def_id);
self.check_generic_arg_count(span, &segments[index], &generics, false, supress_mismatch);
}
let (fn_start, has_self) = match (type_segment, fn_segment) { let has_self = path_segs.last().map(|PathSeg(def_id, _)| {
(_, Some((_, generics))) => { self.tcx.generics_of(*def_id).has_self
(generics.parent_count, generics.has_self) }).unwrap_or(false);
}
(Some((_, generics)), None) => { let fn_start = match (type_segment, fn_segment) {
(generics.params.len(), generics.has_self) (_, Some((_, generics))) => generics.parent_count,
} (Some((_, generics)), None) => generics.params.len(),
(None, None) => (0, false) (None, None) => 0,
}; };
// FIXME(varkor): Separating out the parameters is messy. // FIXME(varkor): Separating out the parameters is messy.
let mut lifetimes_type_seg = vec![]; let mut lifetimes_type_seg = vec![];
@ -5091,64 +5164,55 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
/// Report errors if the provided parameters are too few or too many. /// Report errors if the provided parameters are too few or too many.
fn check_generic_arg_count(&self, fn check_generic_arg_count(&self,
span: Span, span: Span,
segment: &mut Option<(&hir::PathSegment, &ty::Generics)>, segment: &hir::PathSegment,
generics: &ty::Generics,
is_method_call: bool, is_method_call: bool,
supress_mismatch_error: bool) { supress_mismatch_error: bool) {
let (lifetimes, types, infer_types, bindings) = segment.map_or( let (mut lifetimes, mut types) = (vec![], vec![]);
(vec![], vec![], true, &[][..]), let infer_types = segment.infer_types;
|(s, _)| { let mut bindings = vec![];
s.args.as_ref().map_or( if let Some(ref data) = segment.args {
(vec![], vec![], s.infer_types, &[][..]), data.args.iter().for_each(|arg| match arg {
|data| { GenericArg::Lifetime(lt) => lifetimes.push(lt.clone()),
let (mut lifetimes, mut types) = (vec![], vec![]); GenericArg::Type(ty) => types.push(ty.clone()),
data.args.iter().for_each(|arg| match arg {
GenericArg::Lifetime(lt) => lifetimes.push(lt),
GenericArg::Type(ty) => types.push(ty),
});
(lifetimes, types, s.infer_types, &data.bindings[..])
}
)
}); });
bindings = data.bindings.clone().to_vec();
}
// Check provided parameters. struct ParamRange {
let ((ty_required, ty_accepted), lt_accepted) = required: usize,
segment.map_or(((0, 0), 0), |(_, generics)| { accepted: usize
struct ParamRange {
required: usize,
accepted: usize
};
let mut lt_accepted = 0;
let mut ty_params = ParamRange { required: 0, accepted: 0 };
for param in &generics.params {
match param.kind {
GenericParamDefKind::Lifetime => lt_accepted += 1,
GenericParamDefKind::Type { has_default, .. } => {
ty_params.accepted += 1;
if !has_default {
ty_params.required += 1;
}
}
};
}
if generics.parent.is_none() && generics.has_self {
ty_params.required -= 1;
ty_params.accepted -= 1;
}
((ty_params.required, ty_params.accepted), lt_accepted)
});
let count_type_params = |n| {
format!("{} type parameter{}", n, if n == 1 { "" } else { "s" })
}; };
let mut lt_accepted = 0;
let mut ty_params = ParamRange { required: 0, accepted: 0 };
for param in &generics.params {
match param.kind {
GenericParamDefKind::Lifetime => lt_accepted += 1,
GenericParamDefKind::Type { has_default, .. } => {
ty_params.accepted += 1;
if !has_default {
ty_params.required += 1;
}
}
};
}
if generics.parent.is_none() && generics.has_self {
ty_params.required -= 1;
ty_params.accepted -= 1;
}
let ty_accepted = ty_params.accepted;
let ty_required = ty_params.required;
let count_type_params = |n| format!("{} type parameter{}", n, if n == 1 { "" } else { "s" });
let expected_text = count_type_params(ty_accepted); let expected_text = count_type_params(ty_accepted);
let actual_text = count_type_params(types.len()); let actual_text = count_type_params(types.len());
if let Some((mut err, span)) = if types.len() > ty_accepted { if let Some((mut err, span)) = if types.len() > ty_accepted {
// To prevent derived errors to accumulate due to extra // To prevent derived errors to accumulate due to extra
// type parameters, we force instantiate_value_path to // type parameters, we force instantiate_value_path to
// use inference variables instead of the provided types. // use inference variables instead of the provided types.
*segment = None; // FIXME(varkor)
// *segment = None;
let span = types[ty_accepted].span; let span = types[ty_accepted].span;
Some((struct_span_err!(self.tcx.sess, span, E0087, Some((struct_span_err!(self.tcx.sess, span, E0087,
"too many type parameters provided: \ "too many type parameters provided: \
@ -5172,8 +5236,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let infer_lifetimes = lifetimes.len() == 0; let infer_lifetimes = lifetimes.len() == 0;
// Prohibit explicit lifetime arguments if late bound lifetime parameters are present. // Prohibit explicit lifetime arguments if late bound lifetime parameters are present.
let has_late_bound_lifetime_defs = let has_late_bound_lifetime_defs = generics.has_late_bound_regions;
segment.map_or(None, |(_, generics)| generics.has_late_bound_regions);
if let (Some(span_late), false) = (has_late_bound_lifetime_defs, lifetimes.is_empty()) { if let (Some(span_late), false) = (has_late_bound_lifetime_defs, lifetimes.is_empty()) {
// Report this as a lint only if no error was reported previously. // Report this as a lint only if no error was reported previously.
let primary_msg = "cannot specify lifetime arguments explicitly \ let primary_msg = "cannot specify lifetime arguments explicitly \
@ -5184,7 +5247,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let mut err = self.tcx.sess.struct_span_err(lifetimes[0].span, primary_msg); let mut err = self.tcx.sess.struct_span_err(lifetimes[0].span, primary_msg);
err.span_note(span_late, note_msg); err.span_note(span_late, note_msg);
err.emit(); err.emit();
*segment = None; // FIXME(varkor)
// *segment = None;
} else { } else {
let mut multispan = MultiSpan::from_span(lifetimes[0].span); let mut multispan = MultiSpan::from_span(lifetimes[0].span);
multispan.push_span_label(span_late, note_msg.to_string()); multispan.push_span_label(span_late, note_msg.to_string());