From c5829746f9af8f1e8fdabfcbdbb8a76da2fcbd98 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Wed, 22 Oct 2014 22:22:44 -0700 Subject: [PATCH 01/34] vim: Fix :RustRun when cwd has a space in it --- src/etc/vim/autoload/rust.vim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/etc/vim/autoload/rust.vim b/src/etc/vim/autoload/rust.vim index c6b9b314da5c4..fe8e743e7826d 100644 --- a/src/etc/vim/autoload/rust.vim +++ b/src/etc/vim/autoload/rust.vim @@ -178,14 +178,14 @@ function! s:WithPath(func, ...) call mkdir(tmpdir) let save_cwd = getcwd() - silent exe 'lcd' tmpdir + silent exe 'lcd' fnameescape(tmpdir) let path = 'unnamed.rs' let save_mod = &mod set nomod - silent exe 'keepalt write! ' . path + silent exe 'keepalt write! ' . fnameescape(path) if pathisempty silent keepalt 0file endif @@ -195,10 +195,10 @@ function! s:WithPath(func, ...) call call(a:func, [path] + a:000) finally - if exists("save_mod") | let &mod = save_mod | endif - if exists("save_write") | let &write = save_write | endif - if exists("save_cwd") | silent exe 'lcd' save_cwd | endif - if exists("tmpdir") | silent call s:RmDir(tmpdir) | endif + if exists("save_mod") | let &mod = save_mod | endif + if exists("save_write") | let &write = save_write | endif + if exists("save_cwd") | silent exe 'lcd' fnameescape(save_cwd) | endif + if exists("tmpdir") | silent call s:RmDir(tmpdir) | endif endtry endfunction From 56f1a67cc7b95215274580c758ce56727a5bdc40 Mon Sep 17 00:00:00 2001 From: Piotr Szotkowski Date: Sun, 26 Oct 2014 22:27:43 +0100 Subject: [PATCH 02/34] Guide: Iterators, Macros and Unsafe wording fixes --- src/doc/guide.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/doc/guide.md b/src/doc/guide.md index c7b8e42b28cde..34ec7e4eee9af 100644 --- a/src/doc/guide.md +++ b/src/doc/guide.md @@ -4418,7 +4418,7 @@ see why consumers matter. As we've said before, an iterator is something that we can call the `.next()` method on repeatedly, and it gives us a sequence of things. Because you need -to call the method, this means that iterators are **lazy**. This code, for +to call the method, this means that iterators can be **lazy** and don't need to generate all of the values upfront. This code, for example, does not actually generate the numbers `1-100`, and just creates a value that represents the sequence: @@ -4427,7 +4427,7 @@ let nums = range(1i, 100i); ``` Since we didn't do anything with the range, it didn't generate the sequence. -Once we add the consumer: +Let's add the consumer: ```{rust} let nums = range(1i, 100i).collect::>(); @@ -4456,8 +4456,8 @@ std::iter::count(1i, 5i); ``` This iterator counts up from one, adding five each time. It will give -you a new integer every time, forever. Well, technically, until the -maximum number that an `int` can represent. But since iterators are lazy, +you a new integer every time, forever (well, technically, until it reaches the +maximum number representable by an `int`). But since iterators are lazy, that's okay! You probably don't want to use `collect()` on it, though... That's enough about iterators. Iterator adapters are the last concept @@ -5199,8 +5199,8 @@ to do something that it can't currently do? You may be able to write a macro to extend Rust's capabilities. You've already used one macro extensively: `println!`. When we invoke -a Rust macro, we need to use the exclamation mark (`!`). There's two reasons -that this is true: the first is that it makes it clear when you're using a +a Rust macro, we need to use the exclamation mark (`!`). There are two reasons +why this is so: the first is that it makes it clear when you're using a macro. The second is that macros allow for flexible syntax, and so Rust must be able to tell where a macro starts and ends. The `!(...)` helps with this. @@ -5215,7 +5215,7 @@ println!("x is: {}", x); The `println!` macro does a few things: -1. It parses the string to find any `{}`s +1. It parses the string to find any `{}`s. 2. It checks that the number of `{}`s matches the number of other arguments. 3. It generates a bunch of Rust code, taking this in mind. @@ -5224,8 +5224,8 @@ Rust will generate code that takes all of the types into account. If `println!` was a function, it could still do this type checking, but it would happen at run time rather than compile time. -We can check this out using a special flag to `rustc`. This code, in a file -`print.rs`: +We can check this out using a special flag to `rustc`. Put this code in a file +called `print.rs`: ```{rust} fn main() { @@ -5234,7 +5234,7 @@ fn main() { } ``` -Can have its macros expanded like this: `rustc print.rs --pretty=expanded`, will +You can have the macros expanded like this: `rustc print.rs --pretty=expanded` – which will give us this huge result: ```{rust,ignore} @@ -5273,12 +5273,12 @@ invoke the `println_args` function with the generated arguments. This is the code that Rust actually compiles. You can see all of the extra information that's here. We get all of the type safety and options that it provides, but at compile time, and without needing to type all of this out. -This is how macros are powerful. Without them, you would need to type all of -this by hand to get a type checked `println`. +This is how macros are powerful: without them you would need to type all of +this by hand to get a type-checked `println`. For more on macros, please consult [the Macros Guide](guide-macros.html). -Macros are a very advanced and still slightly experimental feature, but don't -require a deep understanding to call, since they look just like functions. The +Macros are a very advanced and still slightly experimental feature, but they don't +require a deep understanding to be called, since they look just like functions. The Guide can help you if you want to write your own. # Unsafe @@ -5295,8 +5295,8 @@ keyword, which indicates that the function may not behave properly. Second, if you'd like to create some sort of shared-memory data structure, Rust won't allow it, because memory must be owned by a single owner. However, if -you're planning on making access to that shared memory safe, such as with a -mutex, _you_ know that it's safe, but Rust can't know. Writing an `unsafe` +you're planning on making access to that shared memory safe – such as with a +mutex – _you_ know that it's safe, but Rust can't know. Writing an `unsafe` block allows you to ask the compiler to trust you. In this case, the _internal_ implementation of the mutex is considered unsafe, but the _external_ interface we present is safe. This allows it to be effectively used in normal Rust, while From 1bfe6a2a54e47679b4aed2a745dbd70bb393121a Mon Sep 17 00:00:00 2001 From: Piotr Szotkowski Date: Tue, 28 Oct 2014 20:23:59 +0100 Subject: [PATCH 03/34] =?UTF-8?q?Guide:=20Iterators:=20=E2=80=A6are=20alwa?= =?UTF-8?q?ys=20lazy=20+=20rewrap=20as=20per=20request?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/doc/guide.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/doc/guide.md b/src/doc/guide.md index 34ec7e4eee9af..a1cb07092b281 100644 --- a/src/doc/guide.md +++ b/src/doc/guide.md @@ -4416,11 +4416,12 @@ see why consumers matter. ## Iterators -As we've said before, an iterator is something that we can call the `.next()` -method on repeatedly, and it gives us a sequence of things. Because you need -to call the method, this means that iterators can be **lazy** and don't need to generate all of the values upfront. This code, for -example, does not actually generate the numbers `1-100`, and just creates a -value that represents the sequence: +As we've said before, an iterator is something that we can call the +`.next()` method on repeatedly, and it gives us a sequence of things. +Because you need to call the method, this means that iterators +are **lazy** and don't need to generate all of the values upfront. +This code, for example, does not actually generate the numbers +`1-100`, and just creates a value that represents the sequence: ```{rust} let nums = range(1i, 100i); From 5bf9ef2122e2d9c872ea551d0561c9326940446f Mon Sep 17 00:00:00 2001 From: P1start Date: Sat, 18 Oct 2014 15:39:44 +1300 Subject: [PATCH 04/34] Convert some notes to help messages Closes #18126. --- src/doc/guide.md | 2 +- src/librustc/middle/borrowck/check_loans.rs | 2 +- .../borrowck/gather_loans/move_error.rs | 7 ++- src/librustc/middle/borrowck/mod.rs | 51 +++++++++++-------- src/librustc/middle/dependency_format.rs | 2 +- src/librustc/middle/liveness.rs | 2 +- src/librustc/middle/resolve.rs | 2 +- src/librustc/middle/typeck/astconv.rs | 11 ++-- src/librustc/middle/typeck/check/mod.rs | 18 ++++--- src/librustc/middle/typeck/check/vtable.rs | 2 +- .../middle/typeck/infer/error_reporting.rs | 27 ++++++---- src/librustc/util/ppaux.rs | 4 +- src/libsyntax/ext/bytes.rs | 4 +- src/libsyntax/feature_gate.rs | 2 +- src/libsyntax/parse/parser.rs | 19 +++++-- .../compile-fail/borrowck-let-suggestion.rs | 2 +- src/test/compile-fail/issue-11714.rs | 2 +- src/test/compile-fail/issue-13428.rs | 4 +- src/test/compile-fail/issue-16747.rs | 5 +- src/test/compile-fail/issue-17441.rs | 8 +-- .../compile-fail/issue-17718-const-mut.rs | 3 +- src/test/compile-fail/issue-2354.rs | 2 +- src/test/compile-fail/issue-6702.rs | 2 +- ...-return-type-requires-explicit-lifetime.rs | 12 +++-- .../liveness-return-last-stmt-semi.rs | 4 +- src/test/compile-fail/method-missing-call.rs | 4 +- 26 files changed, 123 insertions(+), 80 deletions(-) diff --git a/src/doc/guide.md b/src/doc/guide.md index f6aa04df1b9e0..f1e01ce6660bc 100644 --- a/src/doc/guide.md +++ b/src/doc/guide.md @@ -777,7 +777,7 @@ fn add_one(x: int) -> int { x + 1; } -note: consider removing this semicolon: +help: consider removing this semicolon: x + 1; ^ ``` diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs index de61f4f2b404c..073b6dae0c385 100644 --- a/src/librustc/middle/borrowck/check_loans.rs +++ b/src/librustc/middle/borrowck/check_loans.rs @@ -789,7 +789,7 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { assignment_span, format!("cannot assign to {}", self.bccx.cmt_to_string(&*assignee_cmt)).as_slice()); - self.bccx.span_note( + self.bccx.span_help( self.tcx().map.span(upvar_id.closure_expr_id), "consider changing this closure to take self by mutable reference"); } else { diff --git a/src/librustc/middle/borrowck/gather_loans/move_error.rs b/src/librustc/middle/borrowck/gather_loans/move_error.rs index 9d4d9fcf9a9d7..ab6ff08c9d4e8 100644 --- a/src/librustc/middle/borrowck/gather_loans/move_error.rs +++ b/src/librustc/middle/borrowck/gather_loans/move_error.rs @@ -148,9 +148,12 @@ fn note_move_destination(bccx: &BorrowckCtxt, if is_first_note { bccx.span_note( move_to_span, - format!("attempting to move value to here (to prevent the move, \ + "attempting to move value to here"); + bccx.span_help( + move_to_span, + format!("to prevent the move, \ use `ref {0}` or `ref mut {0}` to capture value by \ - reference)", + reference", pat_name).as_slice()); } else { bccx.span_note(move_to_span, diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index ae8e975e843b0..4e2b280eba686 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -527,8 +527,8 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { r).as_slice()) } }; - let suggestion = move_suggestion(self.tcx, expr_ty, - "moved by default (use `copy` to override)"); + let (suggestion, _) = move_suggestion(self.tcx, expr_ty, + ("moved by default", "")); self.tcx.sess.span_note( expr_span, format!("`{}` moved here{} because it has type `{}`, which is {}", @@ -540,13 +540,15 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { move_data::MovePat => { let pat_ty = ty::node_id_to_type(self.tcx, the_move.id); - self.tcx.sess.span_note(self.tcx.map.span(the_move.id), + let span = self.tcx.map.span(the_move.id); + self.tcx.sess.span_note(span, format!("`{}` moved here{} because it has type `{}`, \ - which is moved by default (use `ref` to \ - override)", + which is moved by default", ol, moved_lp_msg, pat_ty.user_string(self.tcx)).as_slice()); + self.tcx.sess.span_help(span, + "use `ref` to override"); } move_data::Captured => { @@ -563,9 +565,9 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { r).as_slice()) } }; - let suggestion = move_suggestion(self.tcx, expr_ty, - "moved by default (make a copy and \ - capture that instead to override)"); + let (suggestion, help) = move_suggestion(self.tcx, expr_ty, + ("moved by default", "make a copy and \ + capture that instead to override")); self.tcx.sess.span_note( expr_span, format!("`{}` moved into closure environment here{} because it \ @@ -574,21 +576,23 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { moved_lp_msg, expr_ty.user_string(self.tcx), suggestion).as_slice()); + self.tcx.sess.span_help(expr_span, help); } } - fn move_suggestion(tcx: &ty::ctxt, ty: ty::t, default_msg: &'static str) - -> &'static str { + fn move_suggestion(tcx: &ty::ctxt, ty: ty::t, default_msgs: (&'static str, &'static str)) + -> (&'static str, &'static str) { match ty::get(ty).sty { ty::ty_closure(box ty::ClosureTy { store: ty::RegionTraitStore(..), .. }) => - "a non-copyable stack closure (capture it in a new closure, \ - e.g. `|x| f(x)`, to override)", + ("a non-copyable stack closure", + "capture it in a new closure, e.g. `|x| f(x)`, to override"), _ if ty::type_moves_by_default(tcx, ty) => - "non-copyable (perhaps you meant to use clone()?)", - _ => default_msg, + ("non-copyable", + "perhaps you meant to use `clone()`?"), + _ => default_msgs, } } } @@ -733,7 +737,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { self.tcx.sess.span_err(span, format!("{} in a captured outer \ variable in an `Fn` closure", prefix).as_slice()); - span_note!(self.tcx.sess, self.tcx.map.span(id), + span_help!(self.tcx.sess, self.tcx.map.span(id), "consider changing this closure to take self by mutable reference"); } mc::AliasableStatic(..) | @@ -750,7 +754,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { } if is_closure { - self.tcx.sess.span_note( + self.tcx.sess.span_help( span, "closures behind references must be called via `&mut`"); } @@ -770,7 +774,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { _ => unreachable!() }; if kind == ty::FnUnboxedClosureKind { - self.tcx.sess.span_note( + self.tcx.sess.span_help( self.tcx.map.span(upvar_id.closure_expr_id), "consider changing this closure to take \ self by mutable reference"); @@ -787,15 +791,20 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { sub_scope, "..."); let suggestion = if is_statement_scope(self.tcx, super_scope) { - "; consider using a `let` binding to increase its lifetime" + Some("consider using a `let` binding to increase its lifetime") } else { - "" + None }; - note_and_explain_region( + let span = note_and_explain_region( self.tcx, "...but borrowed value is only valid for ", super_scope, - suggestion); + ""); + match (span, suggestion) { + (_, None) => {}, + (Some(span), Some(msg)) => self.tcx.sess.span_help(span, msg), + (None, Some(msg)) => self.tcx.sess.help(msg), + } } err_borrowed_pointer_too_short(loan_scope, ptr_scope) => { diff --git a/src/librustc/middle/dependency_format.rs b/src/librustc/middle/dependency_format.rs index 3baa8eb0cc040..15ca00f6a7fe9 100644 --- a/src/librustc/middle/dependency_format.rs +++ b/src/librustc/middle/dependency_format.rs @@ -213,7 +213,7 @@ fn add_library(sess: &session::Session, sess.err(format!("cannot satisfy dependencies so `{}` only \ shows up once", data.name).as_slice()); - sess.note("having upstream crates all available in one format \ + sess.help("having upstream crates all available in one format \ will likely make this go away"); } } diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 80eba56ea6ce0..a11d5f6604ede 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -1568,7 +1568,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { hi: original_span.hi, expn_id: original_span.expn_id }; - self.ir.tcx.sess.span_note( + self.ir.tcx.sess.span_help( span_semicolon, "consider removing this semicolon:"); } } diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 3d10289227745..9a059e5d90501 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -5744,7 +5744,7 @@ impl<'a> Resolver<'a> { uses it like a function name", wrong_name).as_slice()); - self.session.span_note(expr.span, + self.session.span_help(expr.span, format!("Did you mean to write: \ `{} {{ /* fields */ }}`?", wrong_name).as_slice()); diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index 66d4f73eacc87..51a63a4f8b511 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -172,18 +172,18 @@ pub fn opt_ast_region_to_region<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( } } if len == 1 { - span_note!(this.tcx().sess, default_span, + span_help!(this.tcx().sess, default_span, "this function's return type contains a borrowed value, but \ the signature does not say which {} it is borrowed from", m); } else if len == 0 { - span_note!(this.tcx().sess, default_span, + span_help!(this.tcx().sess, default_span, "this function's return type contains a borrowed value, but \ there is no value for it to be borrowed from"); - span_note!(this.tcx().sess, default_span, + span_help!(this.tcx().sess, default_span, "consider giving it a 'static lifetime"); } else { - span_note!(this.tcx().sess, default_span, + span_help!(this.tcx().sess, default_span, "this function's return type contains a borrowed value, but \ the signature does not say whether it is borrowed from {}", m); @@ -302,7 +302,7 @@ fn ast_path_substs<'tcx,AC,RS>( && !this.tcx().sess.features.borrow().default_type_params { span_err!(this.tcx().sess, path.span, E0108, "default type parameters are experimental and possibly buggy"); - span_note!(this.tcx().sess, path.span, + span_help!(this.tcx().sess, path.span, "add #![feature(default_type_params)] to the crate attributes to enable"); } @@ -1168,6 +1168,7 @@ fn ty_of_method_or_bare_fn<'tcx, AC: AstConv<'tcx>>( let param_lifetimes: Vec<(String, uint)> = lifetimes_for_params.into_iter() .map(|(n, v)| (n, v.len())) + .filter(|&(_, l)| l != 0) .collect(); let output_ty = match decl.output.node { diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index f334d8016927d..e2d66fa580fe5 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -1355,18 +1355,18 @@ fn check_cast(fcx: &FnCtxt, ast::MutImmutable => "" }; if ty::type_is_trait(t_1) { - span_note!(fcx.tcx().sess, t.span, "did you mean `&{}{}`?", mtstr, tstr); + span_help!(fcx.tcx().sess, t.span, "did you mean `&{}{}`?", mtstr, tstr); } else { - span_note!(fcx.tcx().sess, span, + span_help!(fcx.tcx().sess, span, "consider using an implicit coercion to `&{}{}` instead", mtstr, tstr); } } ty::ty_uniq(..) => { - span_note!(fcx.tcx().sess, t.span, "did you mean `Box<{}>`?", tstr); + span_help!(fcx.tcx().sess, t.span, "did you mean `Box<{}>`?", tstr); } _ => { - span_note!(fcx.tcx().sess, e.span, + span_help!(fcx.tcx().sess, e.span, "consider using a box or reference as appropriate"); } } @@ -2142,7 +2142,7 @@ fn try_overloaded_call<'a>(fcx: &FnCtxt, if !fcx.tcx().sess.features.borrow().overloaded_calls { span_err!(fcx.tcx().sess, call_expression.span, E0056, "overloaded calls are experimental"); - span_note!(fcx.tcx().sess, call_expression.span, + span_help!(fcx.tcx().sess, call_expression.span, "add `#![feature(overloaded_calls)]` to \ the crate attributes to enable"); } @@ -3479,8 +3479,9 @@ fn check_expr_with_unifier(fcx: &FnCtxt, }, expr_t, None); - tcx.sess.span_note(field.span, - "maybe a missing `()` to call it? If not, try an anonymous function."); + tcx.sess.span_help(field.span, + "maybe a `()` to call it is missing? \ + If not, try an anonymous function"); } Err(_) => { @@ -4787,7 +4788,8 @@ pub fn check_instantiable(tcx: &ty::ctxt, if !ty::is_instantiable(tcx, item_ty) { span_err!(tcx.sess, sp, E0073, "this type cannot be instantiated without an \ - instance of itself; consider using `Option<{}>`", + instance of itself"); + span_help!(tcx.sess, sp, "consider using `Option<{}>`", ppaux::ty_to_string(tcx, item_ty)); false } else { diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs index a17194f008565..48bc3faadcd29 100644 --- a/src/librustc/middle/typeck/check/vtable.rs +++ b/src/librustc/middle/typeck/check/vtable.rs @@ -521,7 +521,7 @@ fn note_obligation_cause(fcx: &FnCtxt, span_note!(tcx.sess, obligation.cause.span, "cannot implement a destructor on a \ structure or enumeration that does not satisfy Send"); - span_note!(tcx.sess, obligation.cause.span, + span_help!(tcx.sess, obligation.cause.span, "use \"#[unsafe_destructor]\" on the implementation \ to force the compiler to allow this"); } diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs index 3baa9a7a5f964..bfa0f94a74751 100644 --- a/src/librustc/middle/typeck/infer/error_reporting.rs +++ b/src/librustc/middle/typeck/infer/error_reporting.rs @@ -438,9 +438,12 @@ impl<'a, 'tcx> ErrorReporting for InferCtxt<'a, 'tcx> { self.tcx.sess.span_err( origin.span(), format!( - "the parameter type `{}` may not live long enough; \ - consider adding an explicit lifetime bound `{}:{}`...", - param_ty.user_string(self.tcx), + "the parameter type `{}` may not live long enough", + param_ty.user_string(self.tcx)).as_slice()); + self.tcx.sess.span_help( + origin.span(), + format!( + "consider adding an explicit lifetime bound `{}: {}`...", param_ty.user_string(self.tcx), sub.user_string(self.tcx)).as_slice()); } @@ -450,9 +453,12 @@ impl<'a, 'tcx> ErrorReporting for InferCtxt<'a, 'tcx> { self.tcx.sess.span_err( origin.span(), format!( - "the parameter type `{}` may not live long enough; \ - consider adding an explicit lifetime bound `{}:'static`...", - param_ty.user_string(self.tcx), + "the parameter type `{}` may not live long enough", + param_ty.user_string(self.tcx)).as_slice()); + self.tcx.sess.span_help( + origin.span(), + format!( + "consider adding an explicit lifetime bound `{}: 'static`...", param_ty.user_string(self.tcx)).as_slice()); } @@ -461,9 +467,12 @@ impl<'a, 'tcx> ErrorReporting for InferCtxt<'a, 'tcx> { self.tcx.sess.span_err( origin.span(), format!( - "the parameter type `{}` may not live long enough; \ - consider adding an explicit lifetime bound to `{}`", - param_ty.user_string(self.tcx), + "the parameter type `{}` may not live long enough", + param_ty.user_string(self.tcx)).as_slice()); + self.tcx.sess.span_help( + origin.span(), + format!( + "consider adding an explicit lifetime bound to `{}`", param_ty.user_string(self.tcx)).as_slice()); note_and_explain_region( self.tcx, diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 8befba00fd2fe..b820780dbf9b6 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -48,16 +48,18 @@ pub trait UserString { pub fn note_and_explain_region(cx: &ctxt, prefix: &str, region: ty::Region, - suffix: &str) { + suffix: &str) -> Option { match explain_region_and_span(cx, region) { (ref str, Some(span)) => { cx.sess.span_note( span, format!("{}{}{}", prefix, *str, suffix).as_slice()); + Some(span) } (ref str, None) => { cx.sess.note( format!("{}{}{}", prefix, *str, suffix).as_slice()); + None } } } diff --git a/src/libsyntax/ext/bytes.rs b/src/libsyntax/ext/bytes.rs index a93295815e0ed..2d1d13f16d06d 100644 --- a/src/libsyntax/ext/bytes.rs +++ b/src/libsyntax/ext/bytes.rs @@ -22,10 +22,10 @@ pub fn expand_syntax_ext<'cx>(cx: &'cx mut ExtCtxt, tts: &[ast::TokenTree]) -> Box { cx.span_warn(sp, "`bytes!` is deprecated, use `b\"foo\"` literals instead"); - cx.parse_sess.span_diagnostic.span_note(sp, + cx.parse_sess.span_diagnostic.span_help(sp, "see http://doc.rust-lang.org/reference.html#byte-and-byte-string-literals \ for documentation"); - cx.parse_sess.span_diagnostic.span_note(sp, + cx.parse_sess.span_diagnostic.span_help(sp, "see https://github.com/rust-lang/rust/blob/master/src/etc/2014-06-rewrite-bytes-macros.py \ for an automated migration"); diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index c1877c827a41f..ff0cc9d0e5793 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -131,7 +131,7 @@ impl<'a> Context<'a> { fn gate_feature(&self, feature: &str, span: Span, explain: &str) { if !self.has_feature(feature) { self.span_handler.span_err(span, explain); - self.span_handler.span_note(span, format!("add #![feature({})] to the \ + self.span_handler.span_help(span, format!("add #![feature({})] to the \ crate attributes to enable", feature).as_slice()); } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index aa3b9668d4632..5e18c6bae48af 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2371,10 +2371,19 @@ impl<'a> Parser<'a> { token::LitFloat(n) => { self.bump(); let last_span = self.last_span; + let fstr = n.as_str(); self.span_err(last_span, format!("unexpected token: `{}`", n.as_str()).as_slice()); - self.span_note(last_span, - "try parenthesizing the first index; e.g., `(foo.0).1`"); + if fstr.chars().all(|x| "0123456789.".contains_char(x)) { + let float = match from_str::(fstr) { + Some(f) => f, + None => continue, + }; + self.span_help(last_span, + format!("try parenthesizing the first index; e.g., `(foo.{}){}`", + float.trunc() as uint, + float.fract().to_string()[1..]).as_slice()); + } self.abort_if_errors(); } @@ -2578,7 +2587,7 @@ impl<'a> Parser<'a> { token::Eof => { let open_braces = self.open_braces.clone(); for sp in open_braces.iter() { - self.span_note(*sp, "Did you mean to close this delimiter?"); + self.span_help(*sp, "did you mean to close this delimiter?"); } // There shouldn't really be a span, but it's easier for the test runner // if we give it one @@ -5352,8 +5361,8 @@ impl<'a> Parser<'a> { self.bump(); if self.eat_keyword(keywords::Mut) { let last_span = self.last_span; - self.span_err(last_span, "const globals cannot be mutable, \ - did you mean to declare a static?"); + self.span_err(last_span, "const globals cannot be mutable"); + self.span_help(last_span, "did you mean to declare a static?"); } let (ident, item_, extra_attrs) = self.parse_item_const(None); let last_span = self.last_span; diff --git a/src/test/compile-fail/borrowck-let-suggestion.rs b/src/test/compile-fail/borrowck-let-suggestion.rs index 385111170b126..cd1101c05d15f 100644 --- a/src/test/compile-fail/borrowck-let-suggestion.rs +++ b/src/test/compile-fail/borrowck-let-suggestion.rs @@ -11,7 +11,7 @@ fn f() { let x = [1i].iter(); //~ ERROR borrowed value does not live long enough //~^^ NOTE reference must be valid for the block - //~^^ NOTE consider using a `let` binding to increase its lifetime + //~^^ HELP consider using a `let` binding to increase its lifetime } fn main() { diff --git a/src/test/compile-fail/issue-11714.rs b/src/test/compile-fail/issue-11714.rs index d57182e275b2b..ed00d4131dbad 100644 --- a/src/test/compile-fail/issue-11714.rs +++ b/src/test/compile-fail/issue-11714.rs @@ -11,7 +11,7 @@ fn blah() -> int { //~ ERROR not all control paths return a value 1i - ; //~ NOTE consider removing this semicolon: + ; //~ HELP consider removing this semicolon: } fn main() { } diff --git a/src/test/compile-fail/issue-13428.rs b/src/test/compile-fail/issue-13428.rs index de558401aa6f0..c771970650d31 100644 --- a/src/test/compile-fail/issue-13428.rs +++ b/src/test/compile-fail/issue-13428.rs @@ -15,12 +15,12 @@ fn foo() -> String { //~ ERROR not all control paths return a value "world") // Put the trailing semicolon on its own line to test that the // note message gets the offending semicolon exactly - ; //~ NOTE consider removing this semicolon + ; //~ HELP consider removing this semicolon } fn bar() -> String { //~ ERROR not all control paths return a value "foobar".to_string() - ; //~ NOTE consider removing this semicolon + ; //~ HELP consider removing this semicolon } pub fn main() {} diff --git a/src/test/compile-fail/issue-16747.rs b/src/test/compile-fail/issue-16747.rs index 134f58951bab2..22e3e9ed09e7d 100644 --- a/src/test/compile-fail/issue-16747.rs +++ b/src/test/compile-fail/issue-16747.rs @@ -15,8 +15,9 @@ trait ListItem<'a> { trait Collection { fn len(&self) -> uint; } struct List<'a, T: ListItem<'a>> { -//~^ ERROR the parameter type `T` may not live long enough; consider adding an explicit lifetime bo -//~^^ NOTE ...so that the reference type `&'a [T]` does not outlive the data it points at +//~^ ERROR the parameter type `T` may not live long enough +//~^^ HELP consider adding an explicit lifetime bound +//~^^^ NOTE ...so that the reference type `&'a [T]` does not outlive the data it points at slice: &'a [T] } diff --git a/src/test/compile-fail/issue-17441.rs b/src/test/compile-fail/issue-17441.rs index da5590971bc3c..11c815da1c7f5 100644 --- a/src/test/compile-fail/issue-17441.rs +++ b/src/test/compile-fail/issue-17441.rs @@ -11,14 +11,14 @@ fn main() { let _foo = &[1u, 2] as [uint]; //~^ ERROR cast to unsized type: `&[uint, ..2]` as `[uint]` - //~^^ NOTE consider using an implicit coercion to `&[uint]` instead + //~^^ HELP consider using an implicit coercion to `&[uint]` instead let _bar = box 1u as std::fmt::Show; //~^ ERROR cast to unsized type: `Box` as `core::fmt::Show` - //~^^ NOTE did you mean `Box`? + //~^^ HELP did you mean `Box`? let _baz = 1u as std::fmt::Show; //~^ ERROR cast to unsized type: `uint` as `core::fmt::Show` - //~^^ NOTE consider using a box or reference as appropriate + //~^^ HELP consider using a box or reference as appropriate let _quux = [1u, 2] as [uint]; //~^ ERROR cast to unsized type: `[uint, ..2]` as `[uint]` - //~^^ NOTE consider using a box or reference as appropriate + //~^^ HELP consider using a box or reference as appropriate } diff --git a/src/test/compile-fail/issue-17718-const-mut.rs b/src/test/compile-fail/issue-17718-const-mut.rs index 31a5fee2044dc..12b9cf4ba8c0c 100644 --- a/src/test/compile-fail/issue-17718-const-mut.rs +++ b/src/test/compile-fail/issue-17718-const-mut.rs @@ -9,7 +9,8 @@ // except according to those terms. const -mut //~ ERROR: const globals cannot be mutable, did you mean to declare a static? +mut //~ ERROR: const globals cannot be mutable +//~^ HELP did you mean to declare a static? FOO: uint = 3; fn main() { diff --git a/src/test/compile-fail/issue-2354.rs b/src/test/compile-fail/issue-2354.rs index 93f38a50b0582..cc219a6acb5ef 100644 --- a/src/test/compile-fail/issue-2354.rs +++ b/src/test/compile-fail/issue-2354.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn foo() { //~ NOTE Did you mean to close this delimiter? +fn foo() { //~ HELP did you mean to close this delimiter? match Some(x) { Some(y) { panic!(); } None { panic!(); } diff --git a/src/test/compile-fail/issue-6702.rs b/src/test/compile-fail/issue-6702.rs index 168aa5f9d691f..3e35e4a659d1e 100644 --- a/src/test/compile-fail/issue-6702.rs +++ b/src/test/compile-fail/issue-6702.rs @@ -15,5 +15,5 @@ struct Monster { fn main() { let _m = Monster(); //~ ERROR `Monster` is a structure name, but - //~^ NOTE Did you mean to write: `Monster { /* fields */ }`? + //~^ HELP Did you mean to write: `Monster { /* fields */ }`? } diff --git a/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs b/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs index 849f337743b77..5d96176434239 100644 --- a/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs +++ b/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs @@ -10,13 +10,13 @@ // Lifetime annotation needed because we have no arguments. fn f() -> &int { //~ ERROR missing lifetime specifier -//~^ NOTE there is no value for it to be borrowed from +//~^ HELP there is no value for it to be borrowed from panic!() } // Lifetime annotation needed because we have two by-reference parameters. fn g(_x: &int, _y: &int) -> &int { //~ ERROR missing lifetime specifier -//~^ NOTE the signature does not say whether it is borrowed from `_x` or `_y` +//~^ HELP the signature does not say whether it is borrowed from `_x` or `_y` panic!() } @@ -27,7 +27,13 @@ struct Foo<'a> { // Lifetime annotation needed because we have two lifetimes: one as a parameter // and one on the reference. fn h(_x: &Foo) -> &int { //~ ERROR missing lifetime specifier -//~^ NOTE the signature does not say which one of `_x`'s 2 elided lifetimes it is borrowed from +//~^ HELP the signature does not say which one of `_x`'s 2 elided lifetimes it is borrowed from + panic!() +} + +fn i(_x: int) -> &int { //~ ERROR missing lifetime specifier +//~^ HELP this function's return type contains a borrowed value +//~^^ HELP consider giving it a 'static lifetime panic!() } diff --git a/src/test/compile-fail/liveness-return-last-stmt-semi.rs b/src/test/compile-fail/liveness-return-last-stmt-semi.rs index 8a6585f8bbc41..f2ea2ca96a59d 100644 --- a/src/test/compile-fail/liveness-return-last-stmt-semi.rs +++ b/src/test/compile-fail/liveness-return-last-stmt-semi.rs @@ -14,12 +14,12 @@ macro_rules! test ( () => { fn foo() -> int { 1i; } } ) //~^ ERROR not all control paths return a value - //~^^ NOTE consider removing this semicolon + //~^^ HELP consider removing this semicolon fn no_return() -> int {} //~ ERROR not all control paths return a value fn bar(x: u32) -> u32 { //~ ERROR not all control paths return a value - x * 2; //~ NOTE consider removing this semicolon + x * 2; //~ HELP consider removing this semicolon } fn baz(x: u64) -> u32 { //~ ERROR not all control paths return a value diff --git a/src/test/compile-fail/method-missing-call.rs b/src/test/compile-fail/method-missing-call.rs index 1c8b7fbf85cc5..ddfa447f60e22 100644 --- a/src/test/compile-fail/method-missing-call.rs +++ b/src/test/compile-fail/method-missing-call.rs @@ -30,7 +30,7 @@ fn main() { let point: Point = Point::new(); let px: int = point .get_x;//~ ERROR attempted to take value of method `get_x` on type `Point` - //~^ NOTE maybe a missing `()` to call it? If not, try an anonymous + //~^ HELP maybe a `()` to call it is missing // Ensure the span is useful let ys = &[1i,2,3,4,5,6,7]; @@ -38,6 +38,6 @@ fn main() { .map(|x| x) .filter(|&&x| x == 1) .filter_map; //~ ERROR attempted to take value of method `filter_map` on type - //~^ NOTE maybe a missing `()` to call it? If not, try an anonymous function. + //~^ HELP maybe a `()` to call it is missing } From e49be7aae3b33ba9afa1e517022fab3558af1aba Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sun, 2 Nov 2014 22:47:19 +1100 Subject: [PATCH 05/34] rand: Add next_f64/f32 to Rng. Some random number generates output floating point numbers directly, so by providing these methods all the functionality in librand is available with high-performance for these things. An example of such an is dSFMT (Double precision SIMD-oriented Fast Mersenne Twister). The choice to use the open interval [0, 1) has backing elsewhere, e.g. GSL (GNU Scientific Library) uses this range, and dSFMT supports generating this natively (I believe the most natural range for that library is [1, 2), but that is not totally sensible from a user perspective, and would trip people up). Fixes https://github.com/rust-lang/rfcs/issues/425. --- src/librand/distributions/mod.rs | 7 ++++++ src/librand/lib.rs | 40 ++++++++++++++++++++++++++++++++ src/librand/rand_impls.rs | 23 +++++++----------- 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/src/librand/distributions/mod.rs b/src/librand/distributions/mod.rs index 06bd04814c00b..6606031989151 100644 --- a/src/librand/distributions/mod.rs +++ b/src/librand/distributions/mod.rs @@ -227,6 +227,13 @@ fn ziggurat( // creating a f64), so we might as well reuse some to save // generating a whole extra random number. (Seems to be 15% // faster.) + // + // This unfortunately misses out on the benefits of direct + // floating point generation if an RNG like dSMFT is + // used. (That is, such RNGs create floats directly, highly + // efficiently and overload next_f32/f64, so by not calling it + // this may be slower than it would be otherwise.) + // FIXME: investigate/optimise for the above. let bits: u64 = rng.gen(); let i = (bits & 0xff) as uint; let f = (bits >> 11) as f64 / SCALE; diff --git a/src/librand/lib.rs b/src/librand/lib.rs index 405b70492a3aa..3c528c564a7ae 100644 --- a/src/librand/lib.rs +++ b/src/librand/lib.rs @@ -78,6 +78,46 @@ pub trait Rng { (self.next_u32() as u64 << 32) | (self.next_u32() as u64) } + /// Return the next random f32 selected from the half-open + /// interval `[0, 1)`. + /// + /// By default this is implemented in terms of `next_u32`, but a + /// random number generator which can generate numbers satisfying + /// the requirements directly can overload this for performance. + /// It is required that the return value lies in `[0, 1)`. + /// + /// See `Closed01` for the closed interval `[0,1]`, and + /// `Open01` for the open interval `(0,1)`. + fn next_f32(&mut self) -> f32 { + const MANTISSA_BITS: uint = 24; + const IGNORED_BITS: uint = 8; + const SCALE: f32 = (1u64 << MANTISSA_BITS) as f32; + + // using any more than `MANTISSA_BITS` bits will + // cause (e.g.) 0xffff_ffff to correspond to 1 + // exactly, so we need to drop some (8 for f32, 11 + // for f64) to guarantee the open end. + (self.next_u32() >> IGNORED_BITS) as f32 / SCALE + } + + /// Return the next random f64 selected from the half-open + /// interval `[0, 1)`. + /// + /// By default this is implemented in terms of `next_u64`, but a + /// random number generator which can generate numbers satisfying + /// the requirements directly can overload this for performance. + /// It is required that the return value lies in `[0, 1)`. + /// + /// See `Closed01` for the closed interval `[0,1]`, and + /// `Open01` for the open interval `(0,1)`. + fn next_f64(&mut self) -> f64 { + const MANTISSA_BITS: uint = 53; + const IGNORED_BITS: uint = 11; + const SCALE: f64 = (1u64 << MANTISSA_BITS) as f64; + + (self.next_u64() >> IGNORED_BITS) as f64 / SCALE + } + /// Fill `dest` with random data. /// /// This has a default implementation in terms of `next_u64` and diff --git a/src/librand/rand_impls.rs b/src/librand/rand_impls.rs index 77433877ec668..96f40bcc1565e 100644 --- a/src/librand/rand_impls.rs +++ b/src/librand/rand_impls.rs @@ -96,11 +96,11 @@ impl Rand for u64 { } macro_rules! float_impls { - ($mod_name:ident, $ty:ty, $mantissa_bits:expr, $method_name:ident, $ignored_bits:expr) => { + ($mod_name:ident, $ty:ty, $mantissa_bits:expr, $method_name:ident) => { mod $mod_name { use {Rand, Rng, Open01, Closed01}; - static SCALE: $ty = (1u64 << $mantissa_bits) as $ty; + const SCALE: $ty = (1u64 << $mantissa_bits) as $ty; impl Rand for $ty { /// Generate a floating point number in the half-open @@ -110,11 +110,7 @@ macro_rules! float_impls { /// and `Open01` for the open interval `(0,1)`. #[inline] fn rand(rng: &mut R) -> $ty { - // using any more than `mantissa_bits` bits will - // cause (e.g.) 0xffff_ffff to correspond to 1 - // exactly, so we need to drop some (8 for f32, 11 - // for f64) to guarantee the open end. - (rng.$method_name() >> $ignored_bits) as $ty / SCALE + rng.$method_name() } } impl Rand for Open01<$ty> { @@ -124,23 +120,22 @@ macro_rules! float_impls { // the precision of f64/f32 at 1.0), so that small // numbers are larger than 0, but large numbers // aren't pushed to/above 1. - Open01(((rng.$method_name() >> $ignored_bits) as $ty + 0.25) / SCALE) + Open01(rng.$method_name() + 0.25 / SCALE) } } impl Rand for Closed01<$ty> { #[inline] fn rand(rng: &mut R) -> Closed01<$ty> { - // divide by the maximum value of the numerator to - // get a non-zero probability of getting exactly - // 1.0. - Closed01((rng.$method_name() >> $ignored_bits) as $ty / (SCALE - 1.0)) + // rescale so that 1.0 - epsilon becomes 1.0 + // precisely. + Closed01(rng.$method_name() * SCALE / (SCALE - 1.0)) } } } } } -float_impls! { f64_rand_impls, f64, 53, next_u64, 11 } -float_impls! { f32_rand_impls, f32, 24, next_u32, 8 } +float_impls! { f64_rand_impls, f64, 53, next_f64 } +float_impls! { f32_rand_impls, f32, 24, next_f32 } impl Rand for char { #[inline] From ab9a1b7d60495ba3652f770305888d6f166c39fe Mon Sep 17 00:00:00 2001 From: whataloadofwhat Date: Sun, 2 Nov 2014 13:27:15 +0000 Subject: [PATCH 06/34] Change Json methods to &str and allow Indexing Json's find, find_path, and search methods now use &str rather than &String. Json can now be indexed with &str (for Objects) and uint (for Lists). Tests updated to reflect this change. [breaking-change] --- src/libserialize/json.rs | 56 ++++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/src/libserialize/json.rs b/src/libserialize/json.rs index 84b5cdd3cc82b..31d306b01c31f 100644 --- a/src/libserialize/json.rs +++ b/src/libserialize/json.rs @@ -203,6 +203,7 @@ use std::num::{FPNaN, FPInfinite}; use std::str::ScalarValue; use std::string; use std::vec::Vec; +use std::ops; use Encodable; @@ -884,9 +885,9 @@ impl Json { /// If the Json value is an Object, returns the value associated with the provided key. /// Otherwise, returns None. - pub fn find<'a>(&'a self, key: &string::String) -> Option<&'a Json>{ + pub fn find<'a>(&'a self, key: &str) -> Option<&'a Json>{ match self { - &Object(ref map) => map.find(key), + &Object(ref map) => map.find_with(|s| key.cmp(&s.as_slice())), _ => None } } @@ -894,7 +895,7 @@ impl Json { /// Attempts to get a nested Json Object for each key in `keys`. /// If any key is found not to exist, find_path will return None. /// Otherwise, it will return the Json value associated with the final key. - pub fn find_path<'a>(&'a self, keys: &[&string::String]) -> Option<&'a Json>{ + pub fn find_path<'a>(&'a self, keys: &[&str]) -> Option<&'a Json>{ let mut target = self; for key in keys.iter() { match target.find(*key) { @@ -908,20 +909,19 @@ impl Json { /// If the Json value is an Object, performs a depth-first search until /// a value associated with the provided key is found. If no value is found /// or the Json value is not an Object, returns None. - pub fn search<'a>(&'a self, key: &string::String) -> Option<&'a Json> { + pub fn search<'a>(&'a self, key: &str) -> Option<&'a Json> { match self { &Object(ref map) => { - match map.find(key) { + match map.find_with(|s| key.cmp(&s.as_slice())) { Some(json_value) => Some(json_value), None => { - let mut value : Option<&'a Json> = None; for (_, v) in map.iter() { - value = v.search(key); - if value.is_some() { - break; + match v.search(key) { + x if x.is_some() => return x, + _ => () } } - value + None } } }, @@ -1063,6 +1063,21 @@ impl Json { } } +impl<'a> ops::Index<&'a str, Json> for Json { + fn index<'a>(&'a self, idx: & &str) -> &'a Json { + self.find(*idx).unwrap() + } +} + +impl ops::Index for Json { + fn index<'a>(&'a self, idx: &uint) -> &'a Json { + match self { + &List(ref v) => v.index(idx), + _ => panic!("can only index Json with uint if it is a list") + } + } +} + /// The output of the streaming parser. #[deriving(PartialEq, Clone, Show)] pub enum JsonEvent { @@ -3055,26 +3070,33 @@ mod tests { #[test] fn test_find(){ let json_value = from_str("{\"dog\" : \"cat\"}").unwrap(); - let found_str = json_value.find(&"dog".to_string()); - assert!(found_str.is_some() && found_str.unwrap().as_string().unwrap() == "cat"); + let found_str = json_value.find("dog"); + assert!(found_str.unwrap().as_string().unwrap() == "cat"); } #[test] fn test_find_path(){ let json_value = from_str("{\"dog\":{\"cat\": {\"mouse\" : \"cheese\"}}}").unwrap(); - let found_str = json_value.find_path(&[&"dog".to_string(), - &"cat".to_string(), &"mouse".to_string()]); - assert!(found_str.is_some() && found_str.unwrap().as_string().unwrap() == "cheese"); + let found_str = json_value.find_path(&["dog", "cat", "mouse"]); + assert!(found_str.unwrap().as_string().unwrap() == "cheese"); } #[test] fn test_search(){ let json_value = from_str("{\"dog\":{\"cat\": {\"mouse\" : \"cheese\"}}}").unwrap(); - let found_str = json_value.search(&"mouse".to_string()).and_then(|j| j.as_string()); - assert!(found_str.is_some()); + let found_str = json_value.search("mouse").and_then(|j| j.as_string()); assert!(found_str.unwrap() == "cheese"); } + #[test] + fn test_index(){ + let json_value = from_str("{\"animals\":[\"dog\",\"cat\",\"mouse\"]}").unwrap(); + let ref list = json_value["animals"]; + assert_eq!(list[0].as_string().unwrap(), "dog"); + assert_eq!(list[1].as_string().unwrap(), "cat"); + assert_eq!(list[2].as_string().unwrap(), "mouse"); + } + #[test] fn test_is_object(){ let json_value = from_str("{}").unwrap(); From 1571abae53d77f36352b543a595969f1ef21503b Mon Sep 17 00:00:00 2001 From: Brian Koropoff Date: Sun, 2 Nov 2014 19:26:14 -0800 Subject: [PATCH 07/34] Fix ICE when checking call overload If the overloaded method does not have a tuple or unit type as its first non-self parameter, produce a list of error types with the correct length to prevent a later index bound panic. This typically occurs due to propagation of an earlier type error or unconstrained type variable. Closes #18532 --- src/librustc/middle/typeck/check/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index f334d8016927d..60eefbcededcb 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -2536,7 +2536,7 @@ fn check_argument_types<'a>(fcx: &FnCtxt, span_err!(tcx.sess, sp, E0059, "cannot use call notation; the first type parameter \ for the function trait is neither a tuple nor unit"); - err_args(supplied_arg_count) + err_args(args.len()) } } } else if expected_arg_count == supplied_arg_count { From 7a8e73e4f53a3aef95bb4d819b0d48bff3b93f56 Mon Sep 17 00:00:00 2001 From: Brian Koropoff Date: Sun, 2 Nov 2014 19:36:15 -0800 Subject: [PATCH 08/34] Add regression test for #18532 --- src/test/compile-fail/issue-18532.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/test/compile-fail/issue-18532.rs diff --git a/src/test/compile-fail/issue-18532.rs b/src/test/compile-fail/issue-18532.rs new file mode 100644 index 0000000000000..a67f4c851bf8a --- /dev/null +++ b/src/test/compile-fail/issue-18532.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. + +// Test that overloaded call parameter checking does not ICE +// when a type error or unconstrained type variable propagates +// into it. + +#![feature(overloaded_calls)] + +fn main() { + (return)((),()); + //~^ ERROR the type of this value must be known + //~^^ ERROR the type of this value must be known + //~^^^ ERROR cannot use call notation +} From 3ceb0112ef307d4d280dda8aca4aa7d4a34eb3f6 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Mon, 3 Nov 2014 17:52:00 +1300 Subject: [PATCH 09/34] Ignore whitespace tokens when re-computing spans in save_analysis --- src/librustc/middle/save/span_utils.rs | 25 +++++++++++++------------ src/libsyntax/parse/lexer/mod.rs | 13 +++++++++++++ src/libsyntax/parse/parser.rs | 20 +++----------------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/librustc/middle/save/span_utils.rs b/src/librustc/middle/save/span_utils.rs index 93ad29cff906a..44cad45519029 100644 --- a/src/librustc/middle/save/span_utils.rs +++ b/src/librustc/middle/save/span_utils.rs @@ -92,7 +92,7 @@ impl<'a> SpanUtils<'a> { let mut toks = self.retokenise_span(span); let mut bracket_count = 0u; loop { - let ts = toks.next_token(); + let ts = toks.real_token(); if ts.tok == token::Eof { return self.make_sub_span(span, result) } @@ -115,7 +115,7 @@ impl<'a> SpanUtils<'a> { let mut toks = self.retokenise_span(span); let mut bracket_count = 0u; loop { - let ts = toks.next_token(); + let ts = toks.real_token(); if ts.tok == token::Eof { return None; } @@ -137,13 +137,13 @@ impl<'a> SpanUtils<'a> { // any brackets, or the last span. pub fn sub_span_for_meth_name(&self, span: Span) -> Option { let mut toks = self.retokenise_span(span); - let mut prev = toks.next_token(); + let mut prev = toks.real_token(); let mut result = None; let mut bracket_count = 0u; let mut last_span = None; while prev.tok != token::Eof { last_span = None; - let mut next = toks.next_token(); + let mut next = toks.real_token(); if (next.tok == token::OpenDelim(token::Paren) || next.tok == token::Lt) && @@ -156,7 +156,7 @@ impl<'a> SpanUtils<'a> { next.tok == token::ModSep { let old = prev; prev = next; - next = toks.next_token(); + next = toks.real_token(); if next.tok == token::Lt && old.tok.is_ident() { result = Some(old.sp); @@ -185,11 +185,11 @@ impl<'a> SpanUtils<'a> { // brackets, or the last span. pub fn sub_span_for_type_name(&self, span: Span) -> Option { let mut toks = self.retokenise_span(span); - let mut prev = toks.next_token(); + let mut prev = toks.real_token(); let mut result = None; let mut bracket_count = 0u; loop { - let next = toks.next_token(); + let next = toks.real_token(); if (next.tok == token::Lt || next.tok == token::Colon) && @@ -234,7 +234,7 @@ impl<'a> SpanUtils<'a> { // We keep track of how many brackets we're nested in let mut bracket_count = 0i; loop { - let ts = toks.next_token(); + let ts = toks.real_token(); if ts.tok == token::Eof { if bracket_count != 0 { let loc = self.sess.codemap().lookup_char_pos(span.lo); @@ -263,12 +263,12 @@ impl<'a> SpanUtils<'a> { pub fn sub_span_before_token(&self, span: Span, tok: Token) -> Option { let mut toks = self.retokenise_span(span); - let mut prev = toks.next_token(); + let mut prev = toks.real_token(); loop { if prev.tok == token::Eof { return None; } - let next = toks.next_token(); + let next = toks.real_token(); if next.tok == tok { return self.make_sub_span(span, Some(prev.sp)); } @@ -281,15 +281,16 @@ impl<'a> SpanUtils<'a> { keyword: keywords::Keyword) -> Option { let mut toks = self.retokenise_span(span); loop { - let ts = toks.next_token(); + let ts = toks.real_token(); if ts.tok == token::Eof { return None; } if ts.tok.is_keyword(keyword) { - let ts = toks.next_token(); + let ts = toks.real_token(); if ts.tok == token::Eof { return None } else { + println!("found keyword: {} at {}", ts, ts.sp); return self.make_sub_span(span, Some(ts.sp)); } } diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index 293b91111b5b2..1bc1d42d888dd 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -35,6 +35,19 @@ pub trait Reader { /// Report a non-fatal error with the current span. fn err(&self, &str); fn peek(&self) -> TokenAndSpan; + /// Get a token the parser cares about. + fn real_token(&mut self) -> TokenAndSpan { + let mut t = self.next_token(); + loop { + match t.tok { + token::Whitespace | token::Comment | token::Shebang(_) => { + t = self.next_token(); + }, + _ => break + } + } + t + } } #[deriving(Clone, PartialEq, Eq, Show)] diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index aa3b9668d4632..e3157d7ac755a 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -338,27 +338,13 @@ fn is_plain_ident_or_underscore(t: &token::Token) -> bool { t.is_plain_ident() || *t == token::Underscore } -/// Get a token the parser cares about -fn real_token(rdr: &mut Reader) -> TokenAndSpan { - let mut t = rdr.next_token(); - loop { - match t.tok { - token::Whitespace | token::Comment | token::Shebang(_) => { - t = rdr.next_token(); - }, - _ => break - } - } - t -} - impl<'a> Parser<'a> { pub fn new(sess: &'a ParseSess, cfg: ast::CrateConfig, mut rdr: Box) -> Parser<'a> { - let tok0 = real_token(&mut *rdr); + let tok0 = rdr.real_token(); let span = tok0.sp; let placeholder = TokenAndSpan { tok: token::Underscore, @@ -898,7 +884,7 @@ impl<'a> Parser<'a> { None }; let next = if self.buffer_start == self.buffer_end { - real_token(&mut *self.reader) + self.reader.real_token() } else { // Avoid token copies with `replace`. let buffer_start = self.buffer_start as uint; @@ -942,7 +928,7 @@ impl<'a> Parser<'a> { -> R { let dist = distance as int; while self.buffer_length() < dist { - self.buffer[self.buffer_end as uint] = real_token(&mut *self.reader); + self.buffer[self.buffer_end as uint] = self.reader.real_token(); self.buffer_end = (self.buffer_end + 1) & 3; } f(&self.buffer[((self.buffer_start + dist - 1) & 3) as uint].tok) From 2b6c2b6e348407ceaac749209e26f0dbf67227fd Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Mon, 3 Nov 2014 18:30:29 +1300 Subject: [PATCH 10/34] Dump char and byte positions in save_analysis --- src/librustc/middle/save/span_utils.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/librustc/middle/save/span_utils.rs b/src/librustc/middle/save/span_utils.rs index 44cad45519029..14d8bf0cfed5c 100644 --- a/src/librustc/middle/save/span_utils.rs +++ b/src/librustc/middle/save/span_utils.rs @@ -31,13 +31,16 @@ impl<'a> SpanUtils<'a> { pub fn extent_str(&self, span: Span) -> String { let lo_loc = self.sess.codemap().lookup_char_pos(span.lo); let hi_loc = self.sess.codemap().lookup_char_pos(span.hi); - let lo_pos = self.sess.codemap().lookup_byte_offset(span.lo).pos; - let hi_pos = self.sess.codemap().lookup_byte_offset(span.hi).pos; - - format!("file_name,{},file_line,{},file_col,{},extent_start,{},\ - file_line_end,{},file_col_end,{},extent_end,{}", - lo_loc.file.name, lo_loc.line, lo_loc.col.to_uint(), lo_pos.to_uint(), - hi_loc.line, hi_loc.col.to_uint(), hi_pos.to_uint()) + let lo_pos = self.sess.codemap().bytepos_to_file_charpos(span.lo); + let hi_pos = self.sess.codemap().bytepos_to_file_charpos(span.hi); + let lo_pos_byte = self.sess.codemap().lookup_byte_offset(span.lo).pos; + let hi_pos_byte = self.sess.codemap().lookup_byte_offset(span.hi).pos; + + format!("file_name,{},file_line,{},file_col,{},extent_start,{},extent_start_bytes,{},\ + file_line_end,{},file_col_end,{},extent_end,{},extent_end_bytes,{}", + lo_loc.file.name, + lo_loc.line, lo_loc.col.to_uint(), lo_pos.to_uint(), lo_pos_byte.to_uint(), + hi_loc.line, hi_loc.col.to_uint(), hi_pos.to_uint(), hi_pos_byte.to_uint()) } // sub_span starts at span.lo, so we need to adjust the positions etc. From 9bc4da3493db7de6ac73038e9b60309295b19cbe Mon Sep 17 00:00:00 2001 From: gamazeps Date: Mon, 3 Nov 2014 12:18:56 +0100 Subject: [PATCH 11/34] Doc: corrects obsolete pointer syntax Goes from ~ to box Closes #18551 --- src/libsyntax/ast.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index a2c859cf9fd3c..df4e51d8e9ddb 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -385,7 +385,7 @@ pub enum Pat_ { PatLit(P), PatRange(P, P), /// [a, b, ..i, y, z] is represented as: - /// PatVec(~[a, b], Some(i), ~[y, z]) + /// PatVec(box [a, b], Some(i), box [y, z]) PatVec(Vec>, Option>, Vec>), PatMac(Mac), } From 138b76b83a067284f25e1f8971600aaf49206816 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Sun, 2 Nov 2014 17:27:11 +1100 Subject: [PATCH 12/34] Separate string->integer implementation in strconv --- src/librustc/metadata/decoder.rs | 6 +- src/librustc/metadata/tydecode.rs | 5 +- src/libstd/num/f32.rs | 6 +- src/libstd/num/f64.rs | 6 +- src/libstd/num/int_macros.rs | 94 ++++++++------------- src/libstd/num/strconv.rs | 135 +++++++++++++++--------------- src/libstd/num/uint_macros.rs | 71 +++++----------- src/test/bench/shootout-pfib.rs | 4 +- 8 files changed, 138 insertions(+), 189 deletions(-) diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index 213f32a1d182c..fa8f7c9ad939c 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -36,7 +36,7 @@ use std::io::extensions::u64_from_be_bytes; use std::io; use std::collections::hashmap::HashMap; use std::rc::Rc; -use std::u64; +use std::str; use rbml::reader; use rbml; use serialize::Decodable; @@ -215,7 +215,9 @@ fn each_reexport(d: rbml::Doc, f: |rbml::Doc| -> bool) -> bool { fn variant_disr_val(d: rbml::Doc) -> Option { reader::maybe_get_doc(d, tag_disr_val).and_then(|val_doc| { - reader::with_doc_data(val_doc, |data| u64::parse_bytes(data, 10u)) + reader::with_doc_data(val_doc, |data| { + str::from_utf8(data).and_then(from_str) + }) }) } diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index a52d02ccca773..6e7a6dfa09448 100644 --- a/src/librustc/metadata/tydecode.rs +++ b/src/librustc/metadata/tydecode.rs @@ -23,7 +23,6 @@ use middle::ty; use std::rc::Rc; use std::str; use std::string::String; -use std::uint; use syntax::abi; use syntax::ast; use syntax::ast::*; @@ -615,12 +614,12 @@ pub fn parse_def_id(buf: &[u8]) -> ast::DefId { let crate_part = buf[0u..colon_idx]; let def_part = buf[colon_idx + 1u..len]; - let crate_num = match uint::parse_bytes(crate_part, 10u) { + let crate_num = match str::from_utf8(crate_part).and_then(from_str::) { Some(cn) => cn as ast::CrateNum, None => panic!("internal error: parse_def_id: crate number expected, found {}", crate_part) }; - let def_num = match uint::parse_bytes(def_part, 10u) { + let def_num = match str::from_utf8(def_part).and_then(from_str::) { Some(dn) => dn as ast::NodeId, None => panic!("internal error: parse_def_id: id expected, found {}", def_part) diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs index 0b2f17b8f93cc..87c28c9362c63 100644 --- a/src/libstd/num/f32.rs +++ b/src/libstd/num/f32.rs @@ -361,7 +361,7 @@ pub fn to_str_exp_digits(num: f32, dig: uint, upper: bool) -> String { #[inline] pub fn from_str_hex(num: &str) -> Option { strconv::from_str_common(num, 16u, true, true, true, - strconv::ExpBin, false, false) + strconv::ExpBin, false) } impl FromStr for f32 { @@ -393,7 +393,7 @@ impl FromStr for f32 { #[inline] fn from_str(val: &str) -> Option { strconv::from_str_common(val, 10u, true, true, true, - strconv::ExpDec, false, false) + strconv::ExpDec, false) } } @@ -418,7 +418,7 @@ impl num::FromStrRadix for f32 { #[inline] fn from_str_radix(val: &str, rdx: uint) -> Option { strconv::from_str_common(val, rdx, true, true, false, - strconv::ExpNone, false, false) + strconv::ExpNone, false) } } diff --git a/src/libstd/num/f64.rs b/src/libstd/num/f64.rs index 35555b140815a..907c860f6a39f 100644 --- a/src/libstd/num/f64.rs +++ b/src/libstd/num/f64.rs @@ -369,7 +369,7 @@ pub fn to_str_exp_digits(num: f64, dig: uint, upper: bool) -> String { #[inline] pub fn from_str_hex(num: &str) -> Option { strconv::from_str_common(num, 16u, true, true, true, - strconv::ExpBin, false, false) + strconv::ExpBin, false) } impl FromStr for f64 { @@ -401,7 +401,7 @@ impl FromStr for f64 { #[inline] fn from_str(val: &str) -> Option { strconv::from_str_common(val, 10u, true, true, true, - strconv::ExpDec, false, false) + strconv::ExpDec, false) } } @@ -426,7 +426,7 @@ impl num::FromStrRadix for f64 { #[inline] fn from_str_radix(val: &str, rdx: uint) -> Option { strconv::from_str_common(val, rdx, true, true, false, - strconv::ExpNone, false, false) + strconv::ExpNone, false) } } diff --git a/src/libstd/num/int_macros.rs b/src/libstd/num/int_macros.rs index ca45b40e687a1..9ae146c840ae8 100644 --- a/src/libstd/num/int_macros.rs +++ b/src/libstd/num/int_macros.rs @@ -14,31 +14,11 @@ macro_rules! int_module (($T:ty) => ( -// String conversion functions and impl str -> num - -/// Parse a byte slice as a number in the given base -/// -/// Yields an `Option` because `buf` may or may not actually be parseable. -/// -/// # Examples -/// -/// ``` -/// let num = std::i64::parse_bytes([49,50,51,52,53,54,55,56,57], 10); -/// assert!(num == Some(123456789)); -/// ``` -#[inline] -#[experimental = "might need to return Result"] -pub fn parse_bytes(buf: &[u8], radix: uint) -> Option<$T> { - strconv::from_str_bytes_common(buf, radix, true, false, false, - strconv::ExpNone, false, false) -} - #[experimental = "might need to return Result"] impl FromStr for $T { #[inline] fn from_str(s: &str) -> Option<$T> { - strconv::from_str_common(s, 10u, true, false, false, - strconv::ExpNone, false, false) + strconv::from_str_radix_int(s, 10) } } @@ -46,18 +26,14 @@ impl FromStr for $T { impl FromStrRadix for $T { #[inline] fn from_str_radix(s: &str, radix: uint) -> Option<$T> { - strconv::from_str_common(s, radix, true, false, false, - strconv::ExpNone, false, false) + strconv::from_str_radix_int(s, radix) } } #[cfg(test)] mod tests { use prelude::*; - use super::*; - - use i32; - use str::StrSlice; + use num::FromStrRadix; #[test] fn test_from_str() { @@ -73,33 +49,33 @@ mod tests { assert_eq!(from_str::("-123456789"), Some(-123456789 as i32)); assert_eq!(from_str::<$T>("-00100"), Some(-100 as $T)); - assert!(from_str::<$T>(" ").is_none()); - assert!(from_str::<$T>("x").is_none()); + assert_eq!(from_str::<$T>(""), None); + assert_eq!(from_str::<$T>(" "), None); + assert_eq!(from_str::<$T>("x"), None); } #[test] - fn test_parse_bytes() { - use str::StrSlice; - assert_eq!(parse_bytes("123".as_bytes(), 10u), Some(123 as $T)); - assert_eq!(parse_bytes("1001".as_bytes(), 2u), Some(9 as $T)); - assert_eq!(parse_bytes("123".as_bytes(), 8u), Some(83 as $T)); - assert_eq!(i32::parse_bytes("123".as_bytes(), 16u), Some(291 as i32)); - assert_eq!(i32::parse_bytes("ffff".as_bytes(), 16u), Some(65535 as i32)); - assert_eq!(i32::parse_bytes("FFFF".as_bytes(), 16u), Some(65535 as i32)); - assert_eq!(parse_bytes("z".as_bytes(), 36u), Some(35 as $T)); - assert_eq!(parse_bytes("Z".as_bytes(), 36u), Some(35 as $T)); - - assert_eq!(parse_bytes("-123".as_bytes(), 10u), Some(-123 as $T)); - assert_eq!(parse_bytes("-1001".as_bytes(), 2u), Some(-9 as $T)); - assert_eq!(parse_bytes("-123".as_bytes(), 8u), Some(-83 as $T)); - assert_eq!(i32::parse_bytes("-123".as_bytes(), 16u), Some(-291 as i32)); - assert_eq!(i32::parse_bytes("-ffff".as_bytes(), 16u), Some(-65535 as i32)); - assert_eq!(i32::parse_bytes("-FFFF".as_bytes(), 16u), Some(-65535 as i32)); - assert_eq!(parse_bytes("-z".as_bytes(), 36u), Some(-35 as $T)); - assert_eq!(parse_bytes("-Z".as_bytes(), 36u), Some(-35 as $T)); - - assert!(parse_bytes("Z".as_bytes(), 35u).is_none()); - assert!(parse_bytes("-9".as_bytes(), 2u).is_none()); + fn test_from_str_radix() { + assert_eq!(FromStrRadix::from_str_radix("123", 10), Some(123 as $T)); + assert_eq!(FromStrRadix::from_str_radix("1001", 2), Some(9 as $T)); + assert_eq!(FromStrRadix::from_str_radix("123", 8), Some(83 as $T)); + assert_eq!(FromStrRadix::from_str_radix("123", 16), Some(291 as i32)); + assert_eq!(FromStrRadix::from_str_radix("ffff", 16), Some(65535 as i32)); + assert_eq!(FromStrRadix::from_str_radix("FFFF", 16), Some(65535 as i32)); + assert_eq!(FromStrRadix::from_str_radix("z", 36), Some(35 as $T)); + assert_eq!(FromStrRadix::from_str_radix("Z", 36), Some(35 as $T)); + + assert_eq!(FromStrRadix::from_str_radix("-123", 10), Some(-123 as $T)); + assert_eq!(FromStrRadix::from_str_radix("-1001", 2), Some(-9 as $T)); + assert_eq!(FromStrRadix::from_str_radix("-123", 8), Some(-83 as $T)); + assert_eq!(FromStrRadix::from_str_radix("-123", 16), Some(-291 as i32)); + assert_eq!(FromStrRadix::from_str_radix("-ffff", 16), Some(-65535 as i32)); + assert_eq!(FromStrRadix::from_str_radix("-FFFF", 16), Some(-65535 as i32)); + assert_eq!(FromStrRadix::from_str_radix("-z", 36), Some(-35 as $T)); + assert_eq!(FromStrRadix::from_str_radix("-Z", 36), Some(-35 as $T)); + + assert_eq!(FromStrRadix::from_str_radix("Z", 35), None::<$T>); + assert_eq!(FromStrRadix::from_str_radix("-9", 2), None::<$T>); } #[test] @@ -133,35 +109,35 @@ mod tests { fn test_int_from_str_overflow() { let mut i8_val: i8 = 127_i8; assert_eq!(from_str::("127"), Some(i8_val)); - assert!(from_str::("128").is_none()); + assert_eq!(from_str::("128"), None); i8_val += 1 as i8; assert_eq!(from_str::("-128"), Some(i8_val)); - assert!(from_str::("-129").is_none()); + assert_eq!(from_str::("-129"), None); let mut i16_val: i16 = 32_767_i16; assert_eq!(from_str::("32767"), Some(i16_val)); - assert!(from_str::("32768").is_none()); + assert_eq!(from_str::("32768"), None); i16_val += 1 as i16; assert_eq!(from_str::("-32768"), Some(i16_val)); - assert!(from_str::("-32769").is_none()); + assert_eq!(from_str::("-32769"), None); let mut i32_val: i32 = 2_147_483_647_i32; assert_eq!(from_str::("2147483647"), Some(i32_val)); - assert!(from_str::("2147483648").is_none()); + assert_eq!(from_str::("2147483648"), None); i32_val += 1 as i32; assert_eq!(from_str::("-2147483648"), Some(i32_val)); - assert!(from_str::("-2147483649").is_none()); + assert_eq!(from_str::("-2147483649"), None); let mut i64_val: i64 = 9_223_372_036_854_775_807_i64; assert_eq!(from_str::("9223372036854775807"), Some(i64_val)); - assert!(from_str::("9223372036854775808").is_none()); + assert_eq!(from_str::("9223372036854775808"), None); i64_val += 1 as i64; assert_eq!(from_str::("-9223372036854775808"), Some(i64_val)); - assert!(from_str::("-9223372036854775809").is_none()); + assert_eq!(from_str::("-9223372036854775809"), None); } } diff --git a/src/libstd/num/strconv.rs b/src/libstd/num/strconv.rs index 30ecf2284df76..1e70a0e571cf9 100644 --- a/src/libstd/num/strconv.rs +++ b/src/libstd/num/strconv.rs @@ -13,15 +13,18 @@ #![allow(missing_docs)] use char; +use char::Char; use clone::Clone; -use num::{NumCast, Zero, One, cast, Int}; +use from_str::from_str; +use iter::Iterator; +use num::{NumCast, Zero, One, cast, Int, Bounded}; use num::{Float, FPNaN, FPInfinite, ToPrimitive}; use num; use ops::{Add, Sub, Mul, Div, Rem, Neg}; use option::{None, Option, Some}; use slice::{ImmutableSlice, MutableSlice, CloneableVector}; use std::cmp::{PartialOrd, PartialEq}; -use str::StrSlice; +use str::{Str, StrSlice}; use string::String; use vec::Vec; @@ -106,35 +109,11 @@ macro_rules! impl_NumStrConv_Floating (($t:ty) => ( } )) -macro_rules! impl_NumStrConv_Integer (($t:ty) => ( - impl NumStrConv for $t { - #[inline] fn nan() -> Option<$t> { None } - #[inline] fn inf() -> Option<$t> { None } - #[inline] fn neg_inf() -> Option<$t> { None } - #[inline] fn neg_zero() -> Option<$t> { None } - - #[inline] fn round_to_zero(&self) -> $t { *self } - #[inline] fn fractional_part(&self) -> $t { 0 } - } -)) - // FIXME: #4955 // Replace by two generic impls for traits 'Integral' and 'Floating' impl_NumStrConv_Floating!(f32) impl_NumStrConv_Floating!(f64) -impl_NumStrConv_Integer!(int) -impl_NumStrConv_Integer!(i8) -impl_NumStrConv_Integer!(i16) -impl_NumStrConv_Integer!(i32) -impl_NumStrConv_Integer!(i64) - -impl_NumStrConv_Integer!(uint) -impl_NumStrConv_Integer!(u8) -impl_NumStrConv_Integer!(u16) -impl_NumStrConv_Integer!(u32) -impl_NumStrConv_Integer!(u64) - // Special value strings as [u8] consts. static INF_BUF: [u8, ..3] = [b'i', b'n', b'f']; @@ -526,8 +505,6 @@ static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u; * `FFp128`. The exponent string itself is always base 10. * Can conflict with `radix`, see Failure. * - `empty_zero` - Whether to accept an empty `buf` as a 0 or not. - * - `ignore_underscores` - Whether all underscores within the string should - * be ignored. * * # Return value * Returns `Some(n)` if `buf` parses to a number n without overflowing, and @@ -548,7 +525,6 @@ pub fn from_str_bytes_common+ NumStrConv+Clone>( buf: &[u8], radix: uint, negative: bool, fractional: bool, special: bool, exponent: ExponentFormat, empty_zero: bool, - ignore_underscores: bool ) -> Option { match exponent { ExpDec if radix >= DIGIT_E_RADIX // decimal exponent 'e' @@ -646,7 +622,6 @@ pub fn from_str_bytes_common+ last_accum = accum.clone(); } None => match c { - '_' if ignore_underscores => {} 'e' | 'E' | 'p' | 'P' => { exp_found = true; break; // start of exponent @@ -690,7 +665,6 @@ pub fn from_str_bytes_common+ last_accum = accum.clone(); } None => match c { - '_' if ignore_underscores => {} 'e' | 'E' | 'p' | 'P' => { exp_found = true; break; // start of exponent @@ -726,9 +700,7 @@ pub fn from_str_bytes_common+ // parse remaining bytes as decimal integer, // skipping the exponent char - let exp: Option = from_str_bytes_common( - buf[i+1..len], 10, true, false, false, ExpNone, false, - ignore_underscores); + let exp = from_str::(String::from_utf8_lossy(buf[i+1..len]).as_slice()); match exp { Some(exp_pow) => { @@ -754,11 +726,65 @@ pub fn from_str_common+Mul Sub+Neg+Add+NumStrConv+Clone>( buf: &str, radix: uint, negative: bool, fractional: bool, special: bool, exponent: ExponentFormat, empty_zero: bool, - ignore_underscores: bool ) -> Option { from_str_bytes_common(buf.as_bytes(), radix, negative, - fractional, special, exponent, empty_zero, - ignore_underscores) + fractional, special, exponent, empty_zero) +} + +pub fn from_str_radix_int(src: &str, radix: uint) -> Option { + fn cast(x: uint) -> T { + num::cast(x).unwrap() + } + + let _0: T = num::zero(); + let _1: T = num::one(); + let is_signed = _0 > Bounded::min_value(); + + let (is_negative, src) = match src.slice_shift_char() { + (Some('-'), src) if is_signed => (true, src), + (Some(_), _) => (false, src), + (None, _) => return None, + }; + + let mut xs = src.chars().map(|c| { + c.to_digit(radix).map(cast) + }); + let radix = cast(radix); + let mut result = _0; + + if is_negative { + for x in xs { + let x = match x { + Some(x) => x, + None => return None, + }; + result = match result.checked_mul(&radix) { + Some(result) => result, + None => return None, + }; + result = match result.checked_sub(&x) { + Some(result) => result, + None => return None, + }; + } + } else { + for x in xs { + let x = match x { + Some(x) => x, + None => return None, + }; + result = match result.checked_mul(&radix) { + Some(result) => result, + None => return None, + }; + result = match result.checked_add(&x) { + Some(result) => result, + None => return None, + }; + } + } + + Some(result) } #[cfg(test)] @@ -766,45 +792,18 @@ mod test { use super::*; use option::*; - #[test] - fn from_str_ignore_underscores() { - let s : Option = from_str_common("__1__", 2, false, false, false, - ExpNone, false, true); - assert_eq!(s, Some(1u8)); - - let n : Option = from_str_common("__1__", 2, false, false, false, - ExpNone, false, false); - assert_eq!(n, None); - - let f : Option = from_str_common("_1_._5_e_1_", 10, false, true, false, - ExpDec, false, true); - assert_eq!(f, Some(1.5e1f32)); - } - - #[test] - fn from_str_issue5770() { - // try to parse 0b1_1111_1111 = 511 as a u8. Caused problems - // since 255*2+1 == 255 (mod 256) so the overflow wasn't - // detected. - let n : Option = from_str_common("111111111", 2, false, false, false, - ExpNone, false, false); - assert_eq!(n, None); - } - #[test] fn from_str_issue7588() { - let u : Option = from_str_common("1000", 10, false, false, false, - ExpNone, false, false); + let u : Option = from_str_radix_int("1000", 10); assert_eq!(u, None); - let s : Option = from_str_common("80000", 10, false, false, false, - ExpNone, false, false); + let s : Option = from_str_radix_int("80000", 10); assert_eq!(s, None); let f : Option = from_str_common( "10000000000000000000000000000000000000000", 10, false, false, false, - ExpNone, false, false); + ExpNone, false); assert_eq!(f, NumStrConv::inf()) let fe : Option = from_str_common("1e40", 10, false, false, false, - ExpDec, false, false); + ExpDec, false); assert_eq!(fe, NumStrConv::inf()) } } diff --git a/src/libstd/num/uint_macros.rs b/src/libstd/num/uint_macros.rs index c69c3ffa41c0e..aa8e58bab0286 100644 --- a/src/libstd/num/uint_macros.rs +++ b/src/libstd/num/uint_macros.rs @@ -15,31 +15,11 @@ macro_rules! uint_module (($T:ty) => ( -// String conversion functions and impl str -> num - -/// Parse a byte slice as a number in the given base -/// -/// Yields an `Option` because `buf` may or may not actually be parseable. -/// -/// # Examples -/// -/// ``` -/// let num = std::uint::parse_bytes([49,50,51,52,53,54,55,56,57], 10); -/// assert!(num == Some(123456789)); -/// ``` -#[inline] -#[experimental = "might need to return Result"] -pub fn parse_bytes(buf: &[u8], radix: uint) -> Option<$T> { - strconv::from_str_bytes_common(buf, radix, false, false, false, - strconv::ExpNone, false, false) -} - #[experimental = "might need to return Result"] impl FromStr for $T { #[inline] fn from_str(s: &str) -> Option<$T> { - strconv::from_str_common(s, 10u, false, false, false, - strconv::ExpNone, false, false) + strconv::from_str_radix_int(s, 10) } } @@ -47,8 +27,7 @@ impl FromStr for $T { impl FromStrRadix for $T { #[inline] fn from_str_radix(s: &str, radix: uint) -> Option<$T> { - strconv::from_str_common(s, radix, false, false, false, - strconv::ExpNone, false, false) + strconv::from_str_radix_int(s, radix) } } @@ -85,10 +64,7 @@ pub fn to_str_bytes(n: $T, radix: uint, f: |v: &[u8]| -> U) -> U { #[cfg(test)] mod tests { use prelude::*; - use super::*; - - use str::StrSlice; - use u16; + use num::FromStrRadix; #[test] pub fn test_from_str() { @@ -98,23 +74,22 @@ mod tests { assert_eq!(from_str::("123456789"), Some(123456789 as u32)); assert_eq!(from_str::<$T>("00100"), Some(100u as $T)); - assert!(from_str::<$T>("").is_none()); - assert!(from_str::<$T>(" ").is_none()); - assert!(from_str::<$T>("x").is_none()); + assert_eq!(from_str::<$T>(""), None); + assert_eq!(from_str::<$T>(" "), None); + assert_eq!(from_str::<$T>("x"), None); } #[test] pub fn test_parse_bytes() { - use str::StrSlice; - assert_eq!(parse_bytes("123".as_bytes(), 10u), Some(123u as $T)); - assert_eq!(parse_bytes("1001".as_bytes(), 2u), Some(9u as $T)); - assert_eq!(parse_bytes("123".as_bytes(), 8u), Some(83u as $T)); - assert_eq!(u16::parse_bytes("123".as_bytes(), 16u), Some(291u as u16)); - assert_eq!(u16::parse_bytes("ffff".as_bytes(), 16u), Some(65535u as u16)); - assert_eq!(parse_bytes("z".as_bytes(), 36u), Some(35u as $T)); - - assert!(parse_bytes("Z".as_bytes(), 10u).is_none()); - assert!(parse_bytes("_".as_bytes(), 2u).is_none()); + assert_eq!(FromStrRadix::from_str_radix("123", 10), Some(123u as $T)); + assert_eq!(FromStrRadix::from_str_radix("1001", 2), Some(9u as $T)); + assert_eq!(FromStrRadix::from_str_radix("123", 8), Some(83u as $T)); + assert_eq!(FromStrRadix::from_str_radix("123", 16), Some(291u as u16)); + assert_eq!(FromStrRadix::from_str_radix("ffff", 16), Some(65535u as u16)); + assert_eq!(FromStrRadix::from_str_radix("z", 36), Some(35u as $T)); + + assert_eq!(FromStrRadix::from_str_radix("Z", 10), None::<$T>); + assert_eq!(FromStrRadix::from_str_radix("_", 2), None::<$T>); } #[test] @@ -148,35 +123,35 @@ mod tests { fn test_uint_from_str_overflow() { let mut u8_val: u8 = 255_u8; assert_eq!(from_str::("255"), Some(u8_val)); - assert!(from_str::("256").is_none()); + assert_eq!(from_str::("256"), None); u8_val += 1 as u8; assert_eq!(from_str::("0"), Some(u8_val)); - assert!(from_str::("-1").is_none()); + assert_eq!(from_str::("-1"), None); let mut u16_val: u16 = 65_535_u16; assert_eq!(from_str::("65535"), Some(u16_val)); - assert!(from_str::("65536").is_none()); + assert_eq!(from_str::("65536"), None); u16_val += 1 as u16; assert_eq!(from_str::("0"), Some(u16_val)); - assert!(from_str::("-1").is_none()); + assert_eq!(from_str::("-1"), None); let mut u32_val: u32 = 4_294_967_295_u32; assert_eq!(from_str::("4294967295"), Some(u32_val)); - assert!(from_str::("4294967296").is_none()); + assert_eq!(from_str::("4294967296"), None); u32_val += 1 as u32; assert_eq!(from_str::("0"), Some(u32_val)); - assert!(from_str::("-1").is_none()); + assert_eq!(from_str::("-1"), None); let mut u64_val: u64 = 18_446_744_073_709_551_615_u64; assert_eq!(from_str::("18446744073709551615"), Some(u64_val)); - assert!(from_str::("18446744073709551616").is_none()); + assert_eq!(from_str::("18446744073709551616"), None); u64_val += 1 as u64; assert_eq!(from_str::("0"), Some(u64_val)); - assert!(from_str::("-1").is_none()); + assert_eq!(from_str::("-1"), None); } } diff --git a/src/test/bench/shootout-pfib.rs b/src/test/bench/shootout-pfib.rs index 425b2e3e7140b..7fa13d6074d43 100644 --- a/src/test/bench/shootout-pfib.rs +++ b/src/test/bench/shootout-pfib.rs @@ -24,7 +24,6 @@ extern crate time; use std::os; use std::result::{Ok, Err}; use std::task; -use std::uint; fn fib(n: int) -> int { fn pfib(tx: &Sender, n: int) { @@ -102,8 +101,7 @@ fn main() { if opts.stress { stress(2); } else { - let max = uint::parse_bytes(args[1].as_bytes(), 10u).unwrap() as - int; + let max = from_str::(args[1].as_slice()).unwrap() as int; let num_trials = 10; From 251fdc877c0b2e3fdff61cbe083d1ba513f8e11b Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Sun, 2 Nov 2014 20:00:34 +1100 Subject: [PATCH 13/34] Remove unnecessary features from strconv --- src/libstd/num/f32.rs | 9 +-- src/libstd/num/f64.rs | 9 +-- src/libstd/num/strconv.rs | 136 ++++++++------------------------------ 3 files changed, 33 insertions(+), 121 deletions(-) diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs index 87c28c9362c63..448cb2d01fe2c 100644 --- a/src/libstd/num/f32.rs +++ b/src/libstd/num/f32.rs @@ -360,8 +360,7 @@ pub fn to_str_exp_digits(num: f32, dig: uint, upper: bool) -> String { /// `Some(n)` where `n` is the floating-point number represented by `[num]`. #[inline] pub fn from_str_hex(num: &str) -> Option { - strconv::from_str_common(num, 16u, true, true, true, - strconv::ExpBin, false) + strconv::from_str_float(num, 16u, true, strconv::ExpBin) } impl FromStr for f32 { @@ -392,8 +391,7 @@ impl FromStr for f32 { /// `Some(n)` where `n` is the floating-point number represented by `num`. #[inline] fn from_str(val: &str) -> Option { - strconv::from_str_common(val, 10u, true, true, true, - strconv::ExpDec, false) + strconv::from_str_float(val, 10u, true, strconv::ExpDec) } } @@ -417,8 +415,7 @@ impl num::FromStrRadix for f32 { /// `Some(n)` where `n` is the floating-point number represented by `num`. #[inline] fn from_str_radix(val: &str, rdx: uint) -> Option { - strconv::from_str_common(val, rdx, true, true, false, - strconv::ExpNone, false) + strconv::from_str_float(val, rdx, false, strconv::ExpNone) } } diff --git a/src/libstd/num/f64.rs b/src/libstd/num/f64.rs index 907c860f6a39f..f49e14cb04bab 100644 --- a/src/libstd/num/f64.rs +++ b/src/libstd/num/f64.rs @@ -368,8 +368,7 @@ pub fn to_str_exp_digits(num: f64, dig: uint, upper: bool) -> String { /// `Some(n)` where `n` is the floating-point number represented by `[num]`. #[inline] pub fn from_str_hex(num: &str) -> Option { - strconv::from_str_common(num, 16u, true, true, true, - strconv::ExpBin, false) + strconv::from_str_float(num, 16u, true, strconv::ExpBin) } impl FromStr for f64 { @@ -400,8 +399,7 @@ impl FromStr for f64 { /// `Some(n)` where `n` is the floating-point number represented by `num`. #[inline] fn from_str(val: &str) -> Option { - strconv::from_str_common(val, 10u, true, true, true, - strconv::ExpDec, false) + strconv::from_str_float(val, 10u, true, strconv::ExpDec) } } @@ -425,8 +423,7 @@ impl num::FromStrRadix for f64 { /// `Some(n)` where `n` is the floating-point number represented by `num`. #[inline] fn from_str_radix(val: &str, rdx: uint) -> Option { - strconv::from_str_common(val, rdx, true, true, false, - strconv::ExpNone, false) + strconv::from_str_float(val, rdx, false, strconv::ExpNone) } } diff --git a/src/libstd/num/strconv.rs b/src/libstd/num/strconv.rs index 1e70a0e571cf9..07468c3702632 100644 --- a/src/libstd/num/strconv.rs +++ b/src/libstd/num/strconv.rs @@ -17,13 +17,11 @@ use char::Char; use clone::Clone; use from_str::from_str; use iter::Iterator; -use num::{NumCast, Zero, One, cast, Int, Bounded}; -use num::{Float, FPNaN, FPInfinite, ToPrimitive}; use num; -use ops::{Add, Sub, Mul, Div, Rem, Neg}; +use num::{Zero, One, cast, Int, Bounded}; +use num::{Float, FPNaN, FPInfinite, ToPrimitive}; use option::{None, Option, Some}; use slice::{ImmutableSlice, MutableSlice, CloneableVector}; -use std::cmp::{PartialOrd, PartialEq}; use str::{Str, StrSlice}; use string::String; use vec::Vec; @@ -70,51 +68,6 @@ pub enum SignFormat { SignAll, } -/// Encompasses functions used by the string converter. -pub trait NumStrConv { - /// Returns the NaN value. - fn nan() -> Option; - - /// Returns the infinite value. - fn inf() -> Option; - - /// Returns the negative infinite value. - fn neg_inf() -> Option; - - /// Returns -0.0. - fn neg_zero() -> Option; - - /// Rounds the number toward zero. - fn round_to_zero(&self) -> Self; - - /// Returns the fractional part of the number. - fn fractional_part(&self) -> Self; -} - -macro_rules! impl_NumStrConv_Floating (($t:ty) => ( - impl NumStrConv for $t { - #[inline] - fn nan() -> Option<$t> { Some( 0.0 / 0.0) } - #[inline] - fn inf() -> Option<$t> { Some( 1.0 / 0.0) } - #[inline] - fn neg_inf() -> Option<$t> { Some(-1.0 / 0.0) } - #[inline] - fn neg_zero() -> Option<$t> { Some(-0.0 ) } - - #[inline] - fn round_to_zero(&self) -> $t { self.trunc() } - #[inline] - fn fractional_part(&self) -> $t { self.fract() } - } -)) - -// FIXME: #4955 -// Replace by two generic impls for traits 'Integral' and 'Floating' -impl_NumStrConv_Floating!(f32) -impl_NumStrConv_Floating!(f64) - - // Special value strings as [u8] consts. static INF_BUF: [u8, ..3] = [b'i', b'n', b'f']; static POS_INF_BUF: [u8, ..4] = [b'+', b'i', b'n', b'f']; @@ -234,8 +187,7 @@ fn int_to_str_bytes_common(num: T, radix: uint, sign: SignFormat, f: |u8 * - Fails if `radix` > 25 and `exp_format` is `ExpBin` due to conflict * between digit and exponent sign `'p'`. */ -pub fn float_to_str_bytes_common+Neg+Rem+Mul>( +pub fn float_to_str_bytes_common( num: T, radix: uint, negative_zero: bool, sign: SignFormat, digits: SignificantDigits, exp_format: ExponentFormat, exp_upper: bool ) -> (Vec, bool) { @@ -467,8 +419,7 @@ pub fn float_to_str_bytes_common+Neg+Rem+Mul>( +pub fn float_to_str_common( num: T, radix: uint, negative_zero: bool, sign: SignFormat, digits: SignificantDigits, exp_format: ExponentFormat, exp_capital: bool ) -> (String, bool) { @@ -484,15 +435,13 @@ static DIGIT_I_RADIX: uint = ('i' as uint) - ('a' as uint) + 11u; static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u; /** - * Parses a byte slice as a number. This is meant to + * Parses a string as a number. This is meant to * be a common base implementation for all numeric string conversion * functions like `from_str()` or `from_str_radix()`. * * # Arguments - * - `buf` - The byte slice to parse. + * - `src` - The string to parse. * - `radix` - Which base to parse the number as. Accepts 2-36. - * - `negative` - Whether to accept negative numbers. - * - `fractional` - Whether to accept numbers with fractional parts. * - `special` - Whether to accept special values like `inf` * and `NaN`. Can conflict with `radix`, see Failure. * - `exponent` - Which exponent format to accept. Options are: @@ -504,7 +453,6 @@ static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u; * - `ExpBin`: Accepts numbers with a binary exponent like `42P-8` or * `FFp128`. The exponent string itself is always base 10. * Can conflict with `radix`, see Failure. - * - `empty_zero` - Whether to accept an empty `buf` as a 0 or not. * * # Return value * Returns `Some(n)` if `buf` parses to a number n without overflowing, and @@ -520,11 +468,8 @@ static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u; * - Fails if `radix` > 18 and `special == true` due to conflict * between digit and lowest first character in `inf` and `NaN`, the `'i'`. */ -pub fn from_str_bytes_common+ - Mul+Sub+Neg+Add+ - NumStrConv+Clone>( - buf: &[u8], radix: uint, negative: bool, fractional: bool, - special: bool, exponent: ExponentFormat, empty_zero: bool, +pub fn from_str_float( + src: &str, radix: uint, special: bool, exponent: ExponentFormat, ) -> Option { match exponent { ExpDec if radix >= DIGIT_E_RADIX // decimal exponent 'e' @@ -548,33 +493,25 @@ pub fn from_str_bytes_common+ let _0: T = Zero::zero(); let _1: T = One::one(); let radix_gen: T = cast(radix as int).unwrap(); + let buf = src.as_bytes(); let len = buf.len(); if len == 0 { - if empty_zero { - return Some(_0); - } else { - return None; - } + return None; } if special { if buf == INF_BUF || buf == POS_INF_BUF { - return NumStrConv::inf(); + return Some(Float::infinity()); } else if buf == NEG_INF_BUF { - if negative { - return NumStrConv::neg_inf(); - } else { - return None; - } + return Some(Float::neg_infinity()); } else if buf == NAN_BUF { - return NumStrConv::nan(); + return Some(Float::nan()); } } let (start, accum_positive) = match buf[0] as char { - '-' if !negative => return None, '-' => (1u, false), '+' => (1u, true), _ => (0u, true) @@ -606,17 +543,17 @@ pub fn from_str_bytes_common+ // Detect overflow by comparing to last value, except // if we've not seen any non-zero digits. if last_accum != _0 { - if accum_positive && accum <= last_accum { return NumStrConv::inf(); } - if !accum_positive && accum >= last_accum { return NumStrConv::neg_inf(); } + if accum_positive && accum <= last_accum { return Some(Float::infinity()); } + if !accum_positive && accum >= last_accum { return Some(Float::neg_infinity()); } // Detect overflow by reversing the shift-and-add process if accum_positive && (last_accum != ((accum - cast(digit as int).unwrap())/radix_gen.clone())) { - return NumStrConv::inf(); + return Some(Float::infinity()); } if !accum_positive && (last_accum != ((accum + cast(digit as int).unwrap())/radix_gen.clone())) { - return NumStrConv::neg_inf(); + return Some(Float::neg_infinity()); } } last_accum = accum.clone(); @@ -626,7 +563,7 @@ pub fn from_str_bytes_common+ exp_found = true; break; // start of exponent } - '.' if fractional => { + '.' => { i += 1u; // skip the '.' break; // start of fractional part } @@ -660,8 +597,8 @@ pub fn from_str_bytes_common+ } // Detect overflow by comparing to last value - if accum_positive && accum < last_accum { return NumStrConv::inf(); } - if !accum_positive && accum > last_accum { return NumStrConv::neg_inf(); } + if accum_positive && accum < last_accum { return Some(Float::infinity()); } + if !accum_positive && accum > last_accum { return Some(Float::neg_infinity()); } last_accum = accum.clone(); } None => match c { @@ -680,11 +617,7 @@ pub fn from_str_bytes_common+ // Special case: buf not empty, but does not contain any digit in front // of the exponent sign -> number is empty string if i == start { - if empty_zero { - return Some(_0); - } else { - return None; - } + return None; } let mut multiplier = _1.clone(); @@ -717,20 +650,6 @@ pub fn from_str_bytes_common+ Some(accum * multiplier) } -/** - * Parses a string as a number. This is a wrapper for - * `from_str_bytes_common()`, for details see there. - */ -#[inline] -pub fn from_str_common+Mul+ - Sub+Neg+Add+NumStrConv+Clone>( - buf: &str, radix: uint, negative: bool, fractional: bool, - special: bool, exponent: ExponentFormat, empty_zero: bool, - ) -> Option { - from_str_bytes_common(buf.as_bytes(), radix, negative, - fractional, special, exponent, empty_zero) -} - pub fn from_str_radix_int(src: &str, radix: uint) -> Option { fn cast(x: uint) -> T { num::cast(x).unwrap() @@ -791,6 +710,7 @@ pub fn from_str_radix_int(src: &str, radix: uint) -> Option { mod test { use super::*; use option::*; + use num::Float; #[test] fn from_str_issue7588() { @@ -798,13 +718,11 @@ mod test { assert_eq!(u, None); let s : Option = from_str_radix_int("80000", 10); assert_eq!(s, None); - let f : Option = from_str_common( - "10000000000000000000000000000000000000000", 10, false, false, false, - ExpNone, false); - assert_eq!(f, NumStrConv::inf()) - let fe : Option = from_str_common("1e40", 10, false, false, false, - ExpDec, false); - assert_eq!(fe, NumStrConv::inf()) + let f : Option = from_str_float( + "10000000000000000000000000000000000000000", 10, false, ExpNone); + assert_eq!(f, Some(Float::infinity())) + let fe : Option = from_str_float("1e40", 10, false, ExpDec); + assert_eq!(fe, Some(Float::infinity())) } } From 84f4b58eeb12f1fcf46c67029f09a21dd6506b22 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Sun, 2 Nov 2014 21:39:55 +1100 Subject: [PATCH 14/34] Clean up from_str_float and use iterators --- src/libstd/num/strconv.rs | 281 +++++++++++++++++--------------------- 1 file changed, 125 insertions(+), 156 deletions(-) diff --git a/src/libstd/num/strconv.rs b/src/libstd/num/strconv.rs index 07468c3702632..0e8d5bc5ba22e 100644 --- a/src/libstd/num/strconv.rs +++ b/src/libstd/num/strconv.rs @@ -14,15 +14,14 @@ use char; use char::Char; -use clone::Clone; use from_str::from_str; use iter::Iterator; use num; -use num::{Zero, One, cast, Int, Bounded}; +use num::{Int, Bounded}; use num::{Float, FPNaN, FPInfinite, ToPrimitive}; use option::{None, Option, Some}; use slice::{ImmutableSlice, MutableSlice, CloneableVector}; -use str::{Str, StrSlice}; +use str::StrSlice; use string::String; use vec::Vec; @@ -68,12 +67,6 @@ pub enum SignFormat { SignAll, } -// Special value strings as [u8] consts. -static INF_BUF: [u8, ..3] = [b'i', b'n', b'f']; -static POS_INF_BUF: [u8, ..4] = [b'+', b'i', b'n', b'f']; -static NEG_INF_BUF: [u8, ..4] = [b'-', b'i', b'n', b'f']; -static NAN_BUF: [u8, ..3] = [b'N', b'a', b'N']; - /** * Converts an integral number to its string representation as a byte vector. * This is meant to be a common base implementation for all integral string @@ -102,10 +95,10 @@ static NAN_BUF: [u8, ..3] = [b'N', b'a', b'N']; fn int_to_str_bytes_common(num: T, radix: uint, sign: SignFormat, f: |u8|) { assert!(2 <= radix && radix <= 36); - let _0: T = Zero::zero(); + let _0: T = num::zero(); let neg = num < _0; - let radix_gen: T = cast(radix).unwrap(); + let radix_gen: T = num::cast(radix).unwrap(); let mut deccum = num; // This is just for integral types, the largest of which is a u64. The @@ -202,8 +195,8 @@ pub fn float_to_str_bytes_common( _ => () } - let _0: T = Zero::zero(); - let _1: T = One::one(); + let _0: T = num::zero(); + let _1: T = num::one(); match num.classify() { FPNaN => { return (b"NaN".to_vec(), true); } @@ -224,7 +217,7 @@ pub fn float_to_str_bytes_common( let neg = num < _0 || (negative_zero && _1 / num == Float::neg_infinity()); let mut buf = Vec::new(); - let radix_gen: T = cast(radix as int).unwrap(); + let radix_gen: T = num::cast(radix as int).unwrap(); let (num, exp) = match exp_format { ExpNone => (num, 0i32), @@ -233,12 +226,12 @@ pub fn float_to_str_bytes_common( (num, 0i32) } else { let (exp, exp_base) = match exp_format { - ExpDec => (num.abs().log10().floor(), cast::(10.0f64).unwrap()), - ExpBin => (num.abs().log2().floor(), cast::(2.0f64).unwrap()), + ExpDec => (num.abs().log10().floor(), num::cast::(10.0f64).unwrap()), + ExpBin => (num.abs().log2().floor(), num::cast::(2.0f64).unwrap()), ExpNone => unreachable!() }; - (num / exp_base.powf(exp), cast::(exp).unwrap()) + (num / exp_base.powf(exp), num::cast::(exp).unwrap()) } } }; @@ -490,163 +483,139 @@ pub fn from_str_float( _ => () } - let _0: T = Zero::zero(); - let _1: T = One::one(); - let radix_gen: T = cast(radix as int).unwrap(); - let buf = src.as_bytes(); - - let len = buf.len(); - - if len == 0 { - return None; - } + let _0: T = num::zero(); + let _1: T = num::one(); + let radix_gen: T = num::cast(radix as int).unwrap(); - if special { - if buf == INF_BUF || buf == POS_INF_BUF { - return Some(Float::infinity()); - } else if buf == NEG_INF_BUF { - return Some(Float::neg_infinity()); - } else if buf == NAN_BUF { - return Some(Float::nan()); - } + match src { + "inf" => return Some(Float::infinity()), + "-inf" => return Some(Float::neg_infinity()), + "NaN" => return Some(Float::nan()), + _ => {}, } - let (start, accum_positive) = match buf[0] as char { - '-' => (1u, false), - '+' => (1u, true), - _ => (0u, true) + let (is_positive, src) = match src.slice_shift_char() { + (None, _) => return None, + (Some('-'), "") => return None, + (Some('-'), src) => (false, src), + (Some(_), _) => (true, src), }; // Initialize accumulator with signed zero for floating point parsing to // work - let mut accum = if accum_positive { _0.clone() } else { -_1 * _0}; - let mut last_accum = accum.clone(); // Necessary to detect overflow - let mut i = start; - let mut exp_found = false; + let mut accum = if is_positive { _0 } else { -_1 }; + let mut last_accum = accum; // Necessary to detect overflow + let mut cs = src.chars().enumerate(); + let mut exp = None::<(char, uint)>; // Parse integer part of number - while i < len { - let c = buf[i] as char; - - match char::to_digit(c, radix) { - Some(digit) => { - // shift accum one digit left - accum = accum * radix_gen.clone(); - - // add/subtract current digit depending on sign - if accum_positive { - accum = accum + cast(digit as int).unwrap(); - } else { - accum = accum - cast(digit as int).unwrap(); - } - - // Detect overflow by comparing to last value, except - // if we've not seen any non-zero digits. - if last_accum != _0 { - if accum_positive && accum <= last_accum { return Some(Float::infinity()); } - if !accum_positive && accum >= last_accum { return Some(Float::neg_infinity()); } - - // Detect overflow by reversing the shift-and-add process - if accum_positive && - (last_accum != ((accum - cast(digit as int).unwrap())/radix_gen.clone())) { - return Some(Float::infinity()); - } - if !accum_positive && - (last_accum != ((accum + cast(digit as int).unwrap())/radix_gen.clone())) { - return Some(Float::neg_infinity()); - } - } - last_accum = accum.clone(); - } - None => match c { - 'e' | 'E' | 'p' | 'P' => { - exp_found = true; - break; // start of exponent - } - '.' => { - i += 1u; // skip the '.' - break; // start of fractional part - } - _ => return None // invalid number - } - } - - i += 1u; - } - - // Parse fractional part of number - // Skip if already reached start of exponent - if !exp_found { - let mut power = _1.clone(); - - while i < len { - let c = buf[i] as char; - - match char::to_digit(c, radix) { + for (i, c) in cs { + match c { + 'e' | 'E' | 'p' | 'P' => { + exp = Some((c, i + 1)); + break; // start of exponent + }, + '.' => { + break; // start of fractional part + }, + c => match c.to_digit(radix) { Some(digit) => { - // Decrease power one order of magnitude - power = power / radix_gen; - - let digit_t: T = cast(digit).unwrap(); + // shift accum one digit left + accum = accum * radix_gen; // add/subtract current digit depending on sign - if accum_positive { - accum = accum + digit_t * power; + if is_positive { + accum = accum + num::cast(digit as int).unwrap(); } else { - accum = accum - digit_t * power; + accum = accum - num::cast(digit as int).unwrap(); } - // Detect overflow by comparing to last value - if accum_positive && accum < last_accum { return Some(Float::infinity()); } - if !accum_positive && accum > last_accum { return Some(Float::neg_infinity()); } - last_accum = accum.clone(); - } - None => match c { - 'e' | 'E' | 'p' | 'P' => { - exp_found = true; - break; // start of exponent + // Detect overflow by comparing to last value, except + // if we've not seen any non-zero digits. + if last_accum != _0 { + if is_positive && accum <= last_accum { return Some(Float::infinity()); } + if !is_positive && accum >= last_accum { return Some(Float::neg_infinity()); } + + // Detect overflow by reversing the shift-and-add process + if is_positive && + (last_accum != ((accum - num::cast(digit as int).unwrap()) / radix_gen)) { + return Some(Float::infinity()); + } + if !is_positive && + (last_accum != ((accum + num::cast(digit as int).unwrap()) / radix_gen)) { + return Some(Float::neg_infinity()); + } } - _ => return None // invalid number - } - } - - i += 1u; + last_accum = accum; + }, + None => { + return None; // invalid number + }, + }, } } - // Special case: buf not empty, but does not contain any digit in front - // of the exponent sign -> number is empty string - if i == start { - return None; + // Parse fractional part of number + // Skip if already reached start of exponent + if exp.is_none() { + let mut power = _1; + for (i, c) in cs { + match c { + 'e' | 'E' | 'p' | 'P' => { + exp = Some((c, i + 1)); + break; // start of exponent + }, + c => match c.to_digit(radix) { + Some(digit) => { + let digit: T = num::cast(digit).unwrap(); + + // Decrease power one order of magnitude + power = power / radix_gen; + // add/subtract current digit depending on sign + accum = if is_positive { + accum + digit * power + } else { + accum - digit * power + }; + // Detect overflow by comparing to last value + if is_positive && accum < last_accum { return Some(Float::infinity()); } + if !is_positive && accum > last_accum { return Some(Float::neg_infinity()); } + last_accum = accum; + }, + None => { + return None; // invalid number + }, + }, + } + } } - let mut multiplier = _1.clone(); - - if exp_found { - let c = buf[i] as char; - let base: T = match (c, exponent) { - // c is never _ so don't need to handle specially - ('e', ExpDec) | ('E', ExpDec) => cast(10u).unwrap(), - ('p', ExpBin) | ('P', ExpBin) => cast(2u).unwrap(), - _ => return None // char doesn't fit given exponent format - }; - - // parse remaining bytes as decimal integer, - // skipping the exponent char - let exp = from_str::(String::from_utf8_lossy(buf[i+1..len]).as_slice()); - - match exp { - Some(exp_pow) => { - multiplier = if exp_pow < 0 { + let multiplier = match exp { + None => { + _1 // no exponent + }, + Some((c, offset)) => { + let base: T = match (c, exponent) { + // c is never _ so don't need to handle specially + ('e', ExpDec) | ('E', ExpDec) => num::cast(10u).unwrap(), + ('p', ExpBin) | ('P', ExpBin) => num::cast(2u).unwrap(), + _ => return None, // char doesn't fit given exponent format + }; + // parse remaining string as decimal integer + let exp = from_str::(src[offset..]); + match exp { + Some(exp_pow) if exp_pow < 0 => { _1 / num::pow(base, (-exp_pow.to_int().unwrap()) as uint) - } else { + }, + Some(exp_pow) => { num::pow(base, exp_pow.to_int().unwrap() as uint) - } + }, + None => { + return None; // invalid exponent + }, } - None => return None // invalid exponent -> invalid number - } - } - + }, + }; Some(accum * multiplier) } @@ -659,9 +628,9 @@ pub fn from_str_radix_int(src: &str, radix: uint) -> Option { let _1: T = num::one(); let is_signed = _0 > Bounded::min_value(); - let (is_negative, src) = match src.slice_shift_char() { - (Some('-'), src) if is_signed => (true, src), - (Some(_), _) => (false, src), + let (is_positive, src) = match src.slice_shift_char() { + (Some('-'), src) if is_signed => (false, src), + (Some(_), _) => (true, src), (None, _) => return None, }; @@ -671,7 +640,7 @@ pub fn from_str_radix_int(src: &str, radix: uint) -> Option { let radix = cast(radix); let mut result = _0; - if is_negative { + if is_positive { for x in xs { let x = match x { Some(x) => x, @@ -681,7 +650,7 @@ pub fn from_str_radix_int(src: &str, radix: uint) -> Option { Some(result) => result, None => return None, }; - result = match result.checked_sub(&x) { + result = match result.checked_add(&x) { Some(result) => result, None => return None, }; @@ -696,7 +665,7 @@ pub fn from_str_radix_int(src: &str, radix: uint) -> Option { Some(result) => result, None => return None, }; - result = match result.checked_add(&x) { + result = match result.checked_sub(&x) { Some(result) => result, None => return None, }; From 01ded5898b1863e195856d2678323315ae224463 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Sun, 2 Nov 2014 22:53:48 +1100 Subject: [PATCH 15/34] Simplify float string conversion function further We now have a really simple function signature: pub fn from_str_radix_float(src: &str, radix: uint) -> Option By removing some of the arguments, we remove the possibility of some invalid states. --- src/libstd/num/f32.rs | 12 +- src/libstd/num/f64.rs | 8 +- src/libstd/num/strconv.rs | 256 ++++++++++++++++---------------------- 3 files changed, 115 insertions(+), 161 deletions(-) diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs index 448cb2d01fe2c..ebb1aee816e24 100644 --- a/src/libstd/num/f32.rs +++ b/src/libstd/num/f32.rs @@ -359,8 +359,8 @@ pub fn to_str_exp_digits(num: f32, dig: uint, upper: bool) -> String { /// `None` if the string did not represent a valid number. Otherwise, /// `Some(n)` where `n` is the floating-point number represented by `[num]`. #[inline] -pub fn from_str_hex(num: &str) -> Option { - strconv::from_str_float(num, 16u, true, strconv::ExpBin) +pub fn from_str_hex(src: &str) -> Option { + strconv::from_str_radix_float(src, 16u) } impl FromStr for f32 { @@ -390,8 +390,8 @@ impl FromStr for f32 { /// `None` if the string did not represent a valid number. Otherwise, /// `Some(n)` where `n` is the floating-point number represented by `num`. #[inline] - fn from_str(val: &str) -> Option { - strconv::from_str_float(val, 10u, true, strconv::ExpDec) + fn from_str(src: &str) -> Option { + strconv::from_str_radix_float(src, 10u) } } @@ -414,8 +414,8 @@ impl num::FromStrRadix for f32 { /// `None` if the string did not represent a valid number. Otherwise, /// `Some(n)` where `n` is the floating-point number represented by `num`. #[inline] - fn from_str_radix(val: &str, rdx: uint) -> Option { - strconv::from_str_float(val, rdx, false, strconv::ExpNone) + fn from_str_radix(src: &str, radix: uint) -> Option { + strconv::from_str_radix_float(src, radix) } } diff --git a/src/libstd/num/f64.rs b/src/libstd/num/f64.rs index f49e14cb04bab..e1a46f5710f50 100644 --- a/src/libstd/num/f64.rs +++ b/src/libstd/num/f64.rs @@ -368,7 +368,7 @@ pub fn to_str_exp_digits(num: f64, dig: uint, upper: bool) -> String { /// `Some(n)` where `n` is the floating-point number represented by `[num]`. #[inline] pub fn from_str_hex(num: &str) -> Option { - strconv::from_str_float(num, 16u, true, strconv::ExpBin) + strconv::from_str_radix_float(num, 16u) } impl FromStr for f64 { @@ -399,7 +399,7 @@ impl FromStr for f64 { /// `Some(n)` where `n` is the floating-point number represented by `num`. #[inline] fn from_str(val: &str) -> Option { - strconv::from_str_float(val, 10u, true, strconv::ExpDec) + strconv::from_str_radix_float(val, 10u) } } @@ -422,8 +422,8 @@ impl num::FromStrRadix for f64 { /// `None` if the string did not represent a valid number. Otherwise, /// `Some(n)` where `n` is the floating-point number represented by `num`. #[inline] - fn from_str_radix(val: &str, rdx: uint) -> Option { - strconv::from_str_float(val, rdx, false, strconv::ExpNone) + fn from_str_radix(val: &str, radix: uint) -> Option { + strconv::from_str_radix_float(val, radix) } } diff --git a/src/libstd/num/strconv.rs b/src/libstd/num/strconv.rs index 0e8d5bc5ba22e..7a02d8d77b0b1 100644 --- a/src/libstd/num/strconv.rs +++ b/src/libstd/num/strconv.rs @@ -424,69 +424,18 @@ pub fn float_to_str_common( // Some constants for from_str_bytes_common's input validation, // they define minimum radix values for which the character is a valid digit. static DIGIT_P_RADIX: uint = ('p' as uint) - ('a' as uint) + 11u; -static DIGIT_I_RADIX: uint = ('i' as uint) - ('a' as uint) + 11u; static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u; -/** - * Parses a string as a number. This is meant to - * be a common base implementation for all numeric string conversion - * functions like `from_str()` or `from_str_radix()`. - * - * # Arguments - * - `src` - The string to parse. - * - `radix` - Which base to parse the number as. Accepts 2-36. - * - `special` - Whether to accept special values like `inf` - * and `NaN`. Can conflict with `radix`, see Failure. - * - `exponent` - Which exponent format to accept. Options are: - * - `ExpNone`: No Exponent, accepts just plain numbers like `42` or - * `-8.2`. - * - `ExpDec`: Accepts numbers with a decimal exponent like `42e5` or - * `8.2E-2`. The exponent string itself is always base 10. - * Can conflict with `radix`, see Failure. - * - `ExpBin`: Accepts numbers with a binary exponent like `42P-8` or - * `FFp128`. The exponent string itself is always base 10. - * Can conflict with `radix`, see Failure. - * - * # Return value - * Returns `Some(n)` if `buf` parses to a number n without overflowing, and - * `None` otherwise, depending on the constraints set by the remaining - * arguments. - * - * # Failure - * - Fails if `radix` < 2 or `radix` > 36. - * - Fails if `radix` > 14 and `exponent` is `ExpDec` due to conflict - * between digit and exponent sign `'e'`. - * - Fails if `radix` > 25 and `exponent` is `ExpBin` due to conflict - * between digit and exponent sign `'p'`. - * - Fails if `radix` > 18 and `special == true` due to conflict - * between digit and lowest first character in `inf` and `NaN`, the `'i'`. - */ -pub fn from_str_float( - src: &str, radix: uint, special: bool, exponent: ExponentFormat, - ) -> Option { - match exponent { - ExpDec if radix >= DIGIT_E_RADIX // decimal exponent 'e' - => panic!("from_str_bytes_common: radix {} incompatible with \ - use of 'e' as decimal exponent", radix), - ExpBin if radix >= DIGIT_P_RADIX // binary exponent 'p' - => panic!("from_str_bytes_common: radix {} incompatible with \ - use of 'p' as binary exponent", radix), - _ if special && radix >= DIGIT_I_RADIX // first digit of 'inf' - => panic!("from_str_bytes_common: radix {} incompatible with \ - special values 'inf' and 'NaN'", radix), - _ if (radix as int) < 2 - => panic!("from_str_bytes_common: radix {} to low, \ - must lie in the range [2, 36]", radix), - _ if (radix as int) > 36 - => panic!("from_str_bytes_common: radix {} to high, \ - must lie in the range [2, 36]", radix), - _ => () - } +pub fn from_str_radix_float(src: &str, radix: uint) -> Option { + assert!(radix >= 2 && radix <= 36, + "from_str_radix_float: must lie in the range `[2, 36]` - found {}", + radix); let _0: T = num::zero(); let _1: T = num::one(); - let radix_gen: T = num::cast(radix as int).unwrap(); + let radix_t: T = num::cast(radix as int).unwrap(); + // Special values match src { "inf" => return Some(Float::infinity()), "-inf" => return Some(Float::neg_infinity()), @@ -501,88 +450,89 @@ pub fn from_str_float( (Some(_), _) => (true, src), }; - // Initialize accumulator with signed zero for floating point parsing to - // work - let mut accum = if is_positive { _0 } else { -_1 }; - let mut last_accum = accum; // Necessary to detect overflow - let mut cs = src.chars().enumerate(); - let mut exp = None::<(char, uint)>; + // The significand to accumulate + let mut sig = if is_positive { _0 } else { -_1 }; + // Necessary to detect overflow + let mut prev_sig = sig; + let mut cs = src.chars().enumerate(); + // Exponent prefix and exponent index offset + let mut exp_info = None::<(char, uint)>; - // Parse integer part of number + // Parse the integer part of the significand for (i, c) in cs { - match c { - 'e' | 'E' | 'p' | 'P' => { - exp = Some((c, i + 1)); - break; // start of exponent - }, - '.' => { - break; // start of fractional part - }, - c => match c.to_digit(radix) { - Some(digit) => { - // shift accum one digit left - accum = accum * radix_gen; - - // add/subtract current digit depending on sign - if is_positive { - accum = accum + num::cast(digit as int).unwrap(); - } else { - accum = accum - num::cast(digit as int).unwrap(); - } + match c.to_digit(radix) { + Some(digit) => { + // shift significand one digit left + sig = sig * radix_t; + + // add/subtract current digit depending on sign + if is_positive { + sig = sig + num::cast(digit as int).unwrap(); + } else { + sig = sig - num::cast(digit as int).unwrap(); + } - // Detect overflow by comparing to last value, except - // if we've not seen any non-zero digits. - if last_accum != _0 { - if is_positive && accum <= last_accum { return Some(Float::infinity()); } - if !is_positive && accum >= last_accum { return Some(Float::neg_infinity()); } - - // Detect overflow by reversing the shift-and-add process - if is_positive && - (last_accum != ((accum - num::cast(digit as int).unwrap()) / radix_gen)) { - return Some(Float::infinity()); - } - if !is_positive && - (last_accum != ((accum + num::cast(digit as int).unwrap()) / radix_gen)) { - return Some(Float::neg_infinity()); - } - } - last_accum = accum; + // Detect overflow by comparing to last value, except + // if we've not seen any non-zero digits. + if prev_sig != _0 { + if is_positive && sig <= prev_sig + { return Some(Float::infinity()); } + if !is_positive && sig >= prev_sig + { return Some(Float::neg_infinity()); } + + // Detect overflow by reversing the shift-and-add process + let digit: T = num::cast(digit as int).unwrap(); + if is_positive && (prev_sig != ((sig - digit) / radix_t)) + { return Some(Float::infinity()); } + if !is_positive && (prev_sig != ((sig + digit) / radix_t)) + { return Some(Float::neg_infinity()); } + } + prev_sig = sig; + }, + None => match c { + 'e' | 'E' | 'p' | 'P' => { + exp_info = Some((c, i + 1)); + break; // start of exponent + }, + '.' => { + break; // start of fractional part }, - None => { - return None; // invalid number + _ => { + return None; }, }, } } - // Parse fractional part of number - // Skip if already reached start of exponent - if exp.is_none() { + // If we are not yet at the exponent parse the fractional + // part of the significand + if exp_info.is_none() { let mut power = _1; for (i, c) in cs { - match c { - 'e' | 'E' | 'p' | 'P' => { - exp = Some((c, i + 1)); - break; // start of exponent + match c.to_digit(radix) { + Some(digit) => { + let digit: T = num::cast(digit).unwrap(); + // Decrease power one order of magnitude + power = power / radix_t; + // add/subtract current digit depending on sign + sig = if is_positive { + sig + digit * power + } else { + sig - digit * power + }; + // Detect overflow by comparing to last value + if is_positive && sig < prev_sig + { return Some(Float::infinity()); } + if !is_positive && sig > prev_sig + { return Some(Float::neg_infinity()); } + prev_sig = sig; }, - c => match c.to_digit(radix) { - Some(digit) => { - let digit: T = num::cast(digit).unwrap(); - - // Decrease power one order of magnitude - power = power / radix_gen; - // add/subtract current digit depending on sign - accum = if is_positive { - accum + digit * power - } else { - accum - digit * power - }; - // Detect overflow by comparing to last value - if is_positive && accum < last_accum { return Some(Float::infinity()); } - if !is_positive && accum > last_accum { return Some(Float::neg_infinity()); } - last_accum = accum; + None => match c { + 'e' | 'E' | 'p' | 'P' => { + exp_info = Some((c, i + 1)); + break; // start of exponent }, - None => { + _ => { return None; // invalid number }, }, @@ -590,36 +540,41 @@ pub fn from_str_float( } } - let multiplier = match exp { - None => { - _1 // no exponent - }, + // Parse and calculate the exponent + let exp = match exp_info { Some((c, offset)) => { - let base: T = match (c, exponent) { - // c is never _ so don't need to handle specially - ('e', ExpDec) | ('E', ExpDec) => num::cast(10u).unwrap(), - ('p', ExpBin) | ('P', ExpBin) => num::cast(2u).unwrap(), - _ => return None, // char doesn't fit given exponent format + let base: T = match c { + 'E' | 'e' if radix == 10 => num::cast(10u).unwrap(), + 'P' | 'p' if radix == 16 => num::cast(2u).unwrap(), + _ => return None, }; - // parse remaining string as decimal integer - let exp = from_str::(src[offset..]); - match exp { - Some(exp_pow) if exp_pow < 0 => { - _1 / num::pow(base, (-exp_pow.to_int().unwrap()) as uint) - }, - Some(exp_pow) => { - num::pow(base, exp_pow.to_int().unwrap() as uint) - }, - None => { - return None; // invalid exponent - }, + + // Parse the exponent as decimal integer + let src = src[offset..]; + let (is_positive, exp) = match src.slice_shift_char() { + (Some('-'), src) => (false, from_str::(src)), + (Some('+'), src) => (true, from_str::(src)), + (Some(_), _) => (true, from_str::(src)), + (None, _) => return None, + }; + + match (is_positive, exp) { + (true, Some(exp)) => num::pow(base, exp), + (false, Some(exp)) => _1 / num::pow(base, exp), + (_, None) => return None, } }, + None => _1, // no exponent }; - Some(accum * multiplier) + + Some(sig * exp) } pub fn from_str_radix_int(src: &str, radix: uint) -> Option { + assert!(radix >= 2 && radix <= 36, + "from_str_radix_int: must lie in the range `[2, 36]` - found {}", + radix); + fn cast(x: uint) -> T { num::cast(x).unwrap() } @@ -687,10 +642,9 @@ mod test { assert_eq!(u, None); let s : Option = from_str_radix_int("80000", 10); assert_eq!(s, None); - let f : Option = from_str_float( - "10000000000000000000000000000000000000000", 10, false, ExpNone); + let f : Option = from_str_radix_float("10000000000000000000000000000000000000000", 10); assert_eq!(f, Some(Float::infinity())) - let fe : Option = from_str_float("1e40", 10, false, ExpDec); + let fe : Option = from_str_radix_float("1e40", 10); assert_eq!(fe, Some(Float::infinity())) } } From 8bd37e672411bf004a4ad64bb7d2160e455a72b0 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Sun, 2 Nov 2014 22:58:46 +1100 Subject: [PATCH 16/34] Deprecate {f32, f64}::from_str_hex This is now covered by `FromStrRadix::from_str_radix` --- src/libstd/num/f32.rs | 44 +++++++----------------------- src/libstd/num/f64.rs | 63 ++++++++++++------------------------------- 2 files changed, 27 insertions(+), 80 deletions(-) diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs index ebb1aee816e24..63c3956ef2412 100644 --- a/src/libstd/num/f32.rs +++ b/src/libstd/num/f32.rs @@ -333,34 +333,10 @@ pub fn to_str_exp_digits(num: f32, dig: uint, upper: bool) -> String { r } -/// Convert a string in base 16 to a float. -/// Accepts an optional binary exponent. -/// -/// This function accepts strings such as -/// -/// * 'a4.fe' -/// * '+a4.fe', equivalent to 'a4.fe' -/// * '-a4.fe' -/// * '2b.aP128', or equivalently, '2b.ap128' -/// * '2b.aP-128' -/// * '.' (understood as 0) -/// * 'c.' -/// * '.c', or, equivalently, '0.c' -/// * '+inf', 'inf', '-inf', 'NaN' -/// -/// Leading and trailing whitespace represent an error. -/// -/// # Arguments -/// -/// * num - A string -/// -/// # Return value -/// -/// `None` if the string did not represent a valid number. Otherwise, -/// `Some(n)` where `n` is the floating-point number represented by `[num]`. #[inline] +#[deprecated="Use `FromStrRadix::from_str_radix(src, 16)`"] pub fn from_str_hex(src: &str) -> Option { - strconv::from_str_radix_float(src, 16u) + strconv::from_str_radix_float(src, 16) } impl FromStr for f32 { @@ -383,12 +359,12 @@ impl FromStr for f32 { /// /// # Arguments /// - /// * num - A string + /// * src - A string /// /// # Return value /// /// `None` if the string did not represent a valid number. Otherwise, - /// `Some(n)` where `n` is the floating-point number represented by `num`. + /// `Some(n)` where `n` is the floating-point number represented by `src`. #[inline] fn from_str(src: &str) -> Option { strconv::from_str_radix_float(src, 10u) @@ -406,13 +382,13 @@ impl num::FromStrRadix for f32 { /// /// # Arguments /// - /// * num - A string + /// * src - A string /// * radix - The base to use. Must lie in the range [2 .. 36] /// /// # Return value /// /// `None` if the string did not represent a valid number. Otherwise, - /// `Some(n)` where `n` is the floating-point number represented by `num`. + /// `Some(n)` where `n` is the floating-point number represented by `src`. #[inline] fn from_str_radix(src: &str, radix: uint) -> Option { strconv::from_str_radix_float(src, radix) @@ -707,8 +683,8 @@ mod tests { fn test_ldexp() { // We have to use from_str until base-2 exponents // are supported in floating-point literals - let f1: f32 = from_str_hex("1p-123").unwrap(); - let f2: f32 = from_str_hex("1p-111").unwrap(); + let f1: f32 = FromStrRadix::from_str_radix("1p-123", 16).unwrap(); + let f2: f32 = FromStrRadix::from_str_radix("1p-111", 16).unwrap(); assert_eq!(FloatMath::ldexp(1f32, -123), f1); assert_eq!(FloatMath::ldexp(1f32, -111), f2); @@ -727,8 +703,8 @@ mod tests { fn test_frexp() { // We have to use from_str until base-2 exponents // are supported in floating-point literals - let f1: f32 = from_str_hex("1p-123").unwrap(); - let f2: f32 = from_str_hex("1p-111").unwrap(); + let f1: f32 = FromStrRadix::from_str_radix("1p-123", 16).unwrap(); + let f2: f32 = FromStrRadix::from_str_radix("1p-111", 16).unwrap(); let (x1, exp1) = f1.frexp(); let (x2, exp2) = f2.frexp(); assert_eq!((x1, exp1), (0.5f32, -122)); diff --git a/src/libstd/num/f64.rs b/src/libstd/num/f64.rs index e1a46f5710f50..6e8e92eb91d03 100644 --- a/src/libstd/num/f64.rs +++ b/src/libstd/num/f64.rs @@ -341,89 +341,60 @@ pub fn to_str_exp_digits(num: f64, dig: uint, upper: bool) -> String { r } -/// Convert a string in base 16 to a float. -/// Accepts an optional binary exponent. -/// -/// This function accepts strings such as -/// -/// * 'a4.fe' -/// * '+a4.fe', equivalent to 'a4.fe' -/// * '-a4.fe' -/// * '2b.aP128', or equivalently, '2b.ap128' -/// * '2b.aP-128' -/// * '.' (understood as 0) -/// * 'c.' -/// * '.c', or, equivalently, '0.c' -/// * '+inf', 'inf', '-inf', 'NaN' -/// -/// Leading and trailing whitespace represent an error. -/// -/// # Arguments -/// -/// * num - A string -/// -/// # Return value -/// -/// `None` if the string did not represent a valid number. Otherwise, -/// `Some(n)` where `n` is the floating-point number represented by `[num]`. #[inline] -pub fn from_str_hex(num: &str) -> Option { - strconv::from_str_radix_float(num, 16u) +#[deprecated="Use `FromStrRadix::from_str_radix(src, 16)`"] +pub fn from_str_hex(src: &str) -> Option { + strconv::from_str_radix_float(src, 16) } impl FromStr for f64 { /// Convert a string in base 10 to a float. /// Accepts an optional decimal exponent. /// - /// This function accepts strings such as + /// This function accepts strings such as: /// /// * '3.14' - /// * '+3.14', equivalent to '3.14' /// * '-3.14' /// * '2.5E10', or equivalently, '2.5e10' /// * '2.5E-10' /// * '.' (understood as 0) /// * '5.' /// * '.5', or, equivalently, '0.5' - /// * '+inf', 'inf', '-inf', 'NaN' + /// * inf', '-inf', 'NaN' /// /// Leading and trailing whitespace represent an error. /// /// # Arguments /// - /// * num - A string + /// * src - A string /// /// # Return value /// /// `none` if the string did not represent a valid number. Otherwise, - /// `Some(n)` where `n` is the floating-point number represented by `num`. + /// `Some(n)` where `n` is the floating-point number represented by `src`. #[inline] - fn from_str(val: &str) -> Option { - strconv::from_str_radix_float(val, 10u) + fn from_str(src: &str) -> Option { + strconv::from_str_radix_float(src, 10u) } } impl num::FromStrRadix for f64 { /// Convert a string in a given base to a float. /// - /// Due to possible conflicts, this function does **not** accept - /// the special values `inf`, `-inf`, `+inf` and `NaN`, **nor** - /// does it recognize exponents of any kind. - /// /// Leading and trailing whitespace represent an error. /// /// # Arguments /// - /// * num - A string + /// * src - A string /// * radix - The base to use. Must lie in the range [2 .. 36] /// /// # Return value /// /// `None` if the string did not represent a valid number. Otherwise, - /// `Some(n)` where `n` is the floating-point number represented by `num`. + /// `Some(n)` where `n` is the floating-point number represented by `src`. #[inline] - fn from_str_radix(val: &str, radix: uint) -> Option { - strconv::from_str_radix_float(val, radix) + fn from_str_radix(src: &str, radix: uint) -> Option { + strconv::from_str_radix_float(src, radix) } } @@ -709,8 +680,8 @@ mod tests { fn test_ldexp() { // We have to use from_str until base-2 exponents // are supported in floating-point literals - let f1: f64 = from_str_hex("1p-123").unwrap(); - let f2: f64 = from_str_hex("1p-111").unwrap(); + let f1: f64 = FromStrRadix::from_str_radix("1p-123", 16).unwrap(); + let f2: f64 = FromStrRadix::from_str_radix("1p-111", 16).unwrap(); assert_eq!(FloatMath::ldexp(1f64, -123), f1); assert_eq!(FloatMath::ldexp(1f64, -111), f2); @@ -729,8 +700,8 @@ mod tests { fn test_frexp() { // We have to use from_str until base-2 exponents // are supported in floating-point literals - let f1: f64 = from_str_hex("1p-123").unwrap(); - let f2: f64 = from_str_hex("1p-111").unwrap(); + let f1: f64 = FromStrRadix::from_str_radix("1p-123", 16).unwrap(); + let f2: f64 = FromStrRadix::from_str_radix("1p-111", 16).unwrap(); let (x1, exp1) = f1.frexp(); let (x2, exp2) = f2.frexp(); assert_eq!((x1, exp1), (0.5f64, -122)); From e9618ce70176f9fa4bde73dd947283bf07ab2c1f Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Mon, 3 Nov 2014 15:34:56 +0100 Subject: [PATCH 17/34] Small doc fixes --- src/doc/reference.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/reference.md b/src/doc/reference.md index 084309e9978f6..def6527a7f573 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -831,7 +831,7 @@ mod math { } ``` -Modules and types share the same namespace. Declaring a named type that has +Modules and types share the same namespace. Declaring a named type with the same name as a module in scope is forbidden: that is, a type definition, trait, struct, enumeration, or type parameter can't shadow the name of a module in scope, or vice versa. @@ -870,8 +870,8 @@ view_item : extern_crate_decl | use_decl ; ``` A view item manages the namespace of a module. View items do not define new -items, but rather, simply change other items' visibility. There are several -kinds of view item: +items, but rather, simply change other items' visibility. There are two +kinds of view items: * [`extern crate` declarations](#extern-crate-declarations) * [`use` declarations](#use-declarations) @@ -896,7 +896,7 @@ external crate when it was compiled. If no `crateid` is provided, a default `name` attribute is assumed, equal to the `ident` given in the `extern_crate_decl`. -Four examples of `extern crate` declarations: +Three examples of `extern crate` declarations: ```{.ignore} extern crate pcre; From 32b903d87768dbc9acf497b1646b20069d9bf212 Mon Sep 17 00:00:00 2001 From: Falco Hirschenberger Date: Mon, 3 Nov 2014 20:08:11 +0100 Subject: [PATCH 18/34] Also fix undefined behaviour when shift equals the number of bits LLVM states: "If op2 is (statically or dynamically) negative or equal to or larger than the number of bits in op1, the result is undefined." --- src/librustc/lint/builtin.rs | 6 +- .../compile-fail/lint-exceeding-bitshifts.rs | 74 +++++++++---------- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 76ef6206d6416..2b145192131c4 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -187,12 +187,12 @@ impl LintPass for TypeLimits { if let Some(bits) = opt_ty_bits { let exceeding = if let ast::ExprLit(ref lit) = r.node { - if let ast::LitInt(shift, _) = lit.node { shift > bits } + if let ast::LitInt(shift, _) = lit.node { shift >= bits } else { false } } else { match eval_const_expr_partial(cx.tcx, &**r) { - Ok(const_int(shift)) => { shift as u64 > bits }, - Ok(const_uint(shift)) => { shift > bits }, + Ok(const_int(shift)) => { shift as u64 >= bits }, + Ok(const_uint(shift)) => { shift >= bits }, _ => { false } } }; diff --git a/src/test/compile-fail/lint-exceeding-bitshifts.rs b/src/test/compile-fail/lint-exceeding-bitshifts.rs index f270994bd38ef..eecc216802955 100644 --- a/src/test/compile-fail/lint-exceeding-bitshifts.rs +++ b/src/test/compile-fail/lint-exceeding-bitshifts.rs @@ -12,47 +12,47 @@ #![allow(unused_variables)] fn main() { - let n = 1u8 << 8; - let n = 1u8 << 9; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1u16 << 16; - let n = 1u16 << 17; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1u32 << 32; - let n = 1u32 << 33; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1u64 << 64; - let n = 1u64 << 65; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1i8 << 8; - let n = 1i8 << 9; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1i16 << 16; - let n = 1i16 << 17; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1i32 << 32; - let n = 1i32 << 33; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1i64 << 64; - let n = 1i64 << 65; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u8 << 7; + let n = 1u8 << 8; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u16 << 15; + let n = 1u16 << 16; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u32 << 31; + let n = 1u32 << 32; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u64 << 63; + let n = 1u64 << 64; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i8 << 7; + let n = 1i8 << 8; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i16 << 15; + let n = 1i16 << 16; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i32 << 31; + let n = 1i32 << 32; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i64 << 63; + let n = 1i64 << 64; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1u8 >> 8; - let n = 1u8 >> 9; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1u16 >> 16; - let n = 1u16 >> 17; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1u32 >> 32; - let n = 1u32 >> 33; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1u64 >> 64; - let n = 1u64 >> 65; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1i8 >> 8; - let n = 1i8 >> 9; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1i16 >> 16; - let n = 1i16 >> 17; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1i32 >> 32; - let n = 1i32 >> 33; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1i64 >> 64; - let n = 1i64 >> 65; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u8 >> 7; + let n = 1u8 >> 8; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u16 >> 15; + let n = 1u16 >> 16; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u32 >> 31; + let n = 1u32 >> 32; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u64 >> 63; + let n = 1u64 >> 64; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i8 >> 7; + let n = 1i8 >> 8; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i16 >> 15; + let n = 1i16 >> 16; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i32 >> 31; + let n = 1i32 >> 32; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i64 >> 63; + let n = 1i64 >> 64; //~ ERROR: bitshift exceeds the type's number of bits let n = 1u8; - let n = n << 8; - let n = n << 9; //~ ERROR: bitshift exceeds the type's number of bits + let n = n << 7; + let n = n << 8; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1u8 << -9; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u8 << -8; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1u8 << (4+4); - let n = 1u8 << (4+5); //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u8 << (4+3); + let n = 1u8 << (4+4); //~ ERROR: bitshift exceeds the type's number of bits } From fbb90c3458e1db30fc62c96195c9e71ba2111aa4 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sat, 25 Oct 2014 20:33:54 +0300 Subject: [PATCH 19/34] Clean-up transmutes in libsyntax --- src/libsyntax/ast.rs | 2 +- src/libsyntax/parse/token.rs | 4 ++-- src/libsyntax/print/pprust.rs | 13 +++++-------- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index a2c859cf9fd3c..1edcb35289a3b 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -119,7 +119,7 @@ impl Name { pub fn as_str<'a>(&'a self) -> &'a str { unsafe { // FIXME #12938: can't use copy_lifetime since &str isn't a &T - ::std::mem::transmute(token::get_name(*self).get()) + ::std::mem::transmute::<&str,&str>(token::get_name(*self).get()) } } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index d56aa8da72a84..615cd34ca14df 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -668,12 +668,12 @@ impl InternedString { impl BytesContainer for InternedString { fn container_as_bytes<'a>(&'a self) -> &'a [u8] { - // FIXME(pcwalton): This is a workaround for the incorrect signature + // FIXME #12938: This is a workaround for the incorrect signature // of `BytesContainer`, which is itself a workaround for the lack of // DST. unsafe { let this = self.get(); - mem::transmute(this.container_as_bytes()) + mem::transmute::<&[u8],&[u8]>(this.container_as_bytes()) } } } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 386fd8ae5a617..4cfc95d4c3ff6 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -169,17 +169,14 @@ pub fn to_string(f: |&mut State| -> IoResult<()>) -> String { let mut s = rust_printer(box MemWriter::new()); f(&mut s).unwrap(); eof(&mut s.s).unwrap(); - unsafe { + let wr = unsafe { // FIXME(pcwalton): A nasty function to extract the string from an `io::Writer` // that we "know" to be a `MemWriter` that works around the lack of checked // downcasts. - let obj: TraitObject = mem::transmute_copy(&s.s.out); - let wr: Box = mem::transmute(obj.data); - let result = - String::from_utf8(wr.get_ref().as_slice().to_vec()).unwrap(); - mem::forget(wr); - result.to_string() - } + let obj: &TraitObject = mem::transmute(&s.s.out); + mem::transmute::<*mut (), &MemWriter>(obj.data) + }; + String::from_utf8(wr.get_ref().to_vec()).unwrap() } pub fn binop_to_string(op: BinOpToken) -> &'static str { From 961ee0a1e0b90f542c718359c7ee2c3567e7c50f Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Wed, 22 Oct 2014 09:08:07 +1300 Subject: [PATCH 20/34] Allow impls for traits as a concrete type --- src/librustc/middle/resolve.rs | 11 ++++++++--- src/librustc/middle/traits/select.rs | 4 ++-- src/librustc/middle/typeck/coherence/mod.rs | 4 ++-- src/librustc/middle/typeck/coherence/orphan.rs | 3 ++- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index c297bdc6ca2e7..03f1c33cce120 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -627,7 +627,10 @@ impl NameBindings { sp: Span) { // Merges the module with the existing type def or creates a new one. let modifiers = if is_public { PUBLIC } else { DefModifiers::empty() } | IMPORTABLE; - let module_ = Rc::new(Module::new(parent_link, def_id, kind, external, + let module_ = Rc::new(Module::new(parent_link, + def_id, + kind, + external, is_public)); let type_def = self.type_def.borrow().clone(); match type_def { @@ -1372,6 +1375,8 @@ impl<'a> Resolver<'a> { // Create the module and add all methods. match ty.node { TyPath(ref path, _, _) if path.segments.len() == 1 => { + // FIXME(18446) we should distinguish between the name of + // a trait and the name of an impl of that trait. let mod_name = path.segments.last().unwrap().identifier.name; let parent_opt = parent.module().children.borrow() @@ -1380,8 +1385,8 @@ impl<'a> Resolver<'a> { // It already exists Some(ref child) if child.get_module_if_available() .is_some() && - child.get_module().kind.get() == - ImplModuleKind => { + (child.get_module().kind.get() == ImplModuleKind || + child.get_module().kind.get() == TraitModuleKind) => { ModuleReducedGraphParent(child.get_module()) } Some(ref child) if child.get_module_if_available() diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index f8c1c37452b38..9ac47a2347209 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -514,7 +514,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // and `Rc`. (Note that it is not a *coherence violation* // to have impls for both `Bar` and `Baz`, despite this // ambiguity). In this case, we report an error, listing all - // the applicable impls. The use can explicitly "up-coerce" + // the applicable impls. The user can explicitly "up-coerce" // to the type they want. // // Note that this coercion step only considers actual impls @@ -1931,7 +1931,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn all_impls(&self, trait_def_id: ast::DefId) -> Vec { /*! - * Returns se tof all impls for a given trait. + * Returns set of all impls for a given trait. */ ty::populate_implementations_for_trait_if_necessary(self.tcx(), diff --git a/src/librustc/middle/typeck/coherence/mod.rs b/src/librustc/middle/typeck/coherence/mod.rs index a569053507cf8..ac18f53de0465 100644 --- a/src/librustc/middle/typeck/coherence/mod.rs +++ b/src/librustc/middle/typeck/coherence/mod.rs @@ -207,8 +207,8 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> { let impl_items = self.create_impl_from_item(item); for associated_trait in associated_traits.iter() { - let trait_ref = ty::node_id_to_trait_ref( - self.crate_context.tcx, associated_trait.ref_id); + let trait_ref = ty::node_id_to_trait_ref(self.crate_context.tcx, + associated_trait.ref_id); debug!("(checking implementation) adding impl for trait '{}', item '{}'", trait_ref.repr(self.crate_context.tcx), token::get_ident(item.ident)); diff --git a/src/librustc/middle/typeck/coherence/orphan.rs b/src/librustc/middle/typeck/coherence/orphan.rs index e7139e1229b23..3c4ad3473610c 100644 --- a/src/librustc/middle/typeck/coherence/orphan.rs +++ b/src/librustc/middle/typeck/coherence/orphan.rs @@ -41,7 +41,8 @@ impl<'cx, 'tcx,'v> visit::Visitor<'v> for OrphanChecker<'cx, 'tcx> { let self_ty = ty::lookup_item_type(self.tcx, def_id).ty; match ty::get(self_ty).sty { ty::ty_enum(def_id, _) | - ty::ty_struct(def_id, _) => { + ty::ty_struct(def_id, _) | + ty::ty_trait(box ty::TyTrait{ def_id, ..}) => { if def_id.krate != ast::LOCAL_CRATE { span_err!(self.tcx.sess, item.span, E0116, "cannot associate methods with a type outside the \ From f9e52fb34c55a417b289fb39dc2254c09a95226d Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Thu, 30 Oct 2014 15:41:07 +1300 Subject: [PATCH 21/34] tests --- src/test/compile-fail/trait-impl-1.rs | 26 +++++++++++++++++++ src/test/compile-fail/trait-impl-2.rs | 24 ++++++++++++++++++ src/test/run-pass/trait-impl.rs | 36 +++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 src/test/compile-fail/trait-impl-1.rs create mode 100644 src/test/compile-fail/trait-impl-2.rs create mode 100644 src/test/run-pass/trait-impl.rs diff --git a/src/test/compile-fail/trait-impl-1.rs b/src/test/compile-fail/trait-impl-1.rs new file mode 100644 index 0000000000000..5e2ebc3e620d6 --- /dev/null +++ b/src/test/compile-fail/trait-impl-1.rs @@ -0,0 +1,26 @@ +// 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. + +// Test calling methods on an impl for a bare trait. This test checks that the +// trait impl is only applied to a trait object, not concrete types which implement +// the trait. + +trait T {} + +impl<'a> T+'a { + fn foo(&self) {} +} + +impl T for int {} + +fn main() { + let x = &42i; + x.foo(); //~ERROR: type `&int` does not implement any method in scope named `foo` +} diff --git a/src/test/compile-fail/trait-impl-2.rs b/src/test/compile-fail/trait-impl-2.rs new file mode 100644 index 0000000000000..303e3d937444d --- /dev/null +++ b/src/test/compile-fail/trait-impl-2.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. + +// Test calling methods on an impl for a bare trait. This test checks trait impls +// must be in the same module as the trait. + +mod Foo { + trait T {} +} + +mod Bar { + impl<'a> ::Foo::T+'a { //~ERROR: inherent implementations may only be implemented in the same + fn foo(&self) {} + } +} + +fn main() {} diff --git a/src/test/run-pass/trait-impl.rs b/src/test/run-pass/trait-impl.rs new file mode 100644 index 0000000000000..216a7ef33f54c --- /dev/null +++ b/src/test/run-pass/trait-impl.rs @@ -0,0 +1,36 @@ +// 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. + +// Test calling methods on an impl for a bare trait. + +static mut COUNT: uint = 1; + +trait T {} + +impl<'a> T+'a { + fn foo(&self) { + unsafe { COUNT *= 2; } + } + fn bar() { + unsafe { COUNT *= 3; } + } +} + +impl T for int {} + +fn main() { + let x: &T = &42i; + + x.foo(); + T::foo(x); + T::bar(); + + unsafe { assert!(COUNT == 12); } +} From a87078a27d4e5434f937ed3810c9fbfa7ff1f793 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sat, 25 Oct 2014 22:27:15 +0300 Subject: [PATCH 22/34] Clean-up transmutes in librustc None of them would break by implementation-defined struct layout, but one would break with strict lifetime aliasing, and the rest are just ugly code. --- src/librustc/lint/context.rs | 4 +-- src/librustc/metadata/common.rs | 3 +- src/librustc/metadata/encoder.rs | 31 ++++++---------- src/librustc/metadata/loader.rs | 35 ++++++------------ src/librustc/middle/astencode.rs | 43 ++++++---------------- src/librustc/middle/liveness.rs | 6 +--- src/librustc/middle/subst.rs | 58 +----------------------------- src/librustc/middle/trans/type_.rs | 15 ++++---- src/librustc_llvm/lib.rs | 3 +- src/libsyntax/ast_util.rs | 33 ++++++++--------- 10 files changed, 61 insertions(+), 170 deletions(-) diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index a18d91196e04d..6da74eee8a319 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -532,7 +532,7 @@ impl<'a, 'tcx> Context<'a, 'tcx> { } } - fn visit_ids(&self, f: |&mut ast_util::IdVisitor|) { + fn visit_ids(&mut self, f: |&mut ast_util::IdVisitor|) { let mut v = ast_util::IdVisitor { operation: self, pass_through_items: false, @@ -749,7 +749,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> { // Output any lints that were previously added to the session. impl<'a, 'tcx> IdVisitingOperation for Context<'a, 'tcx> { - fn visit_id(&self, id: ast::NodeId) { + fn visit_id(&mut self, id: ast::NodeId) { match self.tcx.sess.lints.borrow_mut().pop(&id) { None => {} Some(lints) => { diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index bc58097b86072..a04f94c31bfc5 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -148,7 +148,7 @@ impl astencode_tag { pub fn from_uint(value : uint) -> Option { let is_a_tag = first_astencode_tag <= value && value <= last_astencode_tag; if !is_a_tag { None } else { - Some(unsafe { mem::transmute(value) }) + Some(unsafe { mem::transmute::(value) }) } } } @@ -247,4 +247,3 @@ pub const tag_type_param_def: uint = 0xa5; pub const tag_item_generics: uint = 0xa6; pub const tag_method_ty_generics: uint = 0xa7; - diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index a5d3cc1f12c29..fd389c1f31426 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -29,7 +29,6 @@ use serialize::Encodable; use std::cell::RefCell; use std::hash::Hash; use std::hash; -use std::mem; use std::collections::HashMap; use syntax::abi; use syntax::ast::*; @@ -1508,44 +1507,36 @@ fn my_visit_expr(_e: &Expr) { } fn my_visit_item(i: &Item, rbml_w: &mut Encoder, - ecx_ptr: *const int, + ecx: &EncodeContext, index: &mut Vec>) { - let mut rbml_w = unsafe { rbml_w.unsafe_clone() }; - // See above - let ecx: &EncodeContext = unsafe { mem::transmute(ecx_ptr) }; ecx.tcx.map.with_path(i.id, |path| { - encode_info_for_item(ecx, &mut rbml_w, i, index, path, i.vis); + encode_info_for_item(ecx, rbml_w, i, index, path, i.vis); }); } fn my_visit_foreign_item(ni: &ForeignItem, rbml_w: &mut Encoder, - ecx_ptr:*const int, + ecx: &EncodeContext, index: &mut Vec>) { - // See above - let ecx: &EncodeContext = unsafe { mem::transmute(ecx_ptr) }; debug!("writing foreign item {}::{}", ecx.tcx.map.path_to_string(ni.id), token::get_ident(ni.ident)); - let mut rbml_w = unsafe { - rbml_w.unsafe_clone() - }; let abi = ecx.tcx.map.get_foreign_abi(ni.id); ecx.tcx.map.with_path(ni.id, |path| { - encode_info_for_foreign_item(ecx, &mut rbml_w, + encode_info_for_foreign_item(ecx, rbml_w, ni, index, path, abi); }); } -struct EncodeVisitor<'a,'b:'a> { +struct EncodeVisitor<'a, 'b:'a, 'c:'a, 'tcx:'c> { rbml_w_for_visit_item: &'a mut Encoder<'b>, - ecx_ptr:*const int, + ecx: &'a EncodeContext<'c,'tcx>, index: &'a mut Vec>, } -impl<'a, 'b, 'v> Visitor<'v> for EncodeVisitor<'a, 'b> { +impl<'a, 'b, 'c, 'tcx, 'v> Visitor<'v> for EncodeVisitor<'a, 'b, 'c, 'tcx> { fn visit_expr(&mut self, ex: &Expr) { visit::walk_expr(self, ex); my_visit_expr(ex); @@ -1554,14 +1545,14 @@ impl<'a, 'b, 'v> Visitor<'v> for EncodeVisitor<'a, 'b> { visit::walk_item(self, i); my_visit_item(i, self.rbml_w_for_visit_item, - self.ecx_ptr, + self.ecx, self.index); } fn visit_foreign_item(&mut self, ni: &ForeignItem) { visit::walk_foreign_item(self, ni); my_visit_foreign_item(ni, self.rbml_w_for_visit_item, - self.ecx_ptr, + self.ecx, self.index); } } @@ -1585,11 +1576,9 @@ fn encode_info_for_items(ecx: &EncodeContext, syntax::parse::token::special_idents::invalid, Public); - // See comment in `encode_side_tables_for_ii` in astencode - let ecx_ptr: *const int = unsafe { mem::transmute(ecx) }; visit::walk_crate(&mut EncodeVisitor { index: &mut index, - ecx_ptr: ecx_ptr, + ecx: ecx, rbml_w_for_visit_item: &mut *rbml_w, }, krate); diff --git a/src/librustc/metadata/loader.rs b/src/librustc/metadata/loader.rs index 623a763bd7468..1310e20d7a45f 100644 --- a/src/librustc/metadata/loader.rs +++ b/src/librustc/metadata/loader.rs @@ -231,7 +231,6 @@ use std::c_str::ToCStr; use std::cmp; use std::io::fs::PathExtensions; use std::io; -use std::mem; use std::ptr; use std::slice; use std::string; @@ -287,8 +286,8 @@ pub struct Library { pub struct ArchiveMetadata { _archive: ArchiveRO, - // See comments in ArchiveMetadata::new for why this is static - data: &'static [u8], + // points into self._archive + data: *const [u8], } pub struct CratePaths { @@ -709,33 +708,21 @@ pub fn note_crate_name(diag: &SpanHandler, name: &str) { impl ArchiveMetadata { fn new(ar: ArchiveRO) -> Option { - let data: &'static [u8] = { - let data = match ar.read(METADATA_FILENAME) { - Some(data) => data, - None => { - debug!("didn't find '{}' in the archive", METADATA_FILENAME); - return None; - } - }; - // This data is actually a pointer inside of the archive itself, but - // we essentially want to cache it because the lookup inside the - // archive is a fairly expensive operation (and it's queried for - // *very* frequently). For this reason, we transmute it to the - // static lifetime to put into the struct. Note that the buffer is - // never actually handed out with a static lifetime, but rather the - // buffer is loaned with the lifetime of this containing object. - // Hence, we're guaranteed that the buffer will never be used after - // this object is dead, so this is a safe operation to transmute and - // store the data as a static buffer. - unsafe { mem::transmute(data) } + let data = match ar.read(METADATA_FILENAME) { + Some(data) => data as *const [u8], + None => { + debug!("didn't find '{}' in the archive", METADATA_FILENAME); + return None; + } }; + Some(ArchiveMetadata { _archive: ar, data: data, }) } - pub fn as_slice<'a>(&'a self) -> &'a [u8] { self.data } + pub fn as_slice<'a>(&'a self) -> &'a [u8] { unsafe { &*self.data } } } // Just a small wrapper to time how long reading metadata takes. @@ -798,7 +785,7 @@ fn get_metadata_section_imp(os: abi::Os, filename: &Path) -> Result write_tag_and_id for Encoder<'a> { } } -struct SideTableEncodingIdVisitor<'a,'b:'a> { - ecx_ptr: *const libc::c_void, - new_rbml_w: &'a mut Encoder<'b>, +struct SideTableEncodingIdVisitor<'a, 'b:'a, 'c:'a, 'tcx:'c> { + ecx: &'a e::EncodeContext<'c, 'tcx>, + rbml_w: &'a mut Encoder<'b>, } -impl<'a,'b> ast_util::IdVisitingOperation for - SideTableEncodingIdVisitor<'a,'b> { - fn visit_id(&self, id: ast::NodeId) { - // Note: this will cause a copy of rbml_w, which is bad as - // it is mutable. But I believe it's harmless since we generate - // balanced EBML. - // - // FIXME(pcwalton): Don't copy this way. - let mut new_rbml_w = unsafe { - self.new_rbml_w.unsafe_clone() - }; - // See above - let ecx: &e::EncodeContext = unsafe { - mem::transmute(self.ecx_ptr) - }; - encode_side_tables_for_id(ecx, &mut new_rbml_w, id) +impl<'a, 'b, 'c, 'tcx> ast_util::IdVisitingOperation for + SideTableEncodingIdVisitor<'a, 'b, 'c, 'tcx> { + fn visit_id(&mut self, id: ast::NodeId) { + encode_side_tables_for_id(self.ecx, self.rbml_w, id) } } @@ -1156,18 +1142,9 @@ fn encode_side_tables_for_ii(ecx: &e::EncodeContext, rbml_w: &mut Encoder, ii: &ast::InlinedItem) { rbml_w.start_tag(c::tag_table as uint); - let mut new_rbml_w = unsafe { - rbml_w.unsafe_clone() - }; - - // Because the ast visitor uses @IdVisitingOperation, I can't pass in - // ecx directly, but /I/ know that it'll be fine since the lifetime is - // tied to the CrateContext that lives throughout this entire section. - ast_util::visit_ids_for_inlined_item(ii, &SideTableEncodingIdVisitor { - ecx_ptr: unsafe { - mem::transmute(ecx) - }, - new_rbml_w: &mut new_rbml_w, + ast_util::visit_ids_for_inlined_item(ii, &mut SideTableEncodingIdVisitor { + ecx: ecx, + rbml_w: rbml_w }); rbml_w.end_tag(); } diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 80eba56ea6ce0..a612b4badc175 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -118,7 +118,6 @@ use util::nodemap::NodeMap; use std::fmt; use std::io; -use std::mem::transmute; use std::rc::Rc; use std::str; use std::uint; @@ -380,10 +379,7 @@ fn visit_fn(ir: &mut IrMaps, // swap in a new set of IR maps for this function body: let mut fn_maps = IrMaps::new(ir.tcx); - unsafe { - debug!("creating fn_maps: {}", - transmute::<&IrMaps, *const IrMaps>(&fn_maps)); - } + debug!("creating fn_maps: {}", &fn_maps as *const IrMaps); for arg in decl.inputs.iter() { pat_util::pat_bindings(&ir.tcx.def_map, diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index b986d4dd591f3..bec02c736adcb 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -16,66 +16,10 @@ use middle::ty_fold::{TypeFoldable, TypeFolder}; use util::ppaux::Repr; use std::fmt; -use std::mem; -use std::raw; -use std::slice::{Items, MutItems}; +use std::slice::Items; use std::vec::Vec; use syntax::codemap::{Span, DUMMY_SP}; -/////////////////////////////////////////////////////////////////////////// -// HomogeneousTuple3 trait -// -// This could be moved into standard library at some point. - -trait HomogeneousTuple3 { - fn len(&self) -> uint; - fn as_slice<'a>(&'a self) -> &'a [T]; - fn as_mut_slice<'a>(&'a mut self) -> &'a mut [T]; - fn iter<'a>(&'a self) -> Items<'a, T>; - fn iter_mut<'a>(&'a mut self) -> MutItems<'a, T>; - fn get<'a>(&'a self, index: uint) -> Option<&'a T>; - fn get_mut<'a>(&'a mut self, index: uint) -> Option<&'a mut T>; -} - -impl HomogeneousTuple3 for (T, T, T) { - fn len(&self) -> uint { - 3 - } - - fn as_slice<'a>(&'a self) -> &'a [T] { - unsafe { - let ptr: *const T = mem::transmute(self); - let slice = raw::Slice { data: ptr, len: 3 }; - mem::transmute(slice) - } - } - - fn as_mut_slice<'a>(&'a mut self) -> &'a mut [T] { - unsafe { - let ptr: *const T = mem::transmute(self); - let slice = raw::Slice { data: ptr, len: 3 }; - mem::transmute(slice) - } - } - - fn iter<'a>(&'a self) -> Items<'a, T> { - let slice: &'a [T] = self.as_slice(); - slice.iter() - } - - fn iter_mut<'a>(&'a mut self) -> MutItems<'a, T> { - self.as_mut_slice().iter_mut() - } - - fn get<'a>(&'a self, index: uint) -> Option<&'a T> { - self.as_slice().get(index) - } - - fn get_mut<'a>(&'a mut self, index: uint) -> Option<&'a mut T> { - Some(&mut self.as_mut_slice()[index]) // wrong: fallible - } -} - /////////////////////////////////////////////////////////////////////////// /** diff --git a/src/librustc/middle/trans/type_.rs b/src/librustc/middle/trans/type_.rs index d8424c9f316b5..1746f78531161 100644 --- a/src/librustc/middle/trans/type_.rs +++ b/src/librustc/middle/trans/type_.rs @@ -27,6 +27,7 @@ use std::collections::HashMap; use libc::c_uint; #[deriving(Clone, PartialEq, Show)] +#[repr(C)] pub struct Type { rf: TypeRef } @@ -283,9 +284,10 @@ impl Type { if n_elts == 0 { return Vec::new(); } - let mut elts = Vec::from_elem(n_elts, 0 as TypeRef); - llvm::LLVMGetStructElementTypes(self.to_ref(), &mut elts[0]); - mem::transmute(elts) + let mut elts = Vec::from_elem(n_elts, Type { rf: 0 as TypeRef }); + llvm::LLVMGetStructElementTypes(self.to_ref(), + elts.as_mut_ptr() as *mut TypeRef); + elts } } @@ -296,9 +298,10 @@ impl Type { pub fn func_params(&self) -> Vec { unsafe { let n_args = llvm::LLVMCountParamTypes(self.to_ref()) as uint; - let args = Vec::from_elem(n_args, 0 as TypeRef); - llvm::LLVMGetParamTypes(self.to_ref(), args.as_ptr()); - mem::transmute(args) + let mut args = Vec::from_elem(n_args, Type { rf: 0 as TypeRef }); + llvm::LLVMGetParamTypes(self.to_ref(), + args.as_mut_ptr() as *mut TypeRef); + args } } diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 06456a91e03f3..48e1e590058bb 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -547,7 +547,7 @@ extern { pub fn LLVMIsFunctionVarArg(FunctionTy: TypeRef) -> Bool; pub fn LLVMGetReturnType(FunctionTy: TypeRef) -> TypeRef; pub fn LLVMCountParamTypes(FunctionTy: TypeRef) -> c_uint; - pub fn LLVMGetParamTypes(FunctionTy: TypeRef, Dest: *const TypeRef); + pub fn LLVMGetParamTypes(FunctionTy: TypeRef, Dest: *mut TypeRef); /* Operations on struct types */ pub fn LLVMStructTypeInContext(C: ContextRef, @@ -2195,4 +2195,3 @@ pub unsafe fn static_link_hack_this_sucks() { // Works to the above fix for #15460 to ensure LLVM dependencies that // are only used by rustllvm don't get stripped by the linker. mod llvmdeps; - diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 8b0e1f32fd4c9..863f53be79883 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -21,7 +21,6 @@ use ptr::P; use visit::Visitor; use visit; -use std::cell::Cell; use std::cmp; use std::u32; @@ -333,20 +332,20 @@ impl IdRange { } pub trait IdVisitingOperation { - fn visit_id(&self, node_id: NodeId); + fn visit_id(&mut self, node_id: NodeId); } /// A visitor that applies its operation to all of the node IDs /// in a visitable thing. pub struct IdVisitor<'a, O:'a> { - pub operation: &'a O, + pub operation: &'a mut O, pub pass_through_items: bool, pub visited_outermost: bool, } impl<'a, O: IdVisitingOperation> IdVisitor<'a, O> { - fn visit_generics_helper(&self, generics: &Generics) { + fn visit_generics_helper(&mut self, generics: &Generics) { for type_parameter in generics.ty_params.iter() { self.operation.visit_id(type_parameter.id) } @@ -540,7 +539,7 @@ impl<'a, 'v, O: IdVisitingOperation> Visitor<'v> for IdVisitor<'a, O> { } pub fn visit_ids_for_inlined_item(item: &InlinedItem, - operation: &O) { + operation: &mut O) { let mut id_visitor = IdVisitor { operation: operation, pass_through_items: true, @@ -551,23 +550,21 @@ pub fn visit_ids_for_inlined_item(item: &InlinedItem, } struct IdRangeComputingVisitor { - result: Cell, + result: IdRange, } impl IdVisitingOperation for IdRangeComputingVisitor { - fn visit_id(&self, id: NodeId) { - let mut id_range = self.result.get(); - id_range.add(id); - self.result.set(id_range) + fn visit_id(&mut self, id: NodeId) { + self.result.add(id); } } pub fn compute_id_range_for_inlined_item(item: &InlinedItem) -> IdRange { - let visitor = IdRangeComputingVisitor { - result: Cell::new(IdRange::max()) + let mut visitor = IdRangeComputingVisitor { + result: IdRange::max() }; - visit_ids_for_inlined_item(item, &visitor); - visitor.result.get() + visit_ids_for_inlined_item(item, &mut visitor); + visitor.result } pub fn compute_id_range_for_fn_body(fk: visit::FnKind, @@ -582,16 +579,16 @@ pub fn compute_id_range_for_fn_body(fk: visit::FnKind, * ignoring nested items. */ - let visitor = IdRangeComputingVisitor { - result: Cell::new(IdRange::max()) + let mut visitor = IdRangeComputingVisitor { + result: IdRange::max() }; let mut id_visitor = IdVisitor { - operation: &visitor, + operation: &mut visitor, pass_through_items: false, visited_outermost: false, }; id_visitor.visit_fn(fk, decl, body, sp, id); - visitor.result.get() + id_visitor.operation.result } pub fn walk_pat(pat: &Pat, it: |&Pat| -> bool) -> bool { From 319d778ed306d0b3c35af79c47c702dbdebf6d09 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 28 Oct 2014 14:48:52 -0400 Subject: [PATCH 23/34] Restructure AST so that the associated type definition carries bounds like any other "type parameter". --- src/librustc/middle/astencode.rs | 4 +- src/librustc/middle/privacy.rs | 4 +- src/librustc/middle/resolve.rs | 62 +++++++++++++++------------ src/librustc/middle/stability.rs | 2 +- src/librustc/middle/typeck/collect.rs | 40 ++++++++--------- src/libsyntax/ast.rs | 4 +- src/libsyntax/ast_map/mod.rs | 14 +++--- src/libsyntax/ast_util.rs | 2 +- src/libsyntax/feature_gate.rs | 2 +- src/libsyntax/fold.rs | 11 ++--- src/libsyntax/parse/parser.rs | 11 ++--- src/libsyntax/print/pprust.rs | 44 +++++++++++-------- src/libsyntax/visit.rs | 3 +- 13 files changed, 105 insertions(+), 98 deletions(-) diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 3d2bd763a1434..be4139e65ba81 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -81,7 +81,7 @@ pub fn encode_inlined_item(ecx: &e::EncodeContext, e::IIForeignRef(i) => i.id, e::IITraitItemRef(_, &ast::ProvidedMethod(ref m)) => m.id, e::IITraitItemRef(_, &ast::RequiredMethod(ref m)) => m.id, - e::IITraitItemRef(_, &ast::TypeTraitItem(ref ti)) => ti.id, + e::IITraitItemRef(_, &ast::TypeTraitItem(ref ti)) => ti.ty_param.id, e::IIImplItemRef(_, &ast::MethodImplItem(ref m)) => m.id, e::IIImplItemRef(_, &ast::TypeImplItem(ref ti)) => ti.id, }; @@ -156,7 +156,7 @@ pub fn decode_inlined_item<'tcx>(cdata: &cstore::crate_metadata, match *ti { ast::ProvidedMethod(ref m) => m.pe_ident(), ast::RequiredMethod(ref ty_m) => ty_m.ident, - ast::TypeTraitItem(ref ti) => ti.ident, + ast::TypeTraitItem(ref ti) => ti.ty_param.ident, } }, ast::IIImplItem(_, ref m) => { diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs index 2256bd71e652c..4fbffa2a819d5 100644 --- a/src/librustc/middle/privacy.rs +++ b/src/librustc/middle/privacy.rs @@ -296,8 +296,8 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EmbargoVisitor<'a, 'tcx> { self.exported_items.insert(m.id); } ast::TypeTraitItem(ref t) => { - debug!("typedef {}", t.id); - self.exported_items.insert(t.id); + debug!("typedef {}", t.ty_param.id); + self.exported_items.insert(t.ty_param.id); } } } diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index c297bdc6ca2e7..d1345efaf90d8 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -1559,19 +1559,19 @@ impl<'a> Resolver<'a> { } ast::TypeTraitItem(ref associated_type) => { let def = DefAssociatedTy(local_def( - associated_type.id)); + associated_type.ty_param.id)); let name_bindings = - self.add_child(associated_type.ident.name, + self.add_child(associated_type.ty_param.ident.name, module_parent.clone(), ForbidDuplicateTypesAndValues, - associated_type.span); + associated_type.ty_param.span); // NB: not IMPORTABLE name_bindings.define_type(def, - associated_type.span, + associated_type.ty_param.span, PUBLIC); - (associated_type.ident.name, TypeTraitItemKind) + (associated_type.ty_param.ident.name, TypeTraitItemKind) } }; @@ -4218,7 +4218,7 @@ impl<'a> Resolver<'a> { impl_items.as_slice()); } - ItemTrait(ref generics, ref unbound, ref bounds, ref methods) => { + ItemTrait(ref generics, ref unbound, ref bounds, ref trait_items) => { // Create a new rib for the self type. let mut self_type_rib = Rib::new(ItemRibKind); @@ -4246,13 +4246,13 @@ impl<'a> Resolver<'a> { _ => {} } - for method in (*methods).iter() { - // Create a new rib for the method-specific type + for trait_item in (*trait_items).iter() { + // Create a new rib for the trait_item-specific type // parameters. // // FIXME #4951: Do we need a node ID here? - match *method { + match *trait_item { ast::RequiredMethod(ref ty_m) => { this.with_type_parameter_rib (HasTypeParameters(&ty_m.generics, @@ -4287,8 +4287,9 @@ impl<'a> Resolver<'a> { ProvidedMethod(m.id)), &**m) } - ast::TypeTraitItem(_) => { - visit::walk_trait_item(this, method); + ast::TypeTraitItem(ref data) => { + this.resolve_type_parameter(&data.ty_param); + visit::walk_trait_item(this, trait_item); } } } @@ -4477,20 +4478,25 @@ impl<'a> Resolver<'a> { fn resolve_type_parameters(&mut self, type_parameters: &OwnedSlice) { for type_parameter in type_parameters.iter() { - for bound in type_parameter.bounds.iter() { - self.resolve_type_parameter_bound(type_parameter.id, bound, - TraitBoundingTypeParameter); - } - match &type_parameter.unbound { - &Some(ref unbound) => - self.resolve_type_parameter_bound( - type_parameter.id, unbound, TraitBoundingTypeParameter), - &None => {} - } - match type_parameter.default { - Some(ref ty) => self.resolve_type(&**ty), - None => {} - } + self.resolve_type_parameter(type_parameter); + } + } + + fn resolve_type_parameter(&mut self, + type_parameter: &TyParam) { + for bound in type_parameter.bounds.iter() { + self.resolve_type_parameter_bound(type_parameter.id, bound, + TraitBoundingTypeParameter); + } + match &type_parameter.unbound { + &Some(ref unbound) => + self.resolve_type_parameter_bound( + type_parameter.id, unbound, TraitBoundingTypeParameter), + &None => {} + } + match type_parameter.default { + Some(ref ty) => self.resolve_type(&**ty), + None => {} } } @@ -4577,14 +4583,14 @@ impl<'a> Resolver<'a> { self.resolve_error(trait_reference.path.span, format!("`{}` is not a trait", self.path_names_to_string( - &trait_reference.path))); + &trait_reference.path))); // If it's a typedef, give a note match def { DefTy(..) => { self.session.span_note( - trait_reference.path.span, - format!("`type` aliases cannot \ + trait_reference.path.span, + format!("`type` aliases cannot \ be used for traits") .as_slice()); } diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index 9cac97dc65941..7f6a73c83fa76 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -86,7 +86,7 @@ impl<'v> Visitor<'v> for Annotator { } } - TypeTraitItem(ref typedef) => (typedef.id, &typedef.attrs), + TypeTraitItem(ref typedef) => (typedef.ty_param.id, &typedef.attrs), }; self.annotate(id, attrs, |v| visit::walk_trait_item(v, t)); } diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index 847d8e88bde88..a4faff8e3bd85 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -298,7 +298,7 @@ fn collect_trait_methods(ccx: &CrateCtxt, &*m.pe_fn_decl()) } ast::TypeTraitItem(ref at) => { - tcx.sess.span_bug(at.span, + tcx.sess.span_bug(at.ty_param.span, "there shouldn't \ be a type trait \ item here") @@ -315,9 +315,9 @@ fn collect_trait_methods(ccx: &CrateCtxt, ast::TypeTraitItem(ref ast_associated_type) => { let trait_did = local_def(trait_id); let associated_type = ty::AssociatedType { - name: ast_associated_type.ident.name, + name: ast_associated_type.ty_param.ident.name, vis: ast::Public, - def_id: local_def(ast_associated_type.id), + def_id: local_def(ast_associated_type.ty_param.id), container: TraitContainer(trait_did), }; @@ -345,7 +345,7 @@ fn collect_trait_methods(ccx: &CrateCtxt, method.id)) } ast::TypeTraitItem(ref typedef) => { - ty::TypeTraitItemId(local_def(typedef.id)) + ty::TypeTraitItemId(local_def(typedef.ty_param.id)) } } }).collect()); @@ -463,12 +463,12 @@ fn convert_associated_type(ccx: &CrateCtxt, .get_slice(subst::TypeSpace) .iter() .find(|def| { - def.def_id == local_def(associated_type.id) + def.def_id == local_def(associated_type.ty_param.id) }); let type_parameter_def = match type_parameter_def { Some(type_parameter_def) => type_parameter_def, None => { - ccx.tcx().sess.span_bug(associated_type.span, + ccx.tcx().sess.span_bug(associated_type.ty_param.span, "`convert_associated_type()` didn't find \ a type parameter ID corresponding to \ this type") @@ -477,18 +477,18 @@ fn convert_associated_type(ccx: &CrateCtxt, let param_type = ty::mk_param(ccx.tcx, subst::TypeSpace, type_parameter_def.index, - local_def(associated_type.id)); - ccx.tcx.tcache.borrow_mut().insert(local_def(associated_type.id), + local_def(associated_type.ty_param.id)); + ccx.tcx.tcache.borrow_mut().insert(local_def(associated_type.ty_param.id), Polytype { generics: ty::Generics::empty(), ty: param_type, }); - write_ty_to_tcx(ccx.tcx, associated_type.id, param_type); + write_ty_to_tcx(ccx.tcx, associated_type.ty_param.id, param_type); let associated_type = Rc::new(ty::AssociatedType { - name: associated_type.ident.name, + name: associated_type.ty_param.ident.name, vis: ast::Public, - def_id: local_def(associated_type.id), + def_id: local_def(associated_type.ty_param.id), container: TraitContainer(trait_def.trait_ref.def_id), }); ccx.tcx @@ -978,7 +978,7 @@ impl<'a,'tcx> AstConv<'tcx> for TraitMethodCtxt<'a,'tcx> { match *item { ast::RequiredMethod(_) | ast::ProvidedMethod(_) => {} ast::TypeTraitItem(ref item) => { - if local_def(item.id) == associated_type_id { + if local_def(item.ty_param.id) == associated_type_id { return ty::mk_param(self.tcx(), subst::TypeSpace, index, @@ -1480,7 +1480,7 @@ pub fn trait_def_of_item(ccx: &CrateCtxt, it: &ast::Item) -> Rc { types.push(ty::mk_param(ccx.tcx, subst::TypeSpace, index, - local_def(trait_item.id))) + local_def(trait_item.ty_param.id))) } ast::RequiredMethod(_) | ast::ProvidedMethod(_) => {} } @@ -1630,11 +1630,11 @@ fn ty_of_trait_item(ccx: &CrateCtxt, trait_item: &ast::TraitItem) "ty_of_trait_item() on provided method") } ast::TypeTraitItem(ref associated_type) => { - let parent = ccx.tcx.map.get_parent(associated_type.id); + let parent = ccx.tcx.map.get_parent(associated_type.ty_param.id); let trait_def = match ccx.tcx.map.get(parent) { ast_map::NodeItem(item) => trait_def_of_item(ccx, &*item), _ => { - ccx.tcx.sess.span_bug(associated_type.span, + ccx.tcx.sess.span_bug(associated_type.ty_param.span, "associated type's parent wasn't \ an item?!") } @@ -1680,8 +1680,8 @@ fn ty_generics_for_trait(ccx: &CrateCtxt, let def = ty::TypeParameterDef { space: subst::TypeSpace, index: generics.types.len(subst::TypeSpace), - name: associated_type.ident.name, - def_id: local_def(associated_type.id), + name: associated_type.ty_param.ident.name, + def_id: local_def(associated_type.ty_param.id), bounds: ty::ParamBounds { builtin_bounds: ty::empty_builtin_bounds(), trait_bounds: Vec::new(), @@ -1690,7 +1690,7 @@ fn ty_generics_for_trait(ccx: &CrateCtxt, associated_with: Some(local_def(trait_id)), default: None, }; - ccx.tcx.ty_param_defs.borrow_mut().insert(associated_type.id, + ccx.tcx.ty_param_defs.borrow_mut().insert(associated_type.ty_param.id, def.clone()); generics.types.push(subst::TypeSpace, def); } @@ -1810,9 +1810,9 @@ fn ensure_associated_types<'tcx,AC>(this: &AC, trait_id: ast::DefId) ast::ProvidedMethod(_) => {} ast::TypeTraitItem(ref associated_type) => { let info = ty::AssociatedTypeInfo { - def_id: local_def(associated_type.id), + def_id: local_def(associated_type.ty_param.id), index: index, - name: associated_type.ident.name, + name: associated_type.ty_param.ident.name, }; result.push(info); index += 1; diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index a2c859cf9fd3c..fdacc860bb766 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -861,10 +861,8 @@ pub enum ImplItem { #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] pub struct AssociatedType { - pub id: NodeId, - pub span: Span, - pub ident: Ident, pub attrs: Vec, + pub ty_param: TyParam, } #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] diff --git a/src/libsyntax/ast_map/mod.rs b/src/libsyntax/ast_map/mod.rs index fa36577ebdb1f..f049b964ff33d 100644 --- a/src/libsyntax/ast_map/mod.rs +++ b/src/libsyntax/ast_map/mod.rs @@ -405,7 +405,9 @@ impl<'ast> Map<'ast> { MethMac(_) => panic!("no path elem for {}", node), } } - TypeTraitItem(ref m) => PathName(m.ident.name), + TypeTraitItem(ref m) => { + PathName(m.ty_param.ident.name) + } }, NodeVariant(v) => PathName(v.node.name.name), _ => panic!("no path elem for {}", node) @@ -510,7 +512,7 @@ impl<'ast> Map<'ast> { match *trait_method { RequiredMethod(ref type_method) => type_method.span, ProvidedMethod(ref method) => method.span, - TypeTraitItem(ref typedef) => typedef.span, + TypeTraitItem(ref typedef) => typedef.ty_param.span, } } Some(NodeImplItem(ref impl_item)) => { @@ -650,7 +652,7 @@ impl Named for TraitItem { match *self { RequiredMethod(ref tm) => tm.ident.name, ProvidedMethod(ref m) => m.name(), - TypeTraitItem(ref at) => at.ident.name, + TypeTraitItem(ref at) => at.ty_param.ident.name, } } } @@ -783,7 +785,7 @@ impl<'ast> Visitor<'ast> for NodeCollector<'ast> { self.insert(m.id, NodeTraitItem(tm)); } TypeTraitItem(ref typ) => { - self.insert(typ.id, NodeTraitItem(tm)); + self.insert(typ.ty_param.id, NodeTraitItem(tm)); } } } @@ -976,7 +978,7 @@ pub fn map_decoded_item<'ast, F: FoldOps>(map: &Map<'ast>, let trait_item_id = match *trait_item { ProvidedMethod(ref m) => m.id, RequiredMethod(ref m) => m.id, - TypeTraitItem(ref ty) => ty.id, + TypeTraitItem(ref ty) => ty.ty_param.id, }; collector.insert(trait_item_id, NodeTraitItem(trait_item)); @@ -1080,7 +1082,7 @@ fn node_id_to_string(map: &Map, id: NodeId) -> String { } TypeTraitItem(ref t) => { format!("type item {} in {} (id={})", - token::get_ident(t.ident), + token::get_ident(t.ty_param.ident), map.path_to_string(id), id) } diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 8b0e1f32fd4c9..035b80f5564e2 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -525,7 +525,7 @@ impl<'a, 'v, O: IdVisitingOperation> Visitor<'v> for IdVisitor<'a, O> { match *tm { ast::RequiredMethod(ref m) => self.operation.visit_id(m.id), ast::ProvidedMethod(ref m) => self.operation.visit_id(m.id), - ast::TypeTraitItem(ref typ) => self.operation.visit_id(typ.id), + ast::TypeTraitItem(ref typ) => self.operation.visit_id(typ.ty_param.id), } visit::walk_trait_item(self, tm); } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index c1877c827a41f..3743e08e9a936 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -260,7 +260,7 @@ impl<'a, 'v> Visitor<'v> for Context<'a> { ast::RequiredMethod(_) | ast::ProvidedMethod(_) => {} ast::TypeTraitItem(ref ti) => { self.gate_feature("associated_types", - ti.span, + ti.ty_param.span, "associated types are experimental") } } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 9a55f07e98d79..6535c8e89fd4e 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -793,19 +793,16 @@ pub fn noop_fold_typedef(t: Typedef, folder: &mut T) pub fn noop_fold_associated_type(at: AssociatedType, folder: &mut T) -> AssociatedType - where T: Folder { - let new_id = folder.new_id(at.id); - let new_span = folder.new_span(at.span); - let new_ident = folder.fold_ident(at.ident); + where T: Folder +{ let new_attrs = at.attrs .iter() .map(|attr| folder.fold_attribute((*attr).clone())) .collect(); + let new_param = folder.fold_ty_param(at.ty_param); ast::AssociatedType { - ident: new_ident, attrs: new_attrs, - id: new_id, - span: new_span, + ty_param: new_param, } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index aa3b9668d4632..ad445ef331fe9 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1229,16 +1229,13 @@ impl<'a> Parser<'a> { /// Parses `type Foo;` in a trait declaration only. The `type` keyword has /// already been parsed. fn parse_associated_type(&mut self, attrs: Vec) - -> AssociatedType { - let lo = self.span.lo; - let ident = self.parse_ident(); - let hi = self.span.hi; + -> AssociatedType + { + let ty_param = self.parse_ty_param(); self.expect(&token::Semi); AssociatedType { - id: ast::DUMMY_NODE_ID, - span: mk_sp(lo, hi), - ident: ident, attrs: attrs, + ty_param: ty_param, } } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 386fd8ae5a617..b7f6e65fb9367 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -818,9 +818,11 @@ impl<'a> State<'a> { } fn print_associated_type(&mut self, typedef: &ast::AssociatedType) - -> IoResult<()> { + -> IoResult<()> + { + try!(self.print_outer_attributes(typedef.attrs[])); try!(self.word_space("type")); - try!(self.print_ident(typedef.ident)); + try!(self.print_ty_param(&typedef.ty_param)); word(&mut self.s, ";") } @@ -2434,23 +2436,7 @@ impl<'a> State<'a> { } else { let idx = idx - generics.lifetimes.len(); let param = generics.ty_params.get(idx); - match param.unbound { - Some(TraitTyParamBound(ref tref)) => { - try!(s.print_trait_ref(tref)); - try!(s.word_space("?")); - } - _ => {} - } - try!(s.print_ident(param.ident)); - try!(s.print_bounds(":", ¶m.bounds)); - match param.default { - Some(ref default) => { - try!(space(&mut s.s)); - try!(s.word_space("=")); - s.print_type(&**default) - } - _ => Ok(()) - } + s.print_ty_param(param) } })); @@ -2458,6 +2444,26 @@ impl<'a> State<'a> { Ok(()) } + pub fn print_ty_param(&mut self, param: &ast::TyParam) -> IoResult<()> { + match param.unbound { + Some(TraitTyParamBound(ref tref)) => { + try!(self.print_trait_ref(tref)); + try!(self.word_space("?")); + } + _ => {} + } + try!(self.print_ident(param.ident)); + try!(self.print_bounds(":", ¶m.bounds)); + match param.default { + Some(ref default) => { + try!(space(&mut self.s)); + try!(self.word_space("=")); + self.print_type(&**default) + } + _ => Ok(()) + } + } + pub fn print_where_clause(&mut self, generics: &ast::Generics) -> IoResult<()> { if generics.where_clause.predicates.len() == 0 { diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index bec72e88f99b9..86ee23d71a6b2 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -596,7 +596,8 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_method: &'v Tr RequiredMethod(ref method_type) => visitor.visit_ty_method(method_type), ProvidedMethod(ref method) => walk_method_helper(visitor, &**method), TypeTraitItem(ref associated_type) => { - visitor.visit_ident(associated_type.span, associated_type.ident) + visitor.visit_ident(associated_type.ty_param.span, + associated_type.ty_param.ident) } } } From 94d142b596465989958db270e1c6bec83ee1eb99 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 29 Oct 2014 05:58:31 -0400 Subject: [PATCH 24/34] Add in the bounds into the typeparameterdefs for assoc types --- src/librustc/middle/typeck/collect.rs | 125 +++++++++++++------------- src/librustdoc/clean/mod.rs | 6 +- 2 files changed, 64 insertions(+), 67 deletions(-) diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index a4faff8e3bd85..66c311e4d664b 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -1661,35 +1661,30 @@ fn ty_generics_for_type(ccx: &CrateCtxt, fn ty_generics_for_trait(ccx: &CrateCtxt, trait_id: ast::NodeId, substs: &subst::Substs, - generics: &ast::Generics, + ast_generics: &ast::Generics, items: &[ast::TraitItem]) -> ty::Generics { let mut generics = ty_generics(ccx, subst::TypeSpace, - generics.lifetimes.as_slice(), - generics.ty_params.as_slice(), + ast_generics.lifetimes.as_slice(), + ast_generics.ty_params.as_slice(), ty::Generics::empty(), - &generics.where_clause, + &ast_generics.where_clause, DontCreateTypeParametersForAssociatedTypes); // Add in type parameters for any associated types. for item in items.iter() { match *item { ast::TypeTraitItem(ref associated_type) => { - let def = ty::TypeParameterDef { - space: subst::TypeSpace, - index: generics.types.len(subst::TypeSpace), - name: associated_type.ty_param.ident.name, - def_id: local_def(associated_type.ty_param.id), - bounds: ty::ParamBounds { - builtin_bounds: ty::empty_builtin_bounds(), - trait_bounds: Vec::new(), - region_bounds: Vec::new(), - }, - associated_with: Some(local_def(trait_id)), - default: None, - }; + let def = + get_or_create_type_parameter_def( + ccx, + subst::TypeSpace, + &associated_type.ty_param, + generics.types.len(subst::TypeSpace), + &ast_generics.where_clause, + Some(local_def(trait_id))); ccx.tcx.ty_param_defs.borrow_mut().insert(associated_type.ty_param.id, def.clone()); generics.types.push(subst::TypeSpace, def); @@ -1960,7 +1955,8 @@ fn ty_generics<'tcx,AC>(this: &AC, space, param, i, - where_clause); + where_clause, + None); debug!("ty_generics: def for type param: {}, {}", def.repr(this.tcx()), space); @@ -1980,63 +1976,64 @@ fn ty_generics<'tcx,AC>(this: &AC, } return result; +} - fn get_or_create_type_parameter_def<'tcx,AC>( - this: &AC, - space: subst::ParamSpace, - param: &ast::TyParam, - index: uint, - where_clause: &ast::WhereClause) - -> ty::TypeParameterDef - where AC: AstConv<'tcx> { - match this.tcx().ty_param_defs.borrow().find(¶m.id) { - Some(d) => { return (*d).clone(); } - None => { } - } - - let param_ty = ty::ParamTy::new(space, index, local_def(param.id)); - let bounds = compute_bounds(this, - param.ident.name, - param_ty, - param.bounds.as_slice(), - ¶m.unbound, - param.span, - where_clause); - let default = match param.default { - None => None, - Some(ref path) => { - let ty = ast_ty_to_ty(this, &ExplicitRscope, &**path); - let cur_idx = index; - - ty::walk_ty(ty, |t| { - match ty::get(t).sty { - ty::ty_param(p) => if p.idx > cur_idx { +fn get_or_create_type_parameter_def<'tcx,AC>(this: &AC, + space: subst::ParamSpace, + param: &ast::TyParam, + index: uint, + where_clause: &ast::WhereClause, + associated_with: Option) + -> ty::TypeParameterDef + where AC: AstConv<'tcx> +{ + match this.tcx().ty_param_defs.borrow().find(¶m.id) { + Some(d) => { return (*d).clone(); } + None => { } + } + + let param_ty = ty::ParamTy::new(space, index, local_def(param.id)); + let bounds = compute_bounds(this, + param.ident.name, + param_ty, + param.bounds.as_slice(), + ¶m.unbound, + param.span, + where_clause); + let default = match param.default { + None => None, + Some(ref path) => { + let ty = ast_ty_to_ty(this, &ExplicitRscope, &**path); + let cur_idx = index; + + ty::walk_ty(ty, |t| { + match ty::get(t).sty { + ty::ty_param(p) => if p.idx > cur_idx { span_err!(this.tcx().sess, path.span, E0128, "type parameters with a default cannot use \ forward declared identifiers"); }, _ => {} } - }); + }); - Some(ty) - } - }; + Some(ty) + } + }; - let def = ty::TypeParameterDef { - space: space, - index: index, - name: param.ident.name, - def_id: local_def(param.id), - associated_with: None, - bounds: bounds, - default: default - }; + let def = ty::TypeParameterDef { + space: space, + index: index, + name: param.ident.name, + def_id: local_def(param.id), + associated_with: associated_with, + bounds: bounds, + default: default + }; - this.tcx().ty_param_defs.borrow_mut().insert(param.id, def.clone()); + this.tcx().ty_param_defs.borrow_mut().insert(param.id, def.clone()); - def - } + def } fn compute_bounds<'tcx,AC>(this: &AC, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index c128588918e28..f96b3916f06de 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2203,12 +2203,12 @@ impl Clean for attr::Stability { impl Clean for ast::AssociatedType { fn clean(&self, cx: &DocContext) -> Item { Item { - source: self.span.clean(cx), - name: Some(self.ident.clean(cx)), + source: self.ty_param.span.clean(cx), + name: Some(self.ty_param.ident.clean(cx)), attrs: self.attrs.clean(cx), inner: AssociatedTypeItem, visibility: None, - def_id: ast_util::local_def(self.id), + def_id: ast_util::local_def(self.ty_param.id), stability: None, } } From d2f8074eac9413d70610809ff62ea8b1ff82dc93 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 29 Oct 2014 21:29:16 -0400 Subject: [PATCH 25/34] Add a 4th space for associated types defined in a trait (currently unused) --- src/librustc/middle/astencode.rs | 3 +- src/librustc/middle/subst.rs | 101 ++++++++++-------- src/librustc/middle/traits/select.rs | 3 +- src/librustc/middle/trans/meth.rs | 12 ++- src/librustc/util/ppaux.rs | 9 +- .../compile-fail/variance-regions-direct.rs | 14 +-- .../compile-fail/variance-regions-indirect.rs | 10 +- .../variance-trait-object-bound.rs | 2 +- 8 files changed, 85 insertions(+), 69 deletions(-) diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index be4139e65ba81..d1d81b9a915cc 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -717,8 +717,9 @@ impl<'a> vtable_decoder_helpers for reader::Decoder<'a> { { let types = self.read_to_vec(|this| Ok(f(this))).unwrap(); let selfs = self.read_to_vec(|this| Ok(f(this))).unwrap(); + let assocs = self.read_to_vec(|this| Ok(f(this))).unwrap(); let fns = self.read_to_vec(|this| Ok(f(this))).unwrap(); - VecPerParamSpace::new(types, selfs, fns) + VecPerParamSpace::new(types, selfs, assocs, fns) } fn read_vtable_res_with_key(&mut self, diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index b986d4dd591f3..c322b4aae7ef0 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -112,8 +112,8 @@ impl Substs { r: Vec) -> Substs { - Substs::new(VecPerParamSpace::new(t, Vec::new(), Vec::new()), - VecPerParamSpace::new(r, Vec::new(), Vec::new())) + Substs::new(VecPerParamSpace::new(t, Vec::new(), Vec::new(), Vec::new()), + VecPerParamSpace::new(r, Vec::new(), Vec::new(), Vec::new())) } pub fn new_trait(t: Vec, @@ -121,8 +121,8 @@ impl Substs { s: ty::t) -> Substs { - Substs::new(VecPerParamSpace::new(t, vec!(s), Vec::new()), - VecPerParamSpace::new(r, Vec::new(), Vec::new())) + Substs::new(VecPerParamSpace::new(t, vec!(s), Vec::new(), Vec::new()), + VecPerParamSpace::new(r, Vec::new(), Vec::new(), Vec::new())) } pub fn erased(t: VecPerParamSpace) -> Substs @@ -226,21 +226,23 @@ impl RegionSubsts { #[deriving(PartialOrd, Ord, PartialEq, Eq, Clone, Hash, Encodable, Decodable, Show)] pub enum ParamSpace { - TypeSpace, // Type parameters attached to a type definition, trait, or impl - SelfSpace, // Self parameter on a trait - FnSpace, // Type parameters attached to a method or fn + TypeSpace, // Type parameters attached to a type definition, trait, or impl + SelfSpace, // Self parameter on a trait + AssocSpace, // Assoc types defined in a trait/impl + FnSpace, // Type parameters attached to a method or fn } impl ParamSpace { - pub fn all() -> [ParamSpace, ..3] { - [TypeSpace, SelfSpace, FnSpace] + pub fn all() -> [ParamSpace, ..4] { + [TypeSpace, SelfSpace, AssocSpace, FnSpace] } pub fn to_uint(self) -> uint { match self { TypeSpace => 0, SelfSpace => 1, - FnSpace => 2, + AssocSpace => 2, + FnSpace => 3, } } @@ -248,7 +250,8 @@ impl ParamSpace { match u { 0 => TypeSpace, 1 => SelfSpace, - 2 => FnSpace, + 2 => AssocSpace, + 3 => FnSpace, _ => panic!("Invalid ParamSpace: {}", u) } } @@ -268,11 +271,13 @@ pub struct VecPerParamSpace { // Here is how the representation corresponds to the abstraction // i.e. the "abstraction function" AF: // - // AF(self) = (self.content.slice_to(self.type_limit), - // self.content.slice(self.type_limit, self.self_limit), - // self.content.slice_from(self.self_limit)) + // AF(self) = (self.content[..self.type_limit], + // self.content[self.type_limit..self.self_limit], + // self.content[self.self_limit..self.assoc_limit], + // self.content[self.assoc_limit..]) type_limit: uint, self_limit: uint, + assoc_limit: uint, content: Vec, } @@ -292,7 +297,8 @@ impl VecPerParamSpace { match space { TypeSpace => (0, self.type_limit), SelfSpace => (self.type_limit, self.self_limit), - FnSpace => (self.self_limit, self.content.len()), + AssocSpace => (self.self_limit, self.assoc_limit), + FnSpace => (self.assoc_limit, self.content.len()), } } @@ -300,6 +306,7 @@ impl VecPerParamSpace { VecPerParamSpace { type_limit: 0, self_limit: 0, + assoc_limit: 0, content: Vec::new() } } @@ -310,26 +317,33 @@ impl VecPerParamSpace { /// `t` is the type space. /// `s` is the self space. + /// `a` is the assoc space. /// `f` is the fn space. - pub fn new(t: Vec, s: Vec, f: Vec) -> VecPerParamSpace { + pub fn new(t: Vec, s: Vec, a: Vec, f: Vec) -> VecPerParamSpace { let type_limit = t.len(); - let self_limit = t.len() + s.len(); + let self_limit = type_limit + s.len(); + let assoc_limit = self_limit + a.len(); + let mut content = t; content.extend(s.into_iter()); + content.extend(a.into_iter()); content.extend(f.into_iter()); + VecPerParamSpace { type_limit: type_limit, self_limit: self_limit, + assoc_limit: assoc_limit, content: content, } } - fn new_internal(content: Vec, type_limit: uint, self_limit: uint) + fn new_internal(content: Vec, type_limit: uint, self_limit: uint, assoc_limit: uint) -> VecPerParamSpace { VecPerParamSpace { type_limit: type_limit, self_limit: self_limit, + assoc_limit: assoc_limit, content: content, } } @@ -341,9 +355,10 @@ impl VecPerParamSpace { pub fn push(&mut self, space: ParamSpace, value: T) { let (_, limit) = self.limits(space); match space { - TypeSpace => { self.type_limit += 1; self.self_limit += 1; } - SelfSpace => { self.self_limit += 1; } - FnSpace => {} + TypeSpace => { self.type_limit += 1; self.self_limit += 1; self.assoc_limit += 1; } + SelfSpace => { self.self_limit += 1; self.assoc_limit += 1; } + AssocSpace => { self.assoc_limit += 1; } + FnSpace => { } } self.content.insert(limit, value); } @@ -354,9 +369,10 @@ impl VecPerParamSpace { None } else { match space { - TypeSpace => { self.type_limit -= 1; self.self_limit -= 1; } - SelfSpace => { self.self_limit -= 1; } - FnSpace => {} + TypeSpace => { self.type_limit -= 1; self.self_limit -= 1; self.assoc_limit -= 1; } + SelfSpace => { self.self_limit -= 1; self.assoc_limit -= 1; } + AssocSpace => { self.assoc_limit -= 1; } + FnSpace => {} } self.content.remove(limit - 1) } @@ -442,35 +458,29 @@ impl VecPerParamSpace { let result = self.iter().map(pred).collect(); VecPerParamSpace::new_internal(result, self.type_limit, - self.self_limit) + self.self_limit, + self.assoc_limit) } pub fn map_move(self, pred: |T| -> U) -> VecPerParamSpace { - let (t, s, f) = self.split(); + let (t, s, a, f) = self.split(); VecPerParamSpace::new(t.into_iter().map(|p| pred(p)).collect(), s.into_iter().map(|p| pred(p)).collect(), + a.into_iter().map(|p| pred(p)).collect(), f.into_iter().map(|p| pred(p)).collect()) } - pub fn split(self) -> (Vec, Vec, Vec) { - // FIXME (#15418): this does two traversals when in principle - // one would suffice. i.e. change to use `move_iter`. - let VecPerParamSpace { type_limit, self_limit, content } = self; - let mut i = 0; - let (prefix, fn_vec) = content.partition(|_| { - let on_left = i < self_limit; - i += 1; - on_left - }); + pub fn split(self) -> (Vec, Vec, Vec, Vec) { + let VecPerParamSpace { type_limit, self_limit, assoc_limit, content } = self; + + let mut content_iter = content.into_iter(); - let mut i = 0; - let (type_vec, self_vec) = prefix.partition(|_| { - let on_left = i < type_limit; - i += 1; - on_left - }); + let types = content_iter.by_ref().take(type_limit).collect(); + let selfs = content_iter.by_ref().take(self_limit - type_limit).collect(); + let assocs = content_iter.by_ref().take(assoc_limit - self_limit).collect(); + let fns = content_iter.collect(); - (type_vec, self_vec, fn_vec) + (types, selfs, assocs, fns) } pub fn with_vec(mut self, space: ParamSpace, vec: Vec) @@ -616,12 +626,13 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { this.tcx().sess.span_bug( span, format!("Type parameter `{}` ({}/{}/{}) out of range \ - when substituting (root type={})", + when substituting (root type={}) substs={}", p.repr(this.tcx()), source_ty.repr(this.tcx()), space, index, - this.root_ty.repr(this.tcx())).as_slice()); + this.root_ty.repr(this.tcx()), + this.substs.repr(this.tcx())).as_slice()); } } } diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index f8c1c37452b38..a665e1f0a264e 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -1607,7 +1607,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(o) => o, Err(ErrorReported) => Vec::new() }; - let obligations = VecPerParamSpace::new(obligations, Vec::new(), Vec::new()); + let obligations = VecPerParamSpace::new(obligations, Vec::new(), + Vec::new(), Vec::new()); VtableBuiltinData { nested: obligations } } diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index db433167298ce..e54d50c8b5f59 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -205,7 +205,7 @@ pub fn trans_static_method_callee(bcx: Block, // type parameters that belong to the trait but also some that // belong to the method: let rcvr_substs = node_id_substs(bcx, ExprId(expr_id)); - let (rcvr_type, rcvr_self, rcvr_method) = rcvr_substs.types.split(); + let (rcvr_type, rcvr_self, rcvr_assoc, rcvr_method) = rcvr_substs.types.split(); // Lookup the precise impl being called. To do that, we need to // create a trait reference identifying the self type and other @@ -232,6 +232,7 @@ pub fn trans_static_method_callee(bcx: Block, let trait_substs = Substs::erased(VecPerParamSpace::new(rcvr_type, rcvr_self, + rcvr_assoc, Vec::new())); debug!("trait_substs={}", trait_substs.repr(bcx.tcx())); let trait_ref = Rc::new(ty::TraitRef { def_id: trait_id, @@ -265,10 +266,11 @@ pub fn trans_static_method_callee(bcx: Block, // that with the `rcvr_method` from before, which tells us // the type parameters from the *method*, to yield // `callee_substs=[[T=int],[],[U=String]]`. - let (impl_type, impl_self, _) = impl_substs.types.split(); + let (impl_type, impl_self, impl_assoc, _) = impl_substs.types.split(); let callee_substs = Substs::erased(VecPerParamSpace::new(impl_type, impl_self, + impl_assoc, rcvr_method)); let mth_id = method_with_name(ccx, impl_did, mname); @@ -397,12 +399,12 @@ fn combine_impl_and_methods_tps(bcx: Block, // Break apart the type parameters from the node and type // parameters from the receiver. - let (_, _, node_method) = node_substs.types.split(); - let (rcvr_type, rcvr_self, rcvr_method) = rcvr_substs.types.clone().split(); + let (_, _, _, node_method) = node_substs.types.split(); + let (rcvr_type, rcvr_self, rcvr_assoc, rcvr_method) = rcvr_substs.types.clone().split(); assert!(rcvr_method.is_empty()); subst::Substs { regions: subst::ErasedRegions, - types: subst::VecPerParamSpace::new(rcvr_type, rcvr_self, node_method) + types: subst::VecPerParamSpace::new(rcvr_type, rcvr_self, rcvr_assoc, node_method) } } diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 8befba00fd2fe..1d769572ffee2 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -666,10 +666,11 @@ impl Repr for subst::Substs { impl Repr for subst::VecPerParamSpace { fn repr(&self, tcx: &ctxt) -> String { - format!("[{};{};{}]", - self.get_slice(subst::TypeSpace).repr(tcx), - self.get_slice(subst::SelfSpace).repr(tcx), - self.get_slice(subst::FnSpace).repr(tcx)) + format!("[{};{};{};{}]", + self.get_slice(subst::TypeSpace).repr(tcx), + self.get_slice(subst::SelfSpace).repr(tcx), + self.get_slice(subst::AssocSpace).repr(tcx), + self.get_slice(subst::FnSpace).repr(tcx)) } } diff --git a/src/test/compile-fail/variance-regions-direct.rs b/src/test/compile-fail/variance-regions-direct.rs index fa38482b21c50..48813ff142c18 100644 --- a/src/test/compile-fail/variance-regions-direct.rs +++ b/src/test/compile-fail/variance-regions-direct.rs @@ -14,7 +14,7 @@ // Regions that just appear in normal spots are contravariant: #[rustc_variance] -struct Test2<'a, 'b, 'c> { //~ ERROR regions=[[-, -, -];[];[]] +struct Test2<'a, 'b, 'c> { //~ ERROR regions=[[-, -, -];[];[];[]] x: &'a int, y: &'b [int], c: &'c str @@ -23,7 +23,7 @@ struct Test2<'a, 'b, 'c> { //~ ERROR regions=[[-, -, -];[];[]] // Those same annotations in function arguments become covariant: #[rustc_variance] -struct Test3<'a, 'b, 'c> { //~ ERROR regions=[[+, +, +];[];[]] +struct Test3<'a, 'b, 'c> { //~ ERROR regions=[[+, +, +];[];[];[]] x: extern "Rust" fn(&'a int), y: extern "Rust" fn(&'b [int]), c: extern "Rust" fn(&'c str), @@ -32,7 +32,7 @@ struct Test3<'a, 'b, 'c> { //~ ERROR regions=[[+, +, +];[];[]] // Mutability induces invariance: #[rustc_variance] -struct Test4<'a, 'b:'a> { //~ ERROR regions=[[-, o];[];[]] +struct Test4<'a, 'b:'a> { //~ ERROR regions=[[-, o];[];[];[]] x: &'a mut &'b int, } @@ -40,7 +40,7 @@ struct Test4<'a, 'b:'a> { //~ ERROR regions=[[-, o];[];[]] // contravariant context: #[rustc_variance] -struct Test5<'a, 'b> { //~ ERROR regions=[[+, o];[];[]] +struct Test5<'a, 'b> { //~ ERROR regions=[[+, o];[];[];[]] x: extern "Rust" fn(&'a mut &'b int), } @@ -50,21 +50,21 @@ struct Test5<'a, 'b> { //~ ERROR regions=[[+, o];[];[]] // argument list occurs in an invariant context. #[rustc_variance] -struct Test6<'a, 'b> { //~ ERROR regions=[[-, o];[];[]] +struct Test6<'a, 'b> { //~ ERROR regions=[[-, o];[];[];[]] x: &'a mut extern "Rust" fn(&'b int), } // No uses at all is bivariant: #[rustc_variance] -struct Test7<'a> { //~ ERROR regions=[[*];[];[]] +struct Test7<'a> { //~ ERROR regions=[[*];[];[];[]] x: int } // Try enums too. #[rustc_variance] -enum Test8<'a, 'b, 'c:'b> { //~ ERROR regions=[[+, -, o];[];[]] +enum Test8<'a, 'b, 'c:'b> { //~ ERROR regions=[[+, -, o];[];[];[]] Test8A(extern "Rust" fn(&'a int)), Test8B(&'b [int]), Test8C(&'b mut &'c str), diff --git a/src/test/compile-fail/variance-regions-indirect.rs b/src/test/compile-fail/variance-regions-indirect.rs index c049fbc0fedbc..0e8e52df456af 100644 --- a/src/test/compile-fail/variance-regions-indirect.rs +++ b/src/test/compile-fail/variance-regions-indirect.rs @@ -13,29 +13,29 @@ // Try enums too. #[rustc_variance] -enum Base<'a, 'b, 'c:'b, 'd> { //~ ERROR regions=[[+, -, o, *];[];[]] +enum Base<'a, 'b, 'c:'b, 'd> { //~ ERROR regions=[[+, -, o, *];[];[];[]] Test8A(extern "Rust" fn(&'a int)), Test8B(&'b [int]), Test8C(&'b mut &'c str), } #[rustc_variance] -struct Derived1<'w, 'x:'y, 'y, 'z> { //~ ERROR regions=[[*, o, -, +];[];[]] +struct Derived1<'w, 'x:'y, 'y, 'z> { //~ ERROR regions=[[*, o, -, +];[];[];[]] f: Base<'z, 'y, 'x, 'w> } #[rustc_variance] // Combine - and + to yield o -struct Derived2<'a, 'b:'a, 'c> { //~ ERROR regions=[[o, o, *];[];[]] +struct Derived2<'a, 'b:'a, 'c> { //~ ERROR regions=[[o, o, *];[];[];[]] f: Base<'a, 'a, 'b, 'c> } #[rustc_variance] // Combine + and o to yield o (just pay attention to 'a here) -struct Derived3<'a:'b, 'b, 'c> { //~ ERROR regions=[[o, -, *];[];[]] +struct Derived3<'a:'b, 'b, 'c> { //~ ERROR regions=[[o, -, *];[];[];[]] f: Base<'a, 'b, 'a, 'c> } #[rustc_variance] // Combine + and * to yield + (just pay attention to 'a here) -struct Derived4<'a, 'b, 'c:'b> { //~ ERROR regions=[[+, -, o];[];[]] +struct Derived4<'a, 'b, 'c:'b> { //~ ERROR regions=[[+, -, o];[];[];[]] f: Base<'a, 'b, 'c, 'a> } diff --git a/src/test/compile-fail/variance-trait-object-bound.rs b/src/test/compile-fail/variance-trait-object-bound.rs index c61f2ff79c019..c576c5e2edd64 100644 --- a/src/test/compile-fail/variance-trait-object-bound.rs +++ b/src/test/compile-fail/variance-trait-object-bound.rs @@ -19,7 +19,7 @@ use std::mem; trait T { fn foo(); } #[rustc_variance] -struct TOption<'a> { //~ ERROR regions=[[-];[];[]] +struct TOption<'a> { //~ ERROR regions=[[-];[];[];[]] v: Option>, } From fb9d0ccc2fe4d81c0ff2ecf8d42c59413e24fa38 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 31 Oct 2014 06:36:07 -0400 Subject: [PATCH 26/34] Move associated types into the Assoc space and add in the builtin bounds from the definition (including Sized). --- src/librustc/middle/subst.rs | 3 +- src/librustc/middle/traits/select.rs | 1 + src/librustc/middle/ty.rs | 27 +- src/librustc/middle/typeck/astconv.rs | 51 +++- src/librustc/middle/typeck/collect.rs | 278 ++++++++---------- src/librustc/middle/typeck/infer/mod.rs | 11 +- src/librustc/util/ppaux.rs | 57 +++- .../compile-fail/associated-types-unsized.rs | 24 ++ 8 files changed, 243 insertions(+), 209 deletions(-) create mode 100644 src/test/compile-fail/associated-types-unsized.rs diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index c322b4aae7ef0..bf93743998119 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -118,10 +118,11 @@ impl Substs { pub fn new_trait(t: Vec, r: Vec, + a: Vec, s: ty::t) -> Substs { - Substs::new(VecPerParamSpace::new(t, vec!(s), Vec::new(), Vec::new()), + Substs::new(VecPerParamSpace::new(t, vec!(s), a, Vec::new()), VecPerParamSpace::new(r, Vec::new(), Vec::new(), Vec::new())) } diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index a665e1f0a264e..abc36359a8564 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -1683,6 +1683,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { vec![arguments_tuple.subst(self.tcx(), substs), new_signature.output.unwrap().subst(self.tcx(), substs)], vec![], + vec![], obligation.self_ty()) }); diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index b9af31665a113..8211fec505b9f 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -573,10 +573,6 @@ pub struct ctxt<'tcx> { /// Maps def IDs to true if and only if they're associated types. pub associated_types: RefCell>, - /// Maps def IDs of traits to information about their associated types. - pub trait_associated_types: - RefCell>>>, - /// Caches the results of trait selection. This cache is used /// for things that do not have to do with the parameters in scope. pub selection_cache: traits::SelectionCache, @@ -1564,7 +1560,6 @@ pub fn mk_ctxt<'tcx>(s: Session, stability: RefCell::new(stability), capture_modes: capture_modes, associated_types: RefCell::new(DefIdMap::new()), - trait_associated_types: RefCell::new(DefIdMap::new()), selection_cache: traits::SelectionCache::new(), repr_hint_cache: RefCell::new(DefIdMap::new()), } @@ -1994,6 +1989,16 @@ impl ItemSubsts { } } +impl ParamBounds { + pub fn empty() -> ParamBounds { + ParamBounds { + builtin_bounds: empty_builtin_bounds(), + trait_bounds: Vec::new(), + region_bounds: Vec::new(), + } + } +} + // Type utilities pub fn type_is_nil(ty: t) -> bool { @@ -4155,18 +4160,6 @@ impl Ord for AssociatedTypeInfo { } } -/// Returns the associated types belonging to the given trait, in parameter -/// order. -pub fn associated_types_for_trait(cx: &ctxt, trait_id: ast::DefId) - -> Rc> { - cx.trait_associated_types - .borrow() - .find(&trait_id) - .expect("associated_types_for_trait(): trait not found, try calling \ - ensure_associated_types()") - .clone() -} - pub fn trait_item_def_ids(cx: &ctxt, id: ast::DefId) -> Rc> { lookup_locally_or_in_crate_store("trait_item_def_ids", diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index 66d4f73eacc87..951ac795d80b2 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -54,7 +54,7 @@ use middle::def; use middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem}; use middle::lang_items::{FnOnceTraitLangItem}; use middle::resolve_lifetime as rl; -use middle::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs}; +use middle::subst::{FnSpace, TypeSpace, AssocSpace, SelfSpace, Subst, Substs}; use middle::subst::{VecPerParamSpace}; use middle::ty; use middle::typeck::lookup_def_tcx; @@ -215,7 +215,8 @@ fn ast_path_substs<'tcx,AC,RS>( associated_ty: Option, path: &ast::Path) -> Substs - where AC: AstConv<'tcx>, RS: RegionScope { + where AC: AstConv<'tcx>, RS: RegionScope +{ /*! * Given a path `path` that refers to an item `I` with the * declared generics `decl_generics`, returns an appropriate @@ -338,17 +339,21 @@ fn ast_path_substs<'tcx,AC,RS>( substs.types.push(TypeSpace, default); } None => { - // This is an associated type. - substs.types.push( - TypeSpace, - this.associated_type_binding(path.span, - associated_ty, - decl_def_id, - param.def_id)) + tcx.sess.span_bug(path.span, + "extra parameter without default"); } } } + for param in decl_generics.types.get_slice(AssocSpace).iter() { + substs.types.push( + AssocSpace, + this.associated_type_binding(path.span, + associated_ty, + decl_def_id, + param.def_id)) + } + substs } @@ -628,9 +633,13 @@ fn mk_pointer<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( a_seq_ty: &ast::Ty, ptr_ty: PointerTy, constr: |ty::t| -> ty::t) - -> ty::t { + -> ty::t +{ let tcx = this.tcx(); - debug!("mk_pointer(ptr_ty={})", ptr_ty); + + debug!("mk_pointer(ptr_ty={}, a_seq_ty={})", + ptr_ty, + a_seq_ty.repr(tcx)); match a_seq_ty.node { ast::TyVec(ref ty) => { @@ -730,7 +739,13 @@ fn associated_ty_to_ty<'tcx,AC,RS>(this: &AC, trait_type_id: ast::DefId, span: Span) -> ty::t - where AC: AstConv<'tcx>, RS: RegionScope { + where AC: AstConv<'tcx>, RS: RegionScope +{ + debug!("associated_ty_to_ty(trait_path={}, for_ast_type={}, trait_type_id={})", + trait_path.repr(this.tcx()), + for_ast_type.repr(this.tcx()), + trait_type_id.repr(this.tcx())); + // Find the trait that this associated type belongs to. let trait_did = match ty::impl_or_trait_item(this.tcx(), trait_type_id).container() { @@ -757,9 +772,16 @@ fn associated_ty_to_ty<'tcx,AC,RS>(this: &AC, None, Some(for_type), trait_path); + + debug!("associated_ty_to_ty(trait_ref={})", + trait_ref.repr(this.tcx())); + let trait_def = this.get_trait_def(trait_did); for type_parameter in trait_def.generics.types.iter() { if type_parameter.def_id == trait_type_id { + debug!("associated_ty_to_ty(type_parameter={} substs={})", + type_parameter.repr(this.tcx()), + trait_ref.substs.repr(this.tcx())); return *trait_ref.substs.types.get(type_parameter.space, type_parameter.index) } @@ -772,7 +794,10 @@ fn associated_ty_to_ty<'tcx,AC,RS>(this: &AC, // Parses the programmer's textual representation of a type into our // internal notion of a type. pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( - this: &AC, rscope: &RS, ast_ty: &ast::Ty) -> ty::t { + this: &AC, rscope: &RS, ast_ty: &ast::Ty) -> ty::t +{ + debug!("ast_ty_to_ty(ast_ty={})", + ast_ty.repr(this.tcx())); let tcx = this.tcx(); diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index 66c311e4d664b..863f09736ab27 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -305,6 +305,12 @@ fn collect_trait_methods(ccx: &CrateCtxt, } }); + debug!("ty_method_of_trait_method yielded {} \ + for method {} of trait {}", + ty_method.repr(ccx.tcx), + trait_item.repr(ccx.tcx), + local_def(trait_id).repr(ccx.tcx)); + make_method_ty(ccx, &*ty_method); tcx.impl_or_trait_items @@ -460,7 +466,7 @@ fn convert_associated_type(ccx: &CrateCtxt, // associated type. let type_parameter_def = trait_def.generics .types - .get_slice(subst::TypeSpace) + .get_slice(subst::AssocSpace) .iter() .find(|def| { def.def_id == local_def(associated_type.ty_param.id) @@ -475,7 +481,7 @@ fn convert_associated_type(ccx: &CrateCtxt, } }; let param_type = ty::mk_param(ccx.tcx, - subst::TypeSpace, + type_parameter_def.space, type_parameter_def.index, local_def(associated_type.ty_param.id)); ccx.tcx.tcache.borrow_mut().insert(local_def(associated_type.ty_param.id), @@ -780,25 +786,18 @@ impl<'a,'tcx> AstConv<'tcx> for ImplCtxt<'a,'tcx> { ty: Option, trait_id: ast::DefId, associated_type_id: ast::DefId) - -> ty::t { - ensure_associated_types(self, trait_id); - let associated_type_ids = ty::associated_types_for_trait(self.ccx.tcx, - trait_id); + -> ty::t + { + let trait_def = ty::lookup_trait_def(self.tcx(), trait_id); match self.opt_trait_ref_id { Some(trait_ref_id) if trait_ref_id == trait_id => { // It's an associated type on the trait that we're // implementing. - let associated_type_id = - associated_type_ids.iter() - .find(|id| { - id.def_id == associated_type_id - }) - .expect("associated_type_binding(): \ - expected associated type ID \ - in trait"); - let associated_type = - ty::impl_or_trait_item(self.ccx.tcx, - associated_type_id.def_id); + assert!(trait_def.generics.types + .get_slice(subst::AssocSpace) + .iter() + .any(|type_param_def| type_param_def.def_id == associated_type_id)); + let associated_type = ty::impl_or_trait_item(self.ccx.tcx, associated_type_id); for impl_item in self.impl_items.iter() { match *impl_item { ast::MethodImplItem(_) => {} @@ -980,7 +979,7 @@ impl<'a,'tcx> AstConv<'tcx> for TraitMethodCtxt<'a,'tcx> { ast::TypeTraitItem(ref item) => { if local_def(item.ty_param.id) == associated_type_id { return ty::mk_param(self.tcx(), - subst::TypeSpace, + subst::AssocSpace, index, associated_type_id) } @@ -1451,7 +1450,8 @@ pub fn trait_def_of_item(ccx: &CrateCtxt, it: &ast::Item) -> Rc { trait_id: ast::NodeId, generics: &ast::Generics, items: &[ast::TraitItem]) - -> subst::Substs { + -> subst::Substs + { // Creates a no-op substitution for the trait's type parameters. let regions = generics.lifetimes @@ -1464,7 +1464,7 @@ pub fn trait_def_of_item(ccx: &CrateCtxt, it: &ast::Item) -> Rc { .collect(); // Start with the generics in the type parameters... - let mut types: Vec<_> = + let types: Vec<_> = generics.ty_params .iter() .enumerate() @@ -1472,24 +1472,27 @@ pub fn trait_def_of_item(ccx: &CrateCtxt, it: &ast::Item) -> Rc { i, local_def(def.id))) .collect(); - // ...and add generics synthesized from the associated types. - for item in items.iter() { - match *item { + // ...and also create generics synthesized from the associated types. + let assoc_types: Vec<_> = + items.iter() + .flat_map(|item| match *item { ast::TypeTraitItem(ref trait_item) => { let index = types.len(); - types.push(ty::mk_param(ccx.tcx, - subst::TypeSpace, - index, - local_def(trait_item.ty_param.id))) + Some(ty::mk_param(ccx.tcx, + subst::AssocSpace, + index, + local_def(trait_item.ty_param.id))).into_iter() } - ast::RequiredMethod(_) | ast::ProvidedMethod(_) => {} - } - } + ast::RequiredMethod(_) | ast::ProvidedMethod(_) => { + None.into_iter() + } + }) + .collect(); let self_ty = ty::mk_param(ccx.tcx, subst::SelfSpace, 0, local_def(trait_id)); - subst::Substs::new_trait(types, regions, self_ty) + subst::Substs::new_trait(types, regions, assoc_types, self_ty) } } @@ -1680,14 +1683,14 @@ fn ty_generics_for_trait(ccx: &CrateCtxt, let def = get_or_create_type_parameter_def( ccx, - subst::TypeSpace, + subst::AssocSpace, &associated_type.ty_param, generics.types.len(subst::TypeSpace), &ast_generics.where_clause, Some(local_def(trait_id))); ccx.tcx.ty_param_defs.borrow_mut().insert(associated_type.ty_param.id, def.clone()); - generics.types.push(subst::TypeSpace, def); + generics.types.push(subst::AssocSpace, def); } ast::ProvidedMethod(_) | ast::RequiredMethod(_) => {} } @@ -1786,86 +1789,17 @@ enum CreateTypeParametersForAssociatedTypesFlag { CreateTypeParametersForAssociatedTypes, } -fn ensure_associated_types<'tcx,AC>(this: &AC, trait_id: ast::DefId) - where AC: AstConv<'tcx> { - if this.tcx().trait_associated_types.borrow().contains_key(&trait_id) { - return - } - - if trait_id.krate == ast::LOCAL_CRATE { - match this.tcx().map.find(trait_id.node) { - Some(ast_map::NodeItem(item)) => { - match item.node { - ast::ItemTrait(_, _, _, ref trait_items) => { - let mut result = Vec::new(); - let mut index = 0; - for trait_item in trait_items.iter() { - match *trait_item { - ast::RequiredMethod(_) | - ast::ProvidedMethod(_) => {} - ast::TypeTraitItem(ref associated_type) => { - let info = ty::AssociatedTypeInfo { - def_id: local_def(associated_type.ty_param.id), - index: index, - name: associated_type.ty_param.ident.name, - }; - result.push(info); - index += 1; - } - } - } - this.tcx() - .trait_associated_types - .borrow_mut() - .insert(trait_id, Rc::new(result)); - return - } - _ => { - this.tcx().sess.bug("ensure_associated_types() \ - called on non-trait") - } - } - } - _ => { - this.tcx().sess.bug("ensure_associated_types() called on \ - non-trait") - } - } - - } - - // Cross-crate case. - let mut result = Vec::new(); - let mut index = 0; - let trait_items = ty::trait_items(this.tcx(), trait_id); - for trait_item in trait_items.iter() { - match *trait_item { - ty::MethodTraitItem(_) => {} - ty::TypeTraitItem(ref associated_type) => { - let info = ty::AssociatedTypeInfo { - def_id: associated_type.def_id, - index: index, - name: associated_type.name - }; - result.push(info); - index += 1; - } - } - } - this.tcx().trait_associated_types.borrow_mut().insert(trait_id, - Rc::new(result)); -} - fn ty_generics<'tcx,AC>(this: &AC, space: subst::ParamSpace, lifetime_defs: &[ast::LifetimeDef], types: &[ast::TyParam], base_generics: ty::Generics, where_clause: &ast::WhereClause, - create_type_parameters_for_associated_types: + create_type_parameters_for_associated_types_flag: CreateTypeParametersForAssociatedTypesFlag) -> ty::Generics - where AC: AstConv<'tcx> { + where AC: AstConv<'tcx> +{ let mut result = base_generics; for (i, l) in lifetime_defs.iter().enumerate() { @@ -1886,62 +1820,11 @@ fn ty_generics<'tcx,AC>(this: &AC, // First, create the virtual type parameters for associated types if // necessary. let mut associated_types_generics = ty::Generics::empty(); - match create_type_parameters_for_associated_types { + match create_type_parameters_for_associated_types_flag { DontCreateTypeParametersForAssociatedTypes => {} CreateTypeParametersForAssociatedTypes => { - let mut index = 0; - for param in types.iter() { - for bound in param.bounds.iter() { - match *bound { - ast::TraitTyParamBound(ref trait_bound) => { - match lookup_def_tcx(this.tcx(), - trait_bound.path.span, - trait_bound.ref_id) { - def::DefTrait(trait_did) => { - ensure_associated_types(this, trait_did); - let associated_types = - ty::associated_types_for_trait( - this.tcx(), - trait_did); - for associated_type_info in - associated_types.iter() { - let associated_type_trait_item = - ty::impl_or_trait_item( - this.tcx(), - associated_type_info.def_id); - let def = ty::TypeParameterDef { - name: associated_type_trait_item.name(), - def_id: associated_type_info.def_id, - space: space, - index: types.len() + index, - bounds: ty::ParamBounds { - builtin_bounds: - ty::empty_builtin_bounds(), - trait_bounds: Vec::new(), - region_bounds: Vec::new(), - }, - associated_with: { - Some(local_def(param.id)) - }, - default: None, - }; - associated_types_generics.types - .push(space, - def); - index += 1; - } - } - _ => { - this.tcx().sess.span_bug(trait_bound.path - .span, - "not a trait?!") - } - } - } - _ => {} - } - } - } + create_type_parameters_for_associated_types(this, space, types, + &mut associated_types_generics); } } @@ -1976,6 +1859,83 @@ fn ty_generics<'tcx,AC>(this: &AC, } return result; + + fn create_type_parameters_for_associated_types<'tcx,AC>( + this: &AC, + space: subst::ParamSpace, + types: &[ast::TyParam], + associated_types_generics: &mut ty::Generics) + where AC: AstConv<'tcx> + { + // The idea here is roughly as follows. We start with + // an item that is paramerized by various type parameters + // with bounds: + // + // fn foo(t: T) { ... } + // + // The traits in those bounds declare associated types: + // + // trait Iterator { type Elem; ... } + // + // And we rewrite the original function so that every associated + // type is bound to some fresh type parameter: + // + // fn foo>(t: T) { ... } + + // Number of synthetic type parameters created thus far + let mut index = 0; + + // Iterate over the each type parameter `T` (from the example) + for param in types.iter() { + // Iterate over the bound `Iterator` + for bound in param.bounds.iter() { + // In the above example, `ast_trait_ref` is `Iterator`. + let ast_trait_ref = match *bound { + ast::TraitTyParamBound(ref r) => r, + ast::UnboxedFnTyParamBound(..) => { continue; } + ast::RegionTyParamBound(..) => { continue; } + }; + + let trait_def_id = + match lookup_def_tcx(this.tcx(), + ast_trait_ref.path.span, + ast_trait_ref.ref_id) { + def::DefTrait(trait_def_id) => trait_def_id, + _ => { + this.tcx().sess.span_bug(ast_trait_ref.path.span, + "not a trait?!") + } + }; + + // trait_def_id is def-id of `Iterator` + let trait_def = ty::lookup_trait_def(this.tcx(), trait_def_id); + let associated_type_defs = trait_def.generics.types.get_slice(subst::AssocSpace); + + // Iterate over each associated type `Elem` + for associated_type_def in associated_type_defs.iter() { + // Create the fresh type parameter `A` + let def = ty::TypeParameterDef { + name: associated_type_def.name, + def_id: associated_type_def.def_id, + space: space, + index: types.len() + index, + bounds: ty::ParamBounds { + builtin_bounds: associated_type_def.bounds.builtin_bounds, + + // FIXME(#18178) -- we should add the other bounds, but + // that requires subst and more logic + trait_bounds: Vec::new(), + region_bounds: Vec::new(), + }, + associated_with: Some(local_def(param.id)), + default: None, + }; + associated_types_generics.types.push(space, def); + index += 1; + } + } + } + } } fn get_or_create_type_parameter_def<'tcx,AC>(this: &AC, diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index 227a9b1bdcc88..55c3c23685357 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -774,10 +774,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { assert!(generics.regions.len(subst::FnSpace) == 0); let type_parameter_count = generics.types.len(subst::TypeSpace); + let type_parameters = self.next_ty_vars(type_parameter_count); + let region_param_defs = generics.regions.get_slice(subst::TypeSpace); let regions = self.region_vars_for_defs(span, region_param_defs); - let type_parameters = self.next_ty_vars(type_parameter_count); - subst::Substs::new_trait(type_parameters, regions, self_ty) + + let assoc_type_parameter_count = generics.types.len(subst::AssocSpace); + let assoc_type_parameters = self.next_ty_vars(assoc_type_parameter_count); + + subst::Substs::new_trait(type_parameters, regions, assoc_type_parameters, self_ty) } pub fn fresh_bound_region(&self, binder_id: ast::NodeId) -> ty::Region { @@ -791,7 +796,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn ty_to_string(&self, t: ty::t) -> String { ty_to_string(self.tcx, - self.resolve_type_vars_if_possible(t)) + self.resolve_type_vars_if_possible(t)) } pub fn tys_to_string(&self, ts: &[ty::t]) -> String { diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 1d769572ffee2..c4f988580308f 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -423,7 +423,13 @@ pub fn ty_to_string(cx: &ctxt, typ: t) -> String { } ty_infer(infer_ty) => infer_ty_to_string(cx, infer_ty), ty_err => "[type error]".to_string(), - ty_param(ref param_ty) => param_ty.repr(cx), + ty_param(ref param_ty) => { + if cx.sess.verbose() { + param_ty.repr(cx) + } else { + param_ty.user_string(cx) + } + } ty_enum(did, ref substs) | ty_struct(did, ref substs) => { let base = ty::item_path_str(cx, did); let generics = ty::lookup_item_type(cx, did).generics; @@ -479,6 +485,17 @@ pub fn parameterized(cx: &ctxt, generics: &ty::Generics) -> String { + if cx.sess.verbose() { + if substs.is_noop() { + return format!("{}", base); + } else { + return format!("{}<{},{}>", + base, + substs.regions.repr(cx), + substs.types.repr(cx)); + } + } + let mut strs = Vec::new(); match substs.regions { @@ -503,7 +520,7 @@ pub fn parameterized(cx: &ctxt, let tps = substs.types.get_slice(subst::TypeSpace); let ty_params = generics.types.get_slice(subst::TypeSpace); let has_defaults = ty_params.last().map_or(false, |def| def.default.is_some()); - let num_defaults = if has_defaults && !cx.sess.verbose() { + let num_defaults = if has_defaults { ty_params.iter().zip(tps.iter()).rev().take_while(|&(def, &actual)| { match def.default { Some(default) => default.subst(cx, substs) == actual, @@ -518,18 +535,6 @@ pub fn parameterized(cx: &ctxt, strs.push(ty_to_string(cx, *t)) } - if cx.sess.verbose() { - for t in substs.types.get_slice(subst::SelfSpace).iter() { - strs.push(format!("self {}", t.repr(cx))); - } - - // generally there shouldn't be any substs in the fn param - // space, but in verbose mode, print them out. - for t in substs.types.get_slice(subst::FnSpace).iter() { - strs.push(format!("fn {}", t.repr(cx))); - } - } - if strs.len() > 0u { format!("{}<{}>", base, strs.connect(", ")) } else { @@ -725,7 +730,7 @@ impl Repr for ty::TraitRef { fn repr(&self, tcx: &ctxt) -> String { let base = ty::item_path_str(tcx, self.def_id); let trait_def = ty::lookup_trait_def(tcx, self.def_id); - format!("<{} as {}>", + format!("<{} : {}>", self.substs.self_ty().repr(tcx), parameterized(tcx, base.as_slice(), &self.substs, &trait_def.generics)) } @@ -740,6 +745,19 @@ impl Repr for ty::TraitDef { } } +impl Repr for ast::TraitItem { + fn repr(&self, _tcx: &ctxt) -> String { + match *self { + ast::RequiredMethod(ref data) => format!("RequiredMethod({}, id={})", + data.ident, data.id), + ast::ProvidedMethod(ref data) => format!("ProvidedMethod(id={})", + data.id), + ast::TypeTraitItem(ref data) => format!("TypeTraitItem({}, id={})", + data.ty_param.ident, data.ty_param.id), + } + } +} + impl Repr for ast::Expr { fn repr(&self, _tcx: &ctxt) -> String { format!("expr({}: {})", self.id, pprust::expr_to_string(self)) @@ -758,6 +776,12 @@ impl UserString for ast::Path { } } +impl Repr for ast::Ty { + fn repr(&self, _tcx: &ctxt) -> String { + format!("type({})", pprust::ty_to_string(self)) + } +} + impl Repr for ast::Item { fn repr(&self, tcx: &ctxt) -> String { format!("item({})", tcx.map.node_to_string(self.id)) @@ -1261,7 +1285,8 @@ impl UserString for ParamTy { impl Repr for ParamTy { fn repr(&self, tcx: &ctxt) -> String { - self.user_string(tcx) + let ident = self.user_string(tcx); + format!("{}/{}.{}", ident, self.space, self.idx) } } diff --git a/src/test/compile-fail/associated-types-unsized.rs b/src/test/compile-fail/associated-types-unsized.rs new file mode 100644 index 0000000000000..47ab09d279f62 --- /dev/null +++ b/src/test/compile-fail/associated-types-unsized.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(associated_types)] + +trait Get { + type Sized? Value; + fn get(&self) -> ::Value; +} + +fn foo(t: T) { + let x = t.get(); //~ ERROR the trait `core::kinds::Sized` is not implemented +} + +fn main() { +} + From 3c84e317210d0eaa9014179c9a998449571ad8aa Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 3 Nov 2014 17:36:30 -0500 Subject: [PATCH 27/34] Use a struct rather than a 4-tuple --- src/librustc/middle/subst.rs | 33 +++++++++++++++++++++++-------- src/librustc/middle/trans/meth.rs | 23 +++++++++++++++++---- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index bf93743998119..a7120080eed3b 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -282,6 +282,17 @@ pub struct VecPerParamSpace { content: Vec, } +/** + * The `split` function converts one `VecPerParamSpace` into this + * `SeparateVecsPerParamSpace` structure. + */ +pub struct SeparateVecsPerParamSpace { + pub types: Vec, + pub selfs: Vec, + pub assocs: Vec, + pub fns: Vec, +} + impl fmt::Show for VecPerParamSpace { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { try!(write!(fmt, "VecPerParamSpace {{")); @@ -464,24 +475,30 @@ impl VecPerParamSpace { } pub fn map_move(self, pred: |T| -> U) -> VecPerParamSpace { - let (t, s, a, f) = self.split(); + let SeparateVecsPerParamSpace { + types: t, + selfs: s, + assocs: a, + fns: f + } = self.split(); + VecPerParamSpace::new(t.into_iter().map(|p| pred(p)).collect(), s.into_iter().map(|p| pred(p)).collect(), a.into_iter().map(|p| pred(p)).collect(), f.into_iter().map(|p| pred(p)).collect()) } - pub fn split(self) -> (Vec, Vec, Vec, Vec) { + pub fn split(self) -> SeparateVecsPerParamSpace { let VecPerParamSpace { type_limit, self_limit, assoc_limit, content } = self; let mut content_iter = content.into_iter(); - let types = content_iter.by_ref().take(type_limit).collect(); - let selfs = content_iter.by_ref().take(self_limit - type_limit).collect(); - let assocs = content_iter.by_ref().take(assoc_limit - self_limit).collect(); - let fns = content_iter.collect(); - - (types, selfs, assocs, fns) + SeparateVecsPerParamSpace { + types: content_iter.by_ref().take(type_limit).collect(), + selfs: content_iter.by_ref().take(self_limit - type_limit).collect(), + assocs: content_iter.by_ref().take(assoc_limit - self_limit).collect(), + fns: content_iter.collect() + } } pub fn with_vec(mut self, space: ParamSpace, vec: Vec) diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index e54d50c8b5f59..fbd4db959ce0c 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -205,7 +205,12 @@ pub fn trans_static_method_callee(bcx: Block, // type parameters that belong to the trait but also some that // belong to the method: let rcvr_substs = node_id_substs(bcx, ExprId(expr_id)); - let (rcvr_type, rcvr_self, rcvr_assoc, rcvr_method) = rcvr_substs.types.split(); + let subst::SeparateVecsPerParamSpace { + types: rcvr_type, + selfs: rcvr_self, + assocs: rcvr_assoc, + fns: rcvr_method + } = rcvr_substs.types.split(); // Lookup the precise impl being called. To do that, we need to // create a trait reference identifying the self type and other @@ -266,7 +271,12 @@ pub fn trans_static_method_callee(bcx: Block, // that with the `rcvr_method` from before, which tells us // the type parameters from the *method*, to yield // `callee_substs=[[T=int],[],[U=String]]`. - let (impl_type, impl_self, impl_assoc, _) = impl_substs.types.split(); + let subst::SeparateVecsPerParamSpace { + types: impl_type, + selfs: impl_self, + assocs: impl_assoc, + fns: _ + } = impl_substs.types.split(); let callee_substs = Substs::erased(VecPerParamSpace::new(impl_type, impl_self, @@ -399,8 +409,13 @@ fn combine_impl_and_methods_tps(bcx: Block, // Break apart the type parameters from the node and type // parameters from the receiver. - let (_, _, _, node_method) = node_substs.types.split(); - let (rcvr_type, rcvr_self, rcvr_assoc, rcvr_method) = rcvr_substs.types.clone().split(); + let node_method = node_substs.types.split().fns; + let subst::SeparateVecsPerParamSpace { + types: rcvr_type, + selfs: rcvr_self, + assocs: rcvr_assoc, + fns: rcvr_method + } = rcvr_substs.types.clone().split(); assert!(rcvr_method.is_empty()); subst::Substs { regions: subst::ErasedRegions, From 2a7fb3584c974d6bf09182d454a457515aca228b Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 3 Nov 2014 10:53:49 -0500 Subject: [PATCH 28/34] syntax: Use UFCS in the expansion of `#[deriving(Clone)]` --- src/libsyntax/ext/deriving/clone.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/libsyntax/ext/deriving/clone.rs b/src/libsyntax/ext/deriving/clone.rs index 9748b53134577..e653c8aebf447 100644 --- a/src/libsyntax/ext/deriving/clone.rs +++ b/src/libsyntax/ext/deriving/clone.rs @@ -52,11 +52,19 @@ fn cs_clone( name: &str, cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P { - let clone_ident = substr.method_ident; let ctor_ident; let all_fields; - let subcall = |field: &FieldInfo| - cx.expr_method_call(field.span, field.self_.clone(), clone_ident, Vec::new()); + let fn_path = vec![ + cx.ident_of("std"), + cx.ident_of("clone"), + cx.ident_of("Clone"), + cx.ident_of("clone"), + ]; + let subcall = |field: &FieldInfo| { + let args = vec![cx.expr_addr_of(field.span, field.self_.clone())]; + + cx.expr_call_global(field.span, fn_path.clone(), args) + }; match *substr.fields { Struct(ref af) => { From 8d5208ad8504609f3eddac2491514db8029e48ca Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 3 Nov 2014 11:15:29 -0500 Subject: [PATCH 29/34] core: Fix fallout of changing `#[deriving(Clone)]` --- src/libcore/result.rs | 1 - src/libcore/str.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/libcore/result.rs b/src/libcore/result.rs index 82da972f68a35..5b75e98baefe2 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -276,7 +276,6 @@ #![stable] -use clone::Clone; use cmp::PartialEq; use std::fmt::Show; use slice; diff --git a/src/libcore/str.rs b/src/libcore/str.rs index 3b29c25787292..8937f2a946a85 100644 --- a/src/libcore/str.rs +++ b/src/libcore/str.rs @@ -19,7 +19,6 @@ use mem; use char; use char::Char; -use clone::Clone; use cmp; use cmp::{PartialEq, Eq}; use default::Default; From 07bbde8932a5b3b8ea0c6d07f7db8315ce633dfc Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 3 Nov 2014 11:27:11 -0500 Subject: [PATCH 30/34] unicode: Fix fallout of changing `#[deriving(Clone)]` --- src/libunicode/u_str.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libunicode/u_str.rs b/src/libunicode/u_str.rs index 4bad631798fb2..9e3830c1f6090 100644 --- a/src/libunicode/u_str.rs +++ b/src/libunicode/u_str.rs @@ -17,7 +17,6 @@ * methods provided by the UnicodeChar trait. */ -use core::clone::Clone; use core::cmp; use core::slice::ImmutableSlice; use core::iter::{Filter, AdditiveIterator, Iterator, DoubleEndedIterator}; From 6d951b2cbdaf336dd63ce060eabd5d4e544b0fdc Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 3 Nov 2014 11:52:13 -0500 Subject: [PATCH 31/34] std: Fix fallout of changing `#[deriving(Clone)]` --- src/libstd/collections/hash/set.rs | 1 - src/libstd/io/process.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/libstd/collections/hash/set.rs b/src/libstd/collections/hash/set.rs index 823bd49d7a663..b9758e11bc784 100644 --- a/src/libstd/collections/hash/set.rs +++ b/src/libstd/collections/hash/set.rs @@ -10,7 +10,6 @@ // // ignore-lexer-test FIXME #15883 -use clone::Clone; use cmp::{Eq, Equiv, PartialEq}; use core::kinds::Sized; use default::Default; diff --git a/src/libstd/io/process.rs b/src/libstd/io/process.rs index 312a4c41ac9a6..493e1b559d7b7 100644 --- a/src/libstd/io/process.rs +++ b/src/libstd/io/process.rs @@ -26,7 +26,6 @@ use rt::rtio; use c_str::CString; use collections::HashMap; use hash::Hash; -use clone::Clone; #[cfg(windows)] use std::hash::sip::SipState; From b8fad35ddae081611d9db486de960f9a23371917 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 3 Nov 2014 10:55:43 -0500 Subject: [PATCH 32/34] Add test for second half of issue #15689 cc #15689 --- src/test/run-pass/issue-15689-2.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/test/run-pass/issue-15689-2.rs diff --git a/src/test/run-pass/issue-15689-2.rs b/src/test/run-pass/issue-15689-2.rs new file mode 100644 index 0000000000000..026122d1259f6 --- /dev/null +++ b/src/test/run-pass/issue-15689-2.rs @@ -0,0 +1,16 @@ +// 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. + +#[deriving(Clone)] +enum Test<'a> { + Slice(&'a int) +} + +fn main() {} From 03b568a846e71d2efac88325534c2142b6c25453 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 3 Nov 2014 12:51:21 -0500 Subject: [PATCH 33/34] Fix error message in compile-fail test --- src/test/compile-fail/deriving-no-inner-impl-error-message.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/compile-fail/deriving-no-inner-impl-error-message.rs b/src/test/compile-fail/deriving-no-inner-impl-error-message.rs index 58593869d747b..15a7bc01c3ae7 100644 --- a/src/test/compile-fail/deriving-no-inner-impl-error-message.rs +++ b/src/test/compile-fail/deriving-no-inner-impl-error-message.rs @@ -17,7 +17,8 @@ struct E { } #[deriving(Clone)] struct C { - x: NoCloneOrEq //~ ERROR does not implement any method in scope named `clone` + x: NoCloneOrEq + //~^ ERROR the trait `core::clone::Clone` is not implemented for the type `NoCloneOrEq` } From e7f310970867061ae1eb8d95ce7a674560533857 Mon Sep 17 00:00:00 2001 From: Falco Hirschenberger Date: Tue, 4 Nov 2014 00:48:03 +0100 Subject: [PATCH 34/34] Fix for bitshift errors lint on cross compilation #18587 --- src/librustc/lint/builtin.rs | 14 +-- .../compile-fail/lint-exceeding-bitshifts.rs | 89 +++++++++++-------- 2 files changed, 58 insertions(+), 45 deletions(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 76ef6206d6416..1f39cb0132c89 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -39,7 +39,7 @@ use std::cmp; use std::collections::HashMap; use std::collections::hashmap::{Occupied, Vacant}; use std::slice; -use std::{int, i8, i16, i32, i64, uint, u8, u16, u32, u64, f32, f64}; +use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64}; use syntax::abi; use syntax::ast_map; use syntax::ast_util::is_shift_binop; @@ -180,8 +180,8 @@ impl LintPass for TypeLimits { if is_shift_binop(binop) { let opt_ty_bits = match ty::get(ty::expr_ty(cx.tcx, &**l)).sty { - ty::ty_int(t) => Some(int_ty_bits(t)), - ty::ty_uint(t) => Some(uint_ty_bits(t)), + ty::ty_int(t) => Some(int_ty_bits(t, cx.sess().targ_cfg.int_type)), + ty::ty_uint(t) => Some(uint_ty_bits(t, cx.sess().targ_cfg.uint_type)), _ => None }; @@ -312,9 +312,9 @@ impl LintPass for TypeLimits { } } - fn int_ty_bits(int_ty: ast::IntTy) -> u64 { + fn int_ty_bits(int_ty: ast::IntTy, target_int_ty: ast::IntTy) -> u64 { match int_ty { - ast::TyI => int::BITS as u64, + ast::TyI => int_ty_bits(target_int_ty, target_int_ty), ast::TyI8 => i8::BITS as u64, ast::TyI16 => i16::BITS as u64, ast::TyI32 => i32::BITS as u64, @@ -322,9 +322,9 @@ impl LintPass for TypeLimits { } } - fn uint_ty_bits(uint_ty: ast::UintTy) -> u64 { + fn uint_ty_bits(uint_ty: ast::UintTy, target_uint_ty: ast::UintTy) -> u64 { match uint_ty { - ast::TyU => uint::BITS as u64, + ast::TyU => uint_ty_bits(target_uint_ty, target_uint_ty), ast::TyU8 => u8::BITS as u64, ast::TyU16 => u16::BITS as u64, ast::TyU32 => u32::BITS as u64, diff --git a/src/test/compile-fail/lint-exceeding-bitshifts.rs b/src/test/compile-fail/lint-exceeding-bitshifts.rs index f270994bd38ef..6de2ad8a0f3dc 100644 --- a/src/test/compile-fail/lint-exceeding-bitshifts.rs +++ b/src/test/compile-fail/lint-exceeding-bitshifts.rs @@ -10,49 +10,62 @@ #![deny(exceeding_bitshifts)] #![allow(unused_variables)] +#![allow(dead_code)] fn main() { - let n = 1u8 << 8; - let n = 1u8 << 9; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1u16 << 16; - let n = 1u16 << 17; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1u32 << 32; - let n = 1u32 << 33; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1u64 << 64; - let n = 1u64 << 65; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1i8 << 8; - let n = 1i8 << 9; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1i16 << 16; - let n = 1i16 << 17; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1i32 << 32; - let n = 1i32 << 33; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1i64 << 64; - let n = 1i64 << 65; //~ ERROR: bitshift exceeds the type's number of bits - - let n = 1u8 >> 8; - let n = 1u8 >> 9; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1u16 >> 16; - let n = 1u16 >> 17; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1u32 >> 32; - let n = 1u32 >> 33; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1u64 >> 64; - let n = 1u64 >> 65; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1i8 >> 8; - let n = 1i8 >> 9; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1i16 >> 16; - let n = 1i16 >> 17; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1i32 >> 32; - let n = 1i32 >> 33; //~ ERROR: bitshift exceeds the type's number of bits - let n = 1i64 >> 64; - let n = 1i64 >> 65; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u8 << 7; + let n = 1u8 << 8; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u16 << 15; + let n = 1u16 << 16; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u32 << 31; + let n = 1u32 << 32; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u64 << 63; + let n = 1u64 << 64; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i8 << 7; + let n = 1i8 << 8; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i16 << 15; + let n = 1i16 << 16; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i32 << 31; + let n = 1i32 << 32; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i64 << 63; + let n = 1i64 << 64; //~ ERROR: bitshift exceeds the type's number of bits + + let n = 1u8 >> 7; + let n = 1u8 >> 8; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u16 >> 15; + let n = 1u16 >> 16; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u32 >> 31; + let n = 1u32 >> 32; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u64 >> 63; + let n = 1u64 >> 64; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i8 >> 7; + let n = 1i8 >> 8; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i16 >> 15; + let n = 1i16 >> 16; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i32 >> 31; + let n = 1i32 >> 32; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i64 >> 63; + let n = 1i64 >> 64; //~ ERROR: bitshift exceeds the type's number of bits let n = 1u8; - let n = n << 8; - let n = n << 9; //~ ERROR: bitshift exceeds the type's number of bits + let n = n << 7; + let n = n << 8; //~ ERROR: bitshift exceeds the type's number of bits + + let n = 1u8 << -8; //~ ERROR: bitshift exceeds the type's number of bits + + let n = 1u8 << (4+3); + let n = 1u8 << (4+4); //~ ERROR: bitshift exceeds the type's number of bits - let n = 1u8 << -9; //~ ERROR: bitshift exceeds the type's number of bits + #[cfg(target_word_size = "32")] + fn dead_but_still_linted() { + let n = 1i << 32; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u << 32; //~ ERROR: bitshift exceeds the type's number of bits + } - let n = 1u8 << (4+4); - let n = 1u8 << (4+5); //~ ERROR: bitshift exceeds the type's number of bits + #[cfg(target_word_size = "64")] + fn dead_but_still_still_linted() { + let n = 1i << 64; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u << 64; //~ ERROR: bitshift exceeds the type's number of bits + } }