diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs index a0d7b1dee120b..bfb2759410854 100644 --- a/src/librustc/front/feature_gate.rs +++ b/src/librustc/front/feature_gate.rs @@ -48,6 +48,7 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[ ("log_syntax", Active), ("trace_macros", Active), ("simd", Active), + ("default_type_params", Active), // These are used to test this portion of the compiler, they don't actually // mean anything @@ -234,6 +235,20 @@ impl Visitor<()> for Context { } visit::walk_expr(self, e, ()); } + + fn visit_generics(&mut self, generics: &ast::Generics, _: ()) { + for type_parameter in generics.ty_params.iter() { + match type_parameter.default { + Some(ty) => { + self.gate_feature("default_type_params", ty.span, + "default type parameters are \ + experimental and possibly buggy"); + } + None => {} + } + } + visit::walk_generics(self, generics, ()); + } } pub fn check_crate(sess: Session, crate: &ast::Crate) { diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index a40ba4a44a5db..c986016206047 100644 --- a/src/librustc/metadata/tydecode.rs +++ b/src/librustc/metadata/tydecode.rs @@ -573,9 +573,12 @@ pub fn parse_type_param_def_data(data: &[u8], start: uint, } fn parse_type_param_def(st: &mut PState, conv: conv_did) -> ty::TypeParameterDef { - ty::TypeParameterDef {ident: parse_ident(st, ':'), - def_id: parse_def(st, NominalType, |x,y| conv(x,y)), - bounds: @parse_bounds(st, |x,y| conv(x,y))} + ty::TypeParameterDef { + ident: parse_ident(st, ':'), + def_id: parse_def(st, NominalType, |x,y| conv(x,y)), + bounds: @parse_bounds(st, |x,y| conv(x,y)), + default: parse_opt(st, |st| parse_ty(st, |x,y| conv(x,y))) + } } fn parse_bounds(st: &mut PState, conv: conv_did) -> ty::ParamBounds { diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs index 23e96110c0665..9da19d666673f 100644 --- a/src/librustc/metadata/tyencode.rs +++ b/src/librustc/metadata/tyencode.rs @@ -421,4 +421,5 @@ fn enc_bounds(w: &mut MemWriter, cx: @ctxt, bs: &ty::ParamBounds) { pub fn enc_type_param_def(w: &mut MemWriter, cx: @ctxt, v: &ty::TypeParameterDef) { mywrite!(w, "{}:{}|", cx.tcx.sess.str_of(v.ident), (cx.ds)(v.def_id)); enc_bounds(w, cx, v.bounds); + enc_opt(w, v.default, |w, t| enc_ty(w, cx, t)); } diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs index 9ccd9a463a37b..89d5ca740120e 100644 --- a/src/librustc/middle/lint.rs +++ b/src/librustc/middle/lint.rs @@ -86,6 +86,7 @@ pub enum Lint { AttributeUsage, UnknownFeatures, UnknownCrateType, + DefaultTypeParamUsage, ManagedHeapMemory, OwnedHeapMemory, @@ -359,6 +360,7 @@ static lint_table: &'static [(&'static str, LintSpec)] = &[ desc: "unknown features found in crate-level #[feature] directives", default: deny, }), + ("unknown_crate_type", LintSpec { lint: UnknownCrateType, @@ -379,6 +381,13 @@ static lint_table: &'static [(&'static str, LintSpec)] = &[ desc: "unused result of an expression in a statement", default: allow, }), + + ("default_type_param_usage", + LintSpec { + lint: DefaultTypeParamUsage, + desc: "prevents explicitly setting a type parameter with a default", + default: deny, + }), ]; /* diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index dab9c7c71d968..e235e914689e8 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -3931,6 +3931,10 @@ impl Resolver { for bound in type_parameter.bounds.iter() { self.resolve_type_parameter_bound(type_parameter.id, bound); } + match type_parameter.default { + Some(ty) => self.resolve_type(ty), + None => {} + } } } diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index 89442d0cc848b..067082992c6fc 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -164,7 +164,8 @@ impl Subst for ty::TypeParameterDef { ty::TypeParameterDef { ident: self.ident, def_id: self.def_id, - bounds: self.bounds.subst(tcx, substs) + bounds: self.bounds.subst(tcx, substs), + default: self.default.map(|x| x.subst(tcx, substs)) } } } diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index b9877d327cb1a..c5e0600ea5706 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -2000,7 +2000,8 @@ fn trait_metadata(cx: &CrateContext, ppaux::mutability_to_str(mutability) + token::ident_to_str(&ident); // Add type and region parameters - let name = ppaux::parameterized(cx.tcx, name, &substs.regions, substs.tps); + let name = ppaux::parameterized(cx.tcx, name, &substs.regions, + substs.tps, def_id, true); let (containing_scope, definition_span) = get_namespace_and_span_for_item(cx, def_id, usage_site_span); diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs index a4228fbd17b3d..86456187d1ad1 100644 --- a/src/librustc/middle/trans/type_of.rs +++ b/src/librustc/middle/trans/type_of.rs @@ -330,7 +330,8 @@ pub fn llvm_type_name(cx: &CrateContext, an_enum => { "enum" } }; let tstr = ppaux::parameterized(cx.tcx, ty::item_path_str(cx.tcx, did), - &ty::NonerasedRegions(opt_vec::Empty), tps); + &ty::NonerasedRegions(opt_vec::Empty), + tps, did, false); if did.crate == 0 { format!("{}.{}", name, tstr) } else { diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 3182c4a7ec285..3b5dd67366c5d 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -855,7 +855,8 @@ impl ToStr for IntVarValue { pub struct TypeParameterDef { ident: ast::Ident, def_id: ast::DefId, - bounds: @ParamBounds + bounds: @ParamBounds, + default: Option } #[deriving(Encodable, Decodable, Clone)] diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index f1e1f379b0c5e..144ce99198f4a 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -51,6 +51,7 @@ use middle::const_eval; +use middle::lint; use middle::ty::{substs}; use middle::ty::{ty_param_substs_and_ty}; use middle::ty; @@ -195,21 +196,43 @@ fn ast_path_substs( }; // Convert the type parameters supplied by the user. - let supplied_type_parameter_count = - path.segments.iter().flat_map(|s| s.types.iter()).len(); - if decl_generics.type_param_defs.len() != supplied_type_parameter_count { - this.tcx().sess.span_fatal( - path.span, - format!("wrong number of type arguments: expected {} but found {}", - decl_generics.type_param_defs.len(), - supplied_type_parameter_count)); + let supplied_ty_param_count = path.segments.iter().flat_map(|s| s.types.iter()).len(); + let formal_ty_param_count = decl_generics.type_param_defs.len(); + let required_ty_param_count = decl_generics.type_param_defs.iter() + .take_while(|x| x.default.is_none()) + .len(); + if supplied_ty_param_count < required_ty_param_count { + let expected = if required_ty_param_count < formal_ty_param_count { + "expected at least" + } else { + "expected" + }; + this.tcx().sess.span_fatal(path.span, + format!("wrong number of type arguments: {} {} but found {}", + expected, required_ty_param_count, supplied_ty_param_count)); + } else if supplied_ty_param_count > formal_ty_param_count { + let expected = if required_ty_param_count < formal_ty_param_count { + "expected at most" + } else { + "expected" + }; + this.tcx().sess.span_fatal(path.span, + format!("wrong number of type arguments: {} {} but found {}", + expected, formal_ty_param_count, supplied_ty_param_count)); + } + + if supplied_ty_param_count > required_ty_param_count { + let id = path.segments.iter().flat_map(|s| s.types.iter()) + .nth(required_ty_param_count).unwrap().id; + this.tcx().sess.add_lint(lint::DefaultTypeParamUsage, id, path.span, + ~"provided type arguments with defaults"); } - let tps = path.segments - .iter() - .flat_map(|s| s.types.iter()) - .map(|&a_t| ast_ty_to_ty(this, rscope, a_t)) - .collect(); + 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 { regions: ty::NonerasedRegions(regions), self_ty: self_ty, diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index cefda4372749c..5e6e3c9569294 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -81,6 +81,7 @@ use middle::const_eval; use middle::lang_items::{ExchangeHeapLangItem, GcLangItem}; use middle::lang_items::{ManagedHeapLangItem}; use middle::lint::UnreachableCode; +use middle::lint; use middle::pat_util::pat_id_map; use middle::pat_util; use middle::subst::Subst; @@ -1500,32 +1501,55 @@ fn check_type_parameter_positions_in_path(function_context: @FnCtxt, // Make sure the number of type parameters supplied on the trait // or implementation segment equals the number of type parameters // on the trait or implementation definition. - let trait_type_parameter_count = generics.type_param_defs.len(); - let supplied_type_parameter_count = trait_segment.types.len(); - if trait_type_parameter_count != supplied_type_parameter_count { - let trait_count_suffix = if trait_type_parameter_count == 1 { + let formal_ty_param_count = generics.type_param_defs.len(); + let required_ty_param_count = generics.type_param_defs.iter() + .take_while(|x| x.default.is_none()) + .len(); + let supplied_ty_param_count = trait_segment.types.len(); + if supplied_ty_param_count < required_ty_param_count { + let trait_count_suffix = if required_ty_param_count == 1 { "" } else { "s" }; - let supplied_count_suffix = - if supplied_type_parameter_count == 1 { - "" - } else { - "s" - }; - function_context.tcx() - .sess - .span_err(path.span, - format!("the {} referenced by this \ - path has {} type \ - parameter{}, but {} type \ - parameter{} were supplied", - name, - trait_type_parameter_count, - trait_count_suffix, - supplied_type_parameter_count, - supplied_count_suffix)) + let supplied_count_suffix = if supplied_ty_param_count == 1 { + "" + } else { + "s" + }; + let needs = if required_ty_param_count < generics.type_param_defs.len() { + "needs at least" + } else { + "needs" + }; + function_context.tcx().sess.span_err(path.span, + format!("the {} referenced by this path {} {} type \ + parameter{}, but {} type parameter{} were supplied", + name, needs, + required_ty_param_count, trait_count_suffix, + supplied_ty_param_count, supplied_count_suffix)) + } else if supplied_ty_param_count > formal_ty_param_count { + let trait_count_suffix = if formal_ty_param_count == 1 { + "" + } else { + "s" + }; + let supplied_count_suffix = if supplied_ty_param_count == 1 { + "" + } else { + "s" + }; + let needs = if required_ty_param_count < generics.type_param_defs.len() { + "needs at most" + } else { + "needs" + }; + function_context.tcx().sess.span_err(path.span, + format!("the {} referenced by this path {} {} type \ + parameter{}, but {} type parameter{} were supplied", + name, needs, + formal_ty_param_count, trait_count_suffix, + supplied_ty_param_count, supplied_count_suffix)) } } _ => { @@ -3683,6 +3707,9 @@ pub fn instantiate_path(fcx: @FnCtxt, debug!(">>> instantiate_path"); let ty_param_count = tpt.generics.type_param_defs.len(); + let ty_param_req = tpt.generics.type_param_defs.iter() + .take_while(|x| x.default.is_none()) + .len(); let mut ty_substs_len = 0; for segment in pth.segments.iter() { ty_substs_len += segment.types.len() @@ -3720,13 +3747,13 @@ pub fn instantiate_path(fcx: @FnCtxt, // Here we calculate the "user type parameter count", which is the number // of type parameters actually manifest in the AST. This will differ from // the internal type parameter count when there are self types involved. - let (user_type_parameter_count, self_parameter_index) = match def { + let (user_ty_param_count, user_ty_param_req, self_parameter_index) = match def { ast::DefStaticMethod(_, provenance @ ast::FromTrait(_), _) => { let generics = generics_of_static_method_container(fcx.ccx.tcx, provenance); - (ty_param_count - 1, Some(generics.type_param_defs.len())) + (ty_param_count - 1, ty_param_req - 1, Some(generics.type_param_defs.len())) } - _ => (ty_param_count, None), + _ => (ty_param_count, ty_param_req, None), }; // determine values for type parameters, using the values given by @@ -3737,27 +3764,55 @@ pub fn instantiate_path(fcx: @FnCtxt, fcx.ccx.tcx.sess.span_err (span, "this item does not take type parameters"); fcx.infcx().next_ty_vars(ty_param_count) - } else if ty_substs_len > user_type_parameter_count { + } else if ty_substs_len > user_ty_param_count { + let expected = if user_ty_param_req < user_ty_param_count { + "expected at most" + } else { + "expected" + }; fcx.ccx.tcx.sess.span_err (span, - format!("too many type parameters provided: expected {}, found {}", - user_type_parameter_count, ty_substs_len)); + format!("too many type parameters provided: {} {}, found {}", + expected, user_ty_param_count, ty_substs_len)); fcx.infcx().next_ty_vars(ty_param_count) - } else if ty_substs_len < user_type_parameter_count { + } else if ty_substs_len < user_ty_param_req { + let expected = if user_ty_param_req < user_ty_param_count { + "expected at least" + } else { + "expected" + }; fcx.ccx.tcx.sess.span_err (span, - format!("not enough type parameters provided: expected {}, found {}", - user_type_parameter_count, ty_substs_len)); + format!("not enough type parameters provided: {} {}, found {}", + expected, user_ty_param_req, ty_substs_len)); fcx.infcx().next_ty_vars(ty_param_count) } else { + if ty_substs_len > user_ty_param_req { + fcx.tcx().sess.add_lint(lint::DefaultTypeParamUsage, node_id, pth.span, + ~"provided type arguments with defaults"); + } + // Build up the list of type parameters, inserting the self parameter // at the appropriate position. let mut result = ~[]; let mut pushed = false; - for (i, &ast_type) in pth.segments - .iter() - .flat_map(|segment| segment.types.iter()) - .enumerate() { + 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() { match self_parameter_index { Some(index) if index == i => { result.push(fcx.infcx().next_ty_vars(1)[0]); @@ -3765,7 +3820,7 @@ pub fn instantiate_path(fcx: @FnCtxt, } _ => {} } - result.push(fcx.to_ty(ast_type)) + result.push(ty) } // If the self parameter goes at the end, insert it there. diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index 04d708f0638e9..1077454039def 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -345,7 +345,8 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, trait_id: ast::NodeId) { bounds: @ty::ParamBounds { builtin_bounds: ty::EmptyBuiltinBounds(), trait_bounds: ~[self_trait_ref] - } + }, + default: None }); // add in the type parameters from the method @@ -952,10 +953,12 @@ pub fn ty_generics(ccx: &CrateCtxt, let param_ty = ty::param_ty {idx: base_index + offset, def_id: local_def(param.id)}; let bounds = @compute_bounds(ccx, param_ty, ¶m.bounds); + let default = param.default.map(|x| ast_ty_to_ty(ccx, &ExplicitRscope, x)); let def = ty::TypeParameterDef { ident: param.ident, def_id: local_def(param.id), - bounds: bounds + bounds: bounds, + default: default }; debug!("def for param: {}", def.repr(ccx.tcx)); diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 7199c94562746..f391239df3304 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -485,12 +485,13 @@ pub fn ty_to_str(cx: ctxt, typ: t) -> ~str { ty_enum(did, ref substs) | ty_struct(did, ref substs) => { let path = ty::item_path(cx, did); let base = ast_map::path_to_str(path, cx.sess.intr()); - parameterized(cx, base, &substs.regions, substs.tps) + parameterized(cx, base, &substs.regions, substs.tps, did, false) } ty_trait(did, ref substs, s, mutbl, ref bounds) => { let path = ty::item_path(cx, did); let base = ast_map::path_to_str(path, cx.sess.intr()); - let ty = parameterized(cx, base, &substs.regions, substs.tps); + let ty = parameterized(cx, base, &substs.regions, + substs.tps, did, true); let bound_sep = if bounds.is_empty() { "" } else { ":" }; let bound_str = bounds.repr(cx); format!("{}{}{}{}{}", trait_store_to_str(cx, s), mutability_to_str(mutbl), ty, @@ -506,7 +507,9 @@ pub fn ty_to_str(cx: ctxt, typ: t) -> ~str { pub fn parameterized(cx: ctxt, base: &str, regions: &ty::RegionSubsts, - tps: &[ty::t]) -> ~str { + tps: &[ty::t], + did: ast::DefId, + is_trait: bool) -> ~str { let mut strs = ~[]; match *regions { @@ -518,7 +521,20 @@ pub fn parameterized(cx: ctxt, } } - for t in tps.iter() { + let generics = if is_trait { + ty::lookup_trait_def(cx, did).generics + } 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(); + + for t in tps.slice_to(tps.len() - num_defaults).iter() { strs.push(ty_to_str(cx, *t)) } @@ -969,9 +985,11 @@ impl UserString for ty::TraitRef { if tcx.sess.verbose() && self.substs.self_ty.is_some() { let mut all_tps = self.substs.tps.clone(); for &t in self.substs.self_ty.iter() { all_tps.push(t); } - parameterized(tcx, base, &self.substs.regions, all_tps) + parameterized(tcx, base, &self.substs.regions, + all_tps, self.def_id, true) } else { - parameterized(tcx, base, &self.substs.regions, self.substs.tps) + parameterized(tcx, base, &self.substs.regions, + self.substs.tps, self.def_id, true) } } } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 3070ecde71e6b..40ae98791efbd 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -206,7 +206,8 @@ pub enum TyParamBound { pub struct TyParam { ident: Ident, id: NodeId, - bounds: OptVec + bounds: OptVec, + default: Option> } #[deriving(Clone, Eq, Encodable, Decodable, IterBytes)] diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 3b43c96a184cd..9ad4f4f7fac2d 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -66,7 +66,10 @@ pub trait AstBuilder { fn ty_field_imm(&self, span: Span, name: Ident, ty: P) -> ast::TypeField; fn strip_bounds(&self, bounds: &Generics) -> Generics; - fn typaram(&self, id: ast::Ident, bounds: OptVec) -> ast::TyParam; + fn typaram(&self, + id: ast::Ident, + bounds: OptVec, + default: Option>) -> ast::TyParam; fn trait_ref(&self, path: ast::Path) -> ast::TraitRef; fn typarambound(&self, path: ast::Path) -> ast::TyParamBound; @@ -354,8 +357,16 @@ impl<'a> AstBuilder for ExtCtxt<'a> { }) } - fn typaram(&self, id: ast::Ident, bounds: OptVec) -> ast::TyParam { - ast::TyParam { ident: id, id: ast::DUMMY_NODE_ID, bounds: bounds } + fn typaram(&self, + id: ast::Ident, + bounds: OptVec, + default: Option>) -> ast::TyParam { + ast::TyParam { + ident: id, + id: ast::DUMMY_NODE_ID, + bounds: bounds, + default: default + } } // these are strange, and probably shouldn't be used outside of diff --git a/src/libsyntax/ext/deriving/generic.rs b/src/libsyntax/ext/deriving/generic.rs index 3c9401b109f3a..9ebb771f5da5b 100644 --- a/src/libsyntax/ext/deriving/generic.rs +++ b/src/libsyntax/ext/deriving/generic.rs @@ -375,7 +375,7 @@ impl<'a> TraitDef<'a> { // require the current trait bounds.push(cx.typarambound(trait_path.clone())); - trait_generics.ty_params.push(cx.typaram(ty_param.ident, bounds)); + trait_generics.ty_params.push(cx.typaram(ty_param.ident, bounds, None)); } // Create the reference to the trait. diff --git a/src/libsyntax/ext/deriving/ty.rs b/src/libsyntax/ext/deriving/ty.rs index b22dcfe0da2c9..893a1c6842657 100644 --- a/src/libsyntax/ext/deriving/ty.rs +++ b/src/libsyntax/ext/deriving/ty.rs @@ -196,7 +196,7 @@ fn mk_ty_param(cx: &ExtCtxt, span: Span, name: &str, bounds: &[Path], let path = b.to_path(cx, span, self_ident, self_generics); cx.typarambound(path) })); - cx.typaram(cx.ident_of(name), bounds) + cx.typaram(cx.ident_of(name), bounds, None) } fn mk_generics(lifetimes: ~[ast::Lifetime], ty_params: ~[ast::TyParam]) -> Generics { diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index d02f6384990a9..8dac13f1e31a9 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -406,6 +406,7 @@ pub fn fold_ty_param(tp: &TyParam, fld: &mut T) -> TyParam { ident: tp.ident, id: fld.new_id(tp.id), bounds: tp.bounds.map(|x| fold_ty_param_bound(x, fld)), + default: tp.default.map(|x| fld.fold_ty(x)) } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 0c81e87c7b887..04a984ba95d92 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3452,13 +3452,25 @@ impl Parser { return Some(result); } - // matches typaram = IDENT optbounds + // matches typaram = IDENT optbounds ( EQ ty )? fn parse_ty_param(&mut self) -> TyParam { let ident = self.parse_ident(); let opt_bounds = self.parse_optional_ty_param_bounds(); // For typarams we don't care about the difference b/w "" and "". let bounds = opt_bounds.unwrap_or_default(); - ast::TyParam { ident: ident, id: ast::DUMMY_NODE_ID, bounds: bounds } + + let default = if self.token == token::EQ { + self.bump(); + Some(self.parse_ty(false)) + } + else { None }; + + TyParam { + ident: ident, + id: ast::DUMMY_NODE_ID, + bounds: bounds, + default: default + } } // parse a set of optional generic type parameter declarations @@ -3468,9 +3480,17 @@ impl Parser { pub fn parse_generics(&mut self) -> ast::Generics { if self.eat(&token::LT) { let lifetimes = self.parse_lifetimes(); - let ty_params = self.parse_seq_to_gt( - Some(token::COMMA), - |p| p.parse_ty_param()); + let mut seen_default = false; + let ty_params = self.parse_seq_to_gt(Some(token::COMMA), |p| { + let ty_param = p.parse_ty_param(); + if ty_param.default.is_some() { + seen_default = true; + } else if seen_default { + p.span_err(p.last_span, + "type parameters with a default must be trailing"); + } + ty_param + }); ast::Generics { lifetimes: lifetimes, ty_params: ty_params } } else { ast_util::empty_generics() diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 019f86b342dc9..2783284ea8b05 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1905,6 +1905,14 @@ pub fn print_generics(s: &mut State, generics: &ast::Generics) { let param = generics.ty_params.get(idx); print_ident(s, param.ident); print_bounds(s, ¶m.bounds, false); + match param.default { + Some(default) => { + space(&mut s.s); + word_space(s, "="); + print_type(s, default); + } + _ => {} + } } } diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 7201fc3a38052..2a6c14f0eaef7 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -470,7 +470,11 @@ pub fn walk_generics>(visitor: &mut V, generics: &Generics, env: E) { for type_parameter in generics.ty_params.iter() { - walk_ty_param_bounds(visitor, &type_parameter.bounds, env.clone()) + walk_ty_param_bounds(visitor, &type_parameter.bounds, env.clone()); + match type_parameter.default { + Some(ty) => visitor.visit_ty(ty, env.clone()), + None => {} + } } walk_lifetime_decls(visitor, &generics.lifetimes, env); } diff --git a/src/test/auxiliary/default_type_params_xc.rs b/src/test/auxiliary/default_type_params_xc.rs new file mode 100644 index 0000000000000..b2df2c43c85e5 --- /dev/null +++ b/src/test/auxiliary/default_type_params_xc.rs @@ -0,0 +1,17 @@ +// 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)]; + +pub struct Heap; + +pub struct FakeHeap; + +pub struct FakeVec; diff --git a/src/test/compile-fail/bad-mid-path-type-params.rs b/src/test/compile-fail/bad-mid-path-type-params.rs index 90d6147f0eda3..82eddf669af7a 100644 --- a/src/test/compile-fail/bad-mid-path-type-params.rs +++ b/src/test/compile-fail/bad-mid-path-type-params.rs @@ -29,9 +29,9 @@ impl Trait for S2 { } fn foo<'a>() { - let _ = S::new::(1, 1.0); //~ ERROR the impl referenced by this path has 1 type parameter, but 0 type parameters were supplied + let _ = S::new::(1, 1.0); //~ ERROR the impl referenced by this path needs 1 type parameter, but 0 type parameters were supplied let _ = S::<'a,int>::new::(1, 1.0); //~ ERROR expected 0 lifetime parameter(s) - let _: S2 = Trait::new::(1, 1.0); //~ ERROR the trait referenced by this path has 1 type parameter, but 0 type parameters were supplied + let _: S2 = Trait::new::(1, 1.0); //~ ERROR the trait referenced by this path needs 1 type parameter, but 0 type parameters were supplied let _: S2 = Trait::<'a,int>::new::(1, 1.0); //~ ERROR expected 0 lifetime parameter(s) } diff --git a/src/test/compile-fail/gated-default-type-params.rs b/src/test/compile-fail/gated-default-type-params.rs new file mode 100644 index 0000000000000..65575d4fa8507 --- /dev/null +++ b/src/test/compile-fail/gated-default-type-params.rs @@ -0,0 +1,15 @@ +// 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. + +struct Heap; + +struct Vec; //~ ERROR: default type parameters are experimental + +fn main() {} diff --git a/src/test/compile-fail/generic-impl-less-params-with-defaults.rs b/src/test/compile-fail/generic-impl-less-params-with-defaults.rs new file mode 100644 index 0000000000000..28e7a37778aee --- /dev/null +++ b/src/test/compile-fail/generic-impl-less-params-with-defaults.rs @@ -0,0 +1,22 @@ +// 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)]; + +struct Foo; + +impl Foo { + fn new() -> Foo {Foo} +} + +fn main() { + Foo::::new(); //~ ERROR the impl referenced by this path needs at least 2 type parameters, but 1 type parameter were supplied + //~^ ERROR not enough type parameters provided: expected at least 2, found 1 +} diff --git a/src/test/compile-fail/generic-impl-more-params-with-defaults.rs b/src/test/compile-fail/generic-impl-more-params-with-defaults.rs new file mode 100644 index 0000000000000..10404680cdd23 --- /dev/null +++ b/src/test/compile-fail/generic-impl-more-params-with-defaults.rs @@ -0,0 +1,24 @@ +// 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)]; + +struct Heap; + +struct Vec; + +impl Vec { + fn new() -> Vec {Vec} +} + +fn main() { + Vec::::new(); //~ ERROR the impl referenced by this path needs at most 2 type parameters, but 3 type parameters were supplied + //~^ ERROR too many type parameters provided: expected at most 2, found 3 +} diff --git a/src/test/compile-fail/generic-non-trailing-defaults.rs b/src/test/compile-fail/generic-non-trailing-defaults.rs new file mode 100644 index 0000000000000..a70a52992ddc2 --- /dev/null +++ b/src/test/compile-fail/generic-non-trailing-defaults.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)]; + +struct Heap; + +struct Vec; //~ ERROR type parameters with a default must be trailing + +struct Foo, C>; //~ ERROR type parameters with a default must be trailing + +fn main() {} diff --git a/src/test/compile-fail/generic-type-less-params-with-defaults.rs b/src/test/compile-fail/generic-type-less-params-with-defaults.rs new file mode 100644 index 0000000000000..c5badee3bc5aa --- /dev/null +++ b/src/test/compile-fail/generic-type-less-params-with-defaults.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)]; + +struct Heap; + +struct Vec; + +fn main() { + let _: Vec; //~ ERROR wrong number of type arguments: expected at least 1 but found 0 +} diff --git a/src/test/compile-fail/generic-type-more-params-with-defaults.rs b/src/test/compile-fail/generic-type-more-params-with-defaults.rs new file mode 100644 index 0000000000000..65c0d09ccf4d6 --- /dev/null +++ b/src/test/compile-fail/generic-type-more-params-with-defaults.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)]; + +struct Heap; + +struct Vec; + +fn main() { + let _: Vec; //~ ERROR wrong number of type arguments: expected at most 2 but found 3 +} diff --git a/src/test/compile-fail/generic-type-params-name-repr.rs b/src/test/compile-fail/generic-type-params-name-repr.rs new file mode 100644 index 0000000000000..59e6ba87ecbf2 --- /dev/null +++ b/src/test/compile-fail/generic-type-params-name-repr.rs @@ -0,0 +1,34 @@ +// 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)]; + +struct A; +struct B; +struct C; +struct Foo; + +fn main() { + // Ensure that the printed type doesn't include the default type params... + let _: Foo = (); + //~^ ERROR mismatched types: expected `Foo` but found `()` + + // ...even when they're present, but the same types as the defaults. + let _: Foo = (); + //~^ ERROR mismatched types: expected `Foo` but found `()` + + // But not when there's a different type in between. + let _: Foo = (); + //~^ ERROR mismatched types: expected `Foo` but found `()` + + // And don't print <> at all when there's just defaults. + let _: Foo = (); + //~^ ERROR mismatched types: expected `Foo` but found `()` +} diff --git a/src/test/compile-fail/lint-default-type-param-usage.rs b/src/test/compile-fail/lint-default-type-param-usage.rs new file mode 100644 index 0000000000000..e8cacb02de8b5 --- /dev/null +++ b/src/test/compile-fail/lint-default-type-param-usage.rs @@ -0,0 +1,23 @@ +// 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)]; + +#[deny(default_type_param_usage)]; + +pub struct Heap; + +pub struct Vec; + +pub struct FooAlloc; + +pub type VecFoo = Vec; //~ ERROR provided type arguments with defaults + +fn main() {} diff --git a/src/test/run-pass/generic-default-type-params-cross-crate.rs b/src/test/run-pass/generic-default-type-params-cross-crate.rs new file mode 100644 index 0000000000000..b90b8f38019c7 --- /dev/null +++ b/src/test/run-pass/generic-default-type-params-cross-crate.rs @@ -0,0 +1,29 @@ +// 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. + +// aux-build:default_type_params_xc.rs + +// xfail-fast #[feature] doesn't work with check-fast +#[feature(default_type_params)]; + +#[allow(default_type_param_usage)]; + +extern mod default_type_params_xc; + +struct Vec; + +struct Foo; + +fn main() { + let _a = Vec::; + let _b = Vec::; + let _c = default_type_params_xc::FakeVec::; + let _d = default_type_params_xc::FakeVec::; +} diff --git a/src/test/run-pass/generic-default-type-params.rs b/src/test/run-pass/generic-default-type-params.rs new file mode 100644 index 0000000000000..bb5a923ce9e10 --- /dev/null +++ b/src/test/run-pass/generic-default-type-params.rs @@ -0,0 +1,57 @@ +// 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. + +// xfail-fast #[feature] doesn't work with check-fast +#[feature(default_type_params)]; + +#[allow(default_type_param_usage)]; + +struct Foo { + a: A +} + +impl Foo { + fn bar_int(&self) -> int { + self.a + } +} + +impl Foo { + fn bar_char(&self) -> char { + self.a + } +} + +impl Foo { + fn bar(&self) { + let (i, c): (int, char) = self.a; + assert_eq!(Foo { a: i }.bar_int(), i); + assert_eq!(Foo { a: c }.bar_char(), c); + } +} + +impl Foo { + fn baz(&self) -> A { + self.a.clone() + } +} + +fn default_foo(x: Foo) { + let (i, c): (int, char) = x.a; + assert_eq!(i, 1); + assert_eq!(c, 'a'); + + x.bar(); + assert_eq!(x.baz(), (1, 'a')); +} + +fn main() { + default_foo(Foo { a: (1, 'a') }); +}