]> git.lizzy.rs Git - rust.git/commitdiff
Implement default type parameters in generics.
authorEduard Burtescu <edy.burt@gmail.com>
Thu, 30 Jan 2014 17:28:02 +0000 (19:28 +0200)
committerEduard Burtescu <edy.burt@gmail.com>
Thu, 30 Jan 2014 17:28:41 +0000 (19:28 +0200)
33 files changed:
src/librustc/front/feature_gate.rs
src/librustc/metadata/tydecode.rs
src/librustc/metadata/tyencode.rs
src/librustc/middle/lint.rs
src/librustc/middle/resolve.rs
src/librustc/middle/subst.rs
src/librustc/middle/trans/debuginfo.rs
src/librustc/middle/trans/type_of.rs
src/librustc/middle/ty.rs
src/librustc/middle/typeck/astconv.rs
src/librustc/middle/typeck/check/mod.rs
src/librustc/middle/typeck/collect.rs
src/librustc/util/ppaux.rs
src/libsyntax/ast.rs
src/libsyntax/ext/build.rs
src/libsyntax/ext/deriving/generic.rs
src/libsyntax/ext/deriving/ty.rs
src/libsyntax/fold.rs
src/libsyntax/parse/parser.rs
src/libsyntax/print/pprust.rs
src/libsyntax/visit.rs
src/test/auxiliary/default_type_params_xc.rs [new file with mode: 0644]
src/test/compile-fail/bad-mid-path-type-params.rs
src/test/compile-fail/gated-default-type-params.rs [new file with mode: 0644]
src/test/compile-fail/generic-impl-less-params-with-defaults.rs [new file with mode: 0644]
src/test/compile-fail/generic-impl-more-params-with-defaults.rs [new file with mode: 0644]
src/test/compile-fail/generic-non-trailing-defaults.rs [new file with mode: 0644]
src/test/compile-fail/generic-type-less-params-with-defaults.rs [new file with mode: 0644]
src/test/compile-fail/generic-type-more-params-with-defaults.rs [new file with mode: 0644]
src/test/compile-fail/generic-type-params-name-repr.rs [new file with mode: 0644]
src/test/compile-fail/lint-default-type-param-usage.rs [new file with mode: 0644]
src/test/run-pass/generic-default-type-params-cross-crate.rs [new file with mode: 0644]
src/test/run-pass/generic-default-type-params.rs [new file with mode: 0644]

index a0d7b1dee120be956dbbbd586af63a654932468d..bfb27594108545cdf4b07d0512036e6f0dbf864c 100644 (file)
@@ -48,6 +48,7 @@
     ("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 @@ fn visit_expr(&mut self, e: &ast::Expr, _: ()) {
         }
         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) {
index a40ba4a44a5db62904d70f5d96e4cc477e6df6fd..c986016206047e15f67f1846c9e6c0ce652e8806 100644 (file)
@@ -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 {
index 23e96110c066568dd1e32f341f3501d15a5bf7f2..9da19d666673f1ab3ba2f8258421da0b047288df 100644 (file)
@@ -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));
 }
index 9ccd9a463a37bb5117827c4d5965ac50de1f86d9..89d5ca740120ede60bb51689eefaee90798a7c23 100644 (file)
@@ -86,6 +86,7 @@ pub enum Lint {
     AttributeUsage,
     UnknownFeatures,
     UnknownCrateType,
+    DefaultTypeParamUsage,
 
     ManagedHeapMemory,
     OwnedHeapMemory,
@@ -359,6 +360,7 @@ enum LintSource {
         desc: "unknown features found in crate-level #[feature] directives",
         default: deny,
     }),
+
     ("unknown_crate_type",
     LintSpec {
         lint: UnknownCrateType,
@@ -379,6 +381,13 @@ enum LintSource {
         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,
+     }),
 ];
 
 /*
index dab9c7c71d9685f3047148fa062197366c5cfc3b..e235e914689e8f6b6bfe485b4b504f50cc1bbce7 100644 (file)
@@ -3931,6 +3931,10 @@ fn resolve_type_parameters(&mut self,
             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 => {}
+            }
         }
     }
 
index 89442d0cc848b9580df4d408dc656b06bad9943a..067082992c6fc87554ee60162b78ba4d8438f211 100644 (file)
@@ -164,7 +164,8 @@ fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> 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))
         }
     }
 }
index b9877d327cb1a423bf7c1f565bfef6c2342ca219..c5e0600ea57066274e9e413d1e0bb48bcc18e334 100644 (file)
@@ -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);
index a4228fbd17b3db731e4d2d953e9efba90ebec34a..86456187d1ad1bdfb78ff45a714c7d01f32a1d18 100644 (file)
@@ -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 {
index 3182c4a7ec2856b9796e8099cfe4f15f129c34d4..3b5dd67366c5de54bcd9853dfbed190d835cc77a 100644 (file)
@@ -855,7 +855,8 @@ fn to_str(&self) -> ~str {
 pub struct TypeParameterDef {
     ident: ast::Ident,
     def_id: ast::DefId,
-    bounds: @ParamBounds
+    bounds: @ParamBounds,
+    default: Option<ty::t>
 }
 
 #[deriving(Encodable, Decodable, Clone)]
index f1e1f379b0c5e0d691bce69b431aa2b2f628913f..144ce99198f4ae4286284b4f656996498379cccb 100644 (file)
@@ -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<AC:AstConv,RS:RegionScope>(
     };
 
     // 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,
index cefda4372749cba983455faf5caa868cd266b873..5e6e3c95692940c58df925863eee5f055a6f4e2a 100644 (file)
@@ -81,6 +81,7 @@
 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.
index 04d708f0638e9fd403ecda7b2af0bf6d518c2315..1077454039deff35131a7eacc3263134706917f2 100644 (file)
@@ -345,7 +345,8 @@ fn make_static_method_ty(ccx: &CrateCtxt,
             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, &param.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));
 
index 7199c945627468fbffa1e2d33a472af10bb2f8c0..f391239df330402bd2cfb37e6cb323e19e54f241 100644 (file)
@@ -485,12 +485,13 @@ fn push_sig_to_str(cx: ctxt,
       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 @@ fn push_sig_to_str(cx: ctxt,
 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 @@ fn user_string(&self, tcx: ctxt) -> ~str {
         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)
         }
     }
 }
index 3070ecde71e6be7e3f8591577faac41c0564306c..40ae98791efbd0ab76b9e7e2d0abbfc264c90e24 100644 (file)
@@ -206,7 +206,8 @@ pub enum TyParamBound {
 pub struct TyParam {
     ident: Ident,
     id: NodeId,
-    bounds: OptVec<TyParamBound>
+    bounds: OptVec<TyParamBound>,
+    default: Option<P<Ty>>
 }
 
 #[deriving(Clone, Eq, Encodable, Decodable, IterBytes)]
index 3b43c96a184cdf656088fbb83dc07bddfbb55032..9ad4f4f7fac2d4b7006c18d40a5fbbb160b24053 100644 (file)
@@ -66,7 +66,10 @@ fn ty_rptr(&self, span: Span,
     fn ty_field_imm(&self, span: Span, name: Ident, ty: P<ast::Ty>) -> ast::TypeField;
     fn strip_bounds(&self, bounds: &Generics) -> Generics;
 
-    fn typaram(&self, id: ast::Ident, bounds: OptVec<ast::TyParamBound>) -> ast::TyParam;
+    fn typaram(&self,
+               id: ast::Ident,
+               bounds: OptVec<ast::TyParamBound>,
+               default: Option<P<ast::Ty>>) -> ast::TyParam;
 
     fn trait_ref(&self, path: ast::Path) -> ast::TraitRef;
     fn typarambound(&self, path: ast::Path) -> ast::TyParamBound;
@@ -354,8 +357,16 @@ fn ty_nil(&self) -> P<ast::Ty> {
         })
     }
 
-    fn typaram(&self, id: ast::Ident, bounds: OptVec<ast::TyParamBound>) -> ast::TyParam {
-        ast::TyParam { ident: id, id: ast::DUMMY_NODE_ID, bounds: bounds }
+    fn typaram(&self,
+               id: ast::Ident,
+               bounds: OptVec<ast::TyParamBound>,
+               default: Option<P<ast::Ty>>) -> 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
index 3c9401b109f3a133bdad568b8905b73152fb2970..9ebb771f5da5bfe2646052d89f748e6f703e4655 100644 (file)
@@ -375,7 +375,7 @@ fn create_derived_impl(&self,
             // 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.
index b22dcfe0da2c9a602e6132a36c426c701adad59e..893a1c68426579cbd73fc3ae687cd2da5f110d42 100644 (file)
@@ -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 {
index d02f6384990a95e6d0334c398c2484c7573cbda4..8dac13f1e31a9a307290c79f443271d8ded355d1 100644 (file)
@@ -406,6 +406,7 @@ pub fn fold_ty_param<T: Folder>(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))
     }
 }
 
index 0c81e87c7b8874ed36b5a9fa819648be4a7dabd6..04a984ba95d92b7dc2fe9b102b3f5cc5490fc603 100644 (file)
@@ -3452,13 +3452,25 @@ fn parse_optional_ty_param_bounds(&mut self) -> Option<OptVec<TyParamBound>> {
         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 "<T>" and "<T:>".
         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 @@ fn parse_ty_param(&mut self) -> TyParam {
     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()
index 019f86b342dc95f14741626da300d2cecf31c6ff..2783284ea8b05a862560f4b9a89ae1b2a07c2f42 100644 (file)
@@ -1905,6 +1905,14 @@ fn print_item(s: &mut State, generics: &ast::Generics, idx: uint) {
                 let param = generics.ty_params.get(idx);
                 print_ident(s, param.ident);
                 print_bounds(s, &param.bounds, false);
+                match param.default {
+                    Some(default) => {
+                        space(&mut s.s);
+                        word_space(s, "=");
+                        print_type(s, default);
+                    }
+                    _ => {}
+                }
             }
         }
 
index 7201fc3a380520f5f05731dad9b7586717bd631c..2a6c14f0eaef721d0281d745f43525ae480aecf8 100644 (file)
@@ -470,7 +470,11 @@ pub fn walk_generics<E: Clone, V: Visitor<E>>(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 (file)
index 0000000..b2df2c4
--- /dev/null
@@ -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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T, A = FakeHeap>;
index 90d6147f0eda3416956c715d62251930a2cafd76..82eddf669af7af8d31140773f9a488e3888bd4e5 100644 (file)
@@ -29,9 +29,9 @@ fn new<U>(x: int, _: U) -> S2 {
 }
 
 fn foo<'a>() {
-    let _ = S::new::<int,f64>(1, 1.0);    //~ ERROR the impl referenced by this path has 1 type parameter, but 0 type parameters were supplied
+    let _ = S::new::<int,f64>(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::<f64>(1, 1.0);  //~ ERROR expected 0 lifetime parameter(s)
-    let _: S2 = Trait::new::<int,f64>(1, 1.0);    //~ ERROR the trait referenced by this path has 1 type parameter, but 0 type parameters were supplied
+    let _: S2 = Trait::new::<int,f64>(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::<f64>(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 (file)
index 0000000..65575d4
--- /dev/null
@@ -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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+struct Heap;
+
+struct Vec<T, A = Heap>; //~ 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 (file)
index 0000000..28e7a37
--- /dev/null
@@ -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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[feature(default_type_params)];
+
+struct Foo<A, B, C = (A, B)>;
+
+impl<A, B, C = (A, B)> Foo<A, B, C> {
+    fn new() -> Foo<A, B, C> {Foo}
+}
+
+fn main() {
+    Foo::<int>::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 (file)
index 0000000..1040468
--- /dev/null
@@ -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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T, A = Heap>;
+
+impl<T, A = Heap> Vec<T, A> {
+    fn new() -> Vec<T, A> {Vec}
+}
+
+fn main() {
+    Vec::<int, Heap, bool>::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 (file)
index 0000000..a70a529
--- /dev/null
@@ -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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<A = Heap, T>; //~ ERROR type parameters with a default must be trailing
+
+struct Foo<A, B = Vec<C>, 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 (file)
index 0000000..c5badee
--- /dev/null
@@ -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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T, A = Heap>;
+
+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 (file)
index 0000000..65c0d09
--- /dev/null
@@ -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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T, A = Heap>;
+
+fn main() {
+    let _: Vec<int, Heap, bool>; //~ 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 (file)
index 0000000..59e6ba8
--- /dev/null
@@ -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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T = A, U = B, V = C>;
+
+fn main() {
+    // Ensure that the printed type doesn't include the default type params...
+    let _: Foo<int> = ();
+    //~^ ERROR mismatched types: expected `Foo<int>` but found `()`
+
+    // ...even when they're present, but the same types as the defaults.
+    let _: Foo<int, B, C> = ();
+    //~^ ERROR mismatched types: expected `Foo<int>` but found `()`
+
+    // But not when there's a different type in between.
+    let _: Foo<A, int, C> = ();
+    //~^ ERROR mismatched types: expected `Foo<A,int>` but found `()`
+
+    // And don't print <> at all when there's just defaults.
+    let _: Foo<A, B, C> = ();
+    //~^ 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 (file)
index 0000000..e8cacb0
--- /dev/null
@@ -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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T, A = Heap>;
+
+pub struct FooAlloc;
+
+pub type VecFoo<T> = Vec<T, FooAlloc>; //~ 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 (file)
index 0000000..b90b8f3
--- /dev/null
@@ -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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T, A = default_type_params_xc::Heap>;
+
+struct Foo;
+
+fn main() {
+    let _a = Vec::<int>;
+    let _b = Vec::<int, default_type_params_xc::FakeHeap>;
+    let _c = default_type_params_xc::FakeVec::<int>;
+    let _d = default_type_params_xc::FakeVec::<int, Foo>;
+}
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 (file)
index 0000000..bb5a923
--- /dev/null
@@ -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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 = (int, char)> {
+    a: A
+}
+
+impl Foo<int> {
+    fn bar_int(&self) -> int {
+        self.a
+    }
+}
+
+impl Foo<char> {
+    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<A: Clone> Foo<A> {
+    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') });
+}