diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index c4a30171687..7176b512c71 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -13,8 +13,10 @@ use middle::ty; use middle::ty_fold; use middle::ty_fold::TypeFolder; +use util::ppaux::Repr; use std::rc::Rc; +use syntax::codemap::Span; use syntax::opt_vec::OptVec; /////////////////////////////////////////////////////////////////////////// @@ -22,9 +24,16 @@ use syntax::opt_vec::OptVec; // // Just call `foo.subst(tcx, substs)` to perform a substitution across // `foo`. +// Or use `foo.subst_spanned(tcx, substs, Some(span))` when there is more +// information available (for better errors). pub trait Subst { - fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> Self; + fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> Self { + self.subst_spanned(tcx, substs, None) + } + fn subst_spanned(&self, tcx: ty::ctxt, + substs: &ty::substs, + span: Option) -> Self; } /////////////////////////////////////////////////////////////////////////// @@ -36,11 +45,18 @@ pub trait Subst { // our current method/trait matching algorithm. - Niko impl Subst for ty::t { - fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::t { - if ty::substs_is_noop(substs) { + fn subst_spanned(&self, tcx: ty::ctxt, + substs: &ty::substs, + span: Option) -> ty::t { + if ty::substs_is_noop(substs) && !ty::type_has_params(*self) { *self } else { - let mut folder = SubstFolder {tcx: tcx, substs: substs}; + let mut folder = SubstFolder { + tcx: tcx, + substs: substs, + span: span, + root_ty: Some(*self) + }; folder.fold_ty(*self) } } @@ -48,7 +64,13 @@ impl Subst for ty::t { struct SubstFolder<'a> { tcx: ty::ctxt, - substs: &'a ty::substs + substs: &'a ty::substs, + + // The location for which the substitution is performed, if available. + span: Option, + + // The root type that is being substituted, if available. + root_ty: Option } impl<'a> TypeFolder for SubstFolder<'a> { @@ -65,14 +87,42 @@ impl<'a> TypeFolder for SubstFolder<'a> { match ty::get(t).sty { ty::ty_param(p) => { - self.substs.tps[p.idx] + if p.idx < self.substs.tps.len() { + self.substs.tps[p.idx] + } else { + let root_msg = match self.root_ty { + Some(root) => format!(" in the substitution of `{}`", + root.repr(self.tcx)), + None => ~"" + }; + let m = format!("missing type param `{}`{}", + t.repr(self.tcx), root_msg); + match self.span { + Some(span) => self.tcx.sess.span_err(span, m), + None => self.tcx.sess.err(m) + } + ty::mk_err() + } } ty::ty_self(_) => { - self.substs.self_ty.expect("ty_self not found in substs") - } - _ => { - ty_fold::super_fold_ty(self, t) + match self.substs.self_ty { + Some(ty) => ty, + None => { + let root_msg = match self.root_ty { + Some(root) => format!(" in the substitution of `{}`", + root.repr(self.tcx)), + None => ~"" + }; + let m = format!("missing `Self` type param{}", root_msg); + match self.span { + Some(span) => self.tcx.sess.span_err(span, m), + None => self.tcx.sess.err(m) + } + ty::mk_err() + } + } } + _ => ty_fold::super_fold_ty(self, t) } } } @@ -81,112 +131,145 @@ impl<'a> TypeFolder for SubstFolder<'a> { // Other types impl Subst for ~[T] { - fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ~[T] { - self.map(|t| t.subst(tcx, substs)) + fn subst_spanned(&self, tcx: ty::ctxt, + substs: &ty::substs, + span: Option) -> ~[T] { + self.map(|t| t.subst_spanned(tcx, substs, span)) } } impl Subst for Rc { - fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> Rc { - Rc::new(self.borrow().subst(tcx, substs)) + fn subst_spanned(&self, tcx: ty::ctxt, + substs: &ty::substs, + span: Option) -> Rc { + Rc::new(self.borrow().subst_spanned(tcx, substs, span)) } } impl Subst for OptVec { - fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> OptVec { - self.map(|t| t.subst(tcx, substs)) + fn subst_spanned(&self, tcx: ty::ctxt, + substs: &ty::substs, + span: Option) -> OptVec { + self.map(|t| t.subst_spanned(tcx, substs, span)) } } impl Subst for @T { - fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> @T { + fn subst_spanned(&self, tcx: ty::ctxt, + substs: &ty::substs, + span: Option) -> @T { match self { - t => @(**t).subst(tcx, substs) + t => @(**t).subst_spanned(tcx, substs, span) } } } impl Subst for Option { - fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> Option { - self.as_ref().map(|t| t.subst(tcx, substs)) + fn subst_spanned(&self, tcx: ty::ctxt, + substs: &ty::substs, + span: Option) -> Option { + self.as_ref().map(|t| t.subst_spanned(tcx, substs, span)) } } impl Subst for ty::TraitRef { - fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::TraitRef { + fn subst_spanned(&self, tcx: ty::ctxt, + substs: &ty::substs, + span: Option) -> ty::TraitRef { ty::TraitRef { def_id: self.def_id, - substs: self.substs.subst(tcx, substs) + substs: self.substs.subst_spanned(tcx, substs, span) } } } impl Subst for ty::substs { - fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::substs { + fn subst_spanned(&self, tcx: ty::ctxt, + substs: &ty::substs, + span: Option) -> ty::substs { ty::substs { - regions: self.regions.subst(tcx, substs), - self_ty: self.self_ty.map(|typ| typ.subst(tcx, substs)), - tps: self.tps.map(|typ| typ.subst(tcx, substs)) + regions: self.regions.subst_spanned(tcx, substs, span), + self_ty: self.self_ty.map(|typ| typ.subst_spanned(tcx, substs, span)), + tps: self.tps.map(|typ| typ.subst_spanned(tcx, substs, span)) } } } impl Subst for ty::RegionSubsts { - fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::RegionSubsts { + fn subst_spanned(&self, tcx: ty::ctxt, + substs: &ty::substs, + span: Option) -> ty::RegionSubsts { match *self { ty::ErasedRegions => { ty::ErasedRegions } ty::NonerasedRegions(ref regions) => { - ty::NonerasedRegions(regions.subst(tcx, substs)) + ty::NonerasedRegions(regions.subst_spanned(tcx, substs, span)) } } } } impl Subst for ty::BareFnTy { - fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::BareFnTy { - let mut folder = SubstFolder {tcx: tcx, substs: substs}; + fn subst_spanned(&self, tcx: ty::ctxt, + substs: &ty::substs, + span: Option) -> ty::BareFnTy { + let mut folder = SubstFolder { + tcx: tcx, + substs: substs, + span: span, + root_ty: None + }; folder.fold_bare_fn_ty(self) } } impl Subst for ty::ParamBounds { - fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::ParamBounds { + fn subst_spanned(&self, tcx: ty::ctxt, + substs: &ty::substs, + span: Option) -> ty::ParamBounds { ty::ParamBounds { builtin_bounds: self.builtin_bounds, - trait_bounds: self.trait_bounds.subst(tcx, substs) + trait_bounds: self.trait_bounds.subst_spanned(tcx, substs, span) } } } impl Subst for ty::TypeParameterDef { - fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::TypeParameterDef { + fn subst_spanned(&self, tcx: ty::ctxt, + substs: &ty::substs, + span: Option) -> ty::TypeParameterDef { ty::TypeParameterDef { ident: self.ident, def_id: self.def_id, - bounds: self.bounds.subst(tcx, substs), - default: self.default.map(|x| x.subst(tcx, substs)) + bounds: self.bounds.subst_spanned(tcx, substs, span), + default: self.default.map(|x| x.subst_spanned(tcx, substs, span)) } } } impl Subst for ty::Generics { - fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::Generics { + fn subst_spanned(&self, tcx: ty::ctxt, + substs: &ty::substs, + span: Option) -> ty::Generics { ty::Generics { - type_param_defs: self.type_param_defs.subst(tcx, substs), - region_param_defs: self.region_param_defs.subst(tcx, substs), + type_param_defs: self.type_param_defs.subst_spanned(tcx, substs, span), + region_param_defs: self.region_param_defs.subst_spanned(tcx, substs, span), } } } impl Subst for ty::RegionParameterDef { - fn subst(&self, _: ty::ctxt, _: &ty::substs) -> ty::RegionParameterDef { + fn subst_spanned(&self, _: ty::ctxt, + _: &ty::substs, + _: Option) -> ty::RegionParameterDef { *self } } impl Subst for ty::Region { - fn subst(&self, _tcx: ty::ctxt, substs: &ty::substs) -> ty::Region { + fn subst_spanned(&self, _tcx: ty::ctxt, + substs: &ty::substs, + _: Option) -> ty::Region { // Note: This routine only handles regions that are bound on // type declarationss and other outer declarations, not those // bound in *fn types*. Region substitution of the bound @@ -206,10 +289,12 @@ impl Subst for ty::Region { } impl Subst for ty::ty_param_bounds_and_ty { - fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::ty_param_bounds_and_ty { + fn subst_spanned(&self, tcx: ty::ctxt, + substs: &ty::substs, + span: Option) -> ty::ty_param_bounds_and_ty { ty::ty_param_bounds_and_ty { - generics: self.generics.subst(tcx, substs), - ty: self.ty.subst(tcx, substs) + generics: self.generics.subst_spanned(tcx, substs, span), + ty: self.ty.subst_spanned(tcx, substs, span) } } } diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index 90abdc5ac50..906bb504bb0 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -52,6 +52,7 @@ use middle::const_eval; use middle::lint; +use middle::subst::Subst; use middle::ty::{substs}; use middle::ty::{ty_param_substs_and_ty}; use middle::ty; @@ -228,16 +229,23 @@ fn ast_path_substs( ~"provided type arguments with defaults"); } - let defaults = decl_generics.type_param_defs().slice_from(supplied_ty_param_count) - .iter().map(|&x| x.default.unwrap()); let tps = path.segments.iter().flat_map(|s| s.types.iter()) .map(|&a_t| ast_ty_to_ty(this, rscope, a_t)) - .chain(defaults).collect(); - substs { + .collect(); + + let mut substs = substs { regions: ty::NonerasedRegions(regions), self_ty: self_ty, tps: tps + }; + + for param in decl_generics.type_param_defs() + .slice_from(supplied_ty_param_count).iter() { + let ty = param.default.unwrap().subst_spanned(tcx, &substs, Some(path.span)); + substs.tps.push(ty); } + + substs } pub fn ast_path_to_substs_and_ty user_ty_param_count { let expected = if user_ty_param_req < user_ty_param_count { "expected at most" @@ -3778,7 +3779,7 @@ pub fn instantiate_path(fcx: @FnCtxt, (span, format!("too many type parameters provided: {} {}, found {}", expected, user_ty_param_count, ty_substs_len)); - fcx.infcx().next_ty_vars(ty_param_count) + (fcx.infcx().next_ty_vars(ty_param_count), regions) } else if ty_substs_len < user_ty_param_req { let expected = if user_ty_param_req < user_ty_param_count { "expected at least" @@ -3789,7 +3790,7 @@ pub fn instantiate_path(fcx: @FnCtxt, (span, format!("not enough type parameters provided: {} {}, found {}", expected, user_ty_param_req, ty_substs_len)); - fcx.infcx().next_ty_vars(ty_param_count) + (fcx.infcx().next_ty_vars(ty_param_count), regions) } else { if ty_substs_len > user_ty_param_req { fcx.tcx().sess.add_lint(lint::DefaultTypeParamUsage, node_id, pth.span, @@ -3798,50 +3799,71 @@ pub fn instantiate_path(fcx: @FnCtxt, // Build up the list of type parameters, inserting the self parameter // at the appropriate position. - let mut result = ~[]; + let mut tps = ~[]; let mut pushed = false; + for (i, ty) in pth.segments.iter() + .flat_map(|segment| segment.types.iter()) + .map(|&ast_type| fcx.to_ty(ast_type)) + .enumerate() { + match self_parameter_index { + Some(index) if index == i => { + tps.push(fcx.infcx().next_ty_vars(1)[0]); + pushed = true; + } + _ => {} + } + tps.push(ty) + } + + let mut substs = substs { + regions: regions, + self_ty: None, + tps: tps + }; + let defaults = tpt.generics.type_param_defs().iter() .enumerate().filter_map(|(i, x)| { match self_parameter_index { Some(index) if index == i => None, _ => Some(x.default) } - }).skip(ty_substs_len).map(|x| match x { - Some(default) => default, - None => { - fcx.tcx().sess.span_bug(span, - "missing default for a not explicitely provided type param") - } }); - for (i, ty) in pth.segments.iter() - .flat_map(|segment| segment.types.iter()) - .map(|&ast_type| fcx.to_ty(ast_type)) - .chain(defaults).enumerate() { + for (i, default) in defaults.skip(ty_substs_len).enumerate() { match self_parameter_index { - Some(index) if index == i => { - result.push(fcx.infcx().next_ty_vars(1)[0]); + Some(index) if index == i + ty_substs_len => { + substs.tps.push(fcx.infcx().next_ty_vars(1)[0]); pushed = true; } _ => {} } - result.push(ty) + match default { + Some(default) => { + let ty = default.subst_spanned(fcx.tcx(), &substs, Some(span)); + substs.tps.push(ty); + } + None => { + fcx.tcx().sess.span_bug(span, + "missing default for a not explicitely provided type param") + } + } } // If the self parameter goes at the end, insert it there. if !pushed && self_parameter_index.is_some() { - result.push(fcx.infcx().next_ty_vars(1)[0]) + substs.tps.push(fcx.infcx().next_ty_vars(1)[0]) } - assert_eq!(result.len(), ty_param_count) - result + assert_eq!(substs.tps.len(), ty_param_count) + + let substs {tps, regions, ..} = substs; + (tps, regions) }; - let substs = substs { - regions: ty::NonerasedRegions(regions), + fcx.write_ty_substs(node_id, tpt.ty, substs { + regions: regions, self_ty: None, tps: tps - }; - fcx.write_ty_substs(node_id, tpt.ty, substs); + }); debug!("<<<"); } diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index eba99c7fb5a..334407ff122 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -525,13 +525,25 @@ pub fn parameterized(cx: ctxt, } else { ty::lookup_item_type(cx, did).generics }; - let ty_params = generics.type_param_defs().iter(); - let num_defaults = ty_params.zip(tps.iter()).rev().take_while(|&(def, &actual)| { - match def.default { - Some(default) => default == actual, - None => false - } - }).len(); + let ty_params = generics.type_param_defs(); + let has_defaults = ty_params.last().map_or(false, |def| def.default.is_some()); + let num_defaults = if has_defaults { + // We should have a borrowed version of substs instead of cloning. + let mut substs = ty::substs { + tps: tps.to_owned(), + regions: regions.clone(), + self_ty: None + }; + ty_params.iter().zip(tps.iter()).rev().take_while(|&(def, &actual)| { + substs.tps.pop(); + match def.default { + Some(default) => ty::subst(cx, &substs, default) == actual, + None => false + } + }).len() + } else { + 0 + }; for t in tps.slice_to(tps.len() - num_defaults).iter() { strs.push(ty_to_str(cx, *t)) diff --git a/src/test/compile-fail/generic-type-params-forward-mention.rs b/src/test/compile-fail/generic-type-params-forward-mention.rs new file mode 100644 index 00000000000..003ffdc8cc0 --- /dev/null +++ b/src/test/compile-fail/generic-type-params-forward-mention.rs @@ -0,0 +1,19 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[feature(default_type_params)]; + +// Ensure that we get an error and not an ICE for this problematic case. +struct Foo, U = bool>; + +fn main() { + let x: Foo; + //~^ ERROR missing type param `U` in the substitution of `std::option::Option` +} diff --git a/src/test/compile-fail/generic-type-params-name-repr.rs b/src/test/compile-fail/generic-type-params-name-repr.rs index 59e6ba87ecb..066bbc38e1a 100644 --- a/src/test/compile-fail/generic-type-params-name-repr.rs +++ b/src/test/compile-fail/generic-type-params-name-repr.rs @@ -15,6 +15,9 @@ struct B; struct C; struct Foo; +struct Hash; +struct HashMap>; + fn main() { // Ensure that the printed type doesn't include the default type params... let _: Foo = (); @@ -24,6 +27,12 @@ fn main() { let _: Foo = (); //~^ ERROR mismatched types: expected `Foo` but found `()` + // Including cases where the default is using previous type params. + let _: HashMap<~str, int> = (); + //~^ ERROR mismatched types: expected `HashMap<~str,int>` but found `()` + let _: HashMap<~str, int, Hash<~str>> = (); + //~^ ERROR mismatched types: expected `HashMap<~str,int>` but found `()` + // But not when there's a different type in between. let _: Foo = (); //~^ ERROR mismatched types: expected `Foo` but found `()` diff --git a/src/test/run-pass/generic-default-type-params.rs b/src/test/run-pass/generic-default-type-params.rs index bb5a923ce9e..1521710072d 100644 --- a/src/test/run-pass/generic-default-type-params.rs +++ b/src/test/run-pass/generic-default-type-params.rs @@ -52,6 +52,16 @@ fn default_foo(x: Foo) { assert_eq!(x.baz(), (1, 'a')); } +#[deriving(Eq)] +struct BazHelper(T); + +#[deriving(Eq)] +// Ensure that we can use previous type parameters in defaults. +struct Baz, V = Option>(T, U, V); + fn main() { default_foo(Foo { a: (1, 'a') }); + + let x: Baz = Baz(true, BazHelper(false), Some(BazHelper(true))); + assert_eq!(x, Baz(true, BazHelper(false), Some(BazHelper(true)))); }