From f2c0ecded5f831eb03c187a2b13f04e959f5e3a3 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 14 Jan 2016 15:03:48 +0100 Subject: [PATCH 01/23] enable cross crate and unsafe const fn --- src/librustc/middle/const_eval.rs | 69 +++---------------- src/test/compile-fail/const-call.rs | 5 -- .../const-fn-stability-calls-2.rs | 22 ------ src/test/run-pass/const-fn-cross-crate.rs | 1 + src/test/run-pass/const-fn.rs | 6 ++ 5 files changed, 18 insertions(+), 85 deletions(-) delete mode 100644 src/test/compile-fail/const-fn-stability-calls-2.rs diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index eae2aa9cb7e73..5a07124792d9c 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -27,7 +27,7 @@ use util::num::ToPrimitive; use util::nodemap::NodeMap; use graphviz::IntoCow; -use syntax::{ast, abi}; +use syntax::ast; use rustc_front::hir::Expr; use rustc_front::hir; use rustc_front::intravisit::FnKind; @@ -1089,19 +1089,16 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>, hir::ExprCall(ref callee, ref args) => { let sub_ty_hint = ty_hint.erase_hint(); let callee_val = try!(eval_const_expr_partial(tcx, callee, sub_ty_hint, fn_args)); - let (decl, block, constness) = try!(get_fn_def(tcx, e, callee_val)); - match (ty_hint, constness) { - (ExprTypeChecked, _) => { - // no need to check for constness... either check_const - // already forbids this or we const eval over whatever - // we want - }, - (_, hir::Constness::Const) => { - // we don't know much about the function, so we force it to be a const fn - // so compilation will fail later in case the const fn's body is not const - }, - _ => signal!(e, NonConstPath), - } + let did = match callee_val { + Function(did) => did, + callee => signal!(e, CallOn(callee)), + }; + let (decl, result) = if let Some(fn_like) = lookup_const_fn_by_id(tcx, did) { + (fn_like.decl(), &fn_like.body().expr) + } else { + signal!(e, NonConstPath) + }; + let result = result.as_ref().expect("const fn has no result expression"); assert_eq!(decl.inputs.len(), args.len()); let mut call_args = NodeMap(); @@ -1116,7 +1113,6 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>, let old = call_args.insert(arg.pat.id, arg_val); assert!(old.is_none()); } - let result = block.expr.as_ref().unwrap(); debug!("const call({:?})", call_args); try!(eval_const_expr_partial(tcx, &**result, ty_hint, Some(&call_args))) }, @@ -1397,46 +1393,3 @@ pub fn compare_lit_exprs<'tcx>(tcx: &ty::ctxt<'tcx>, }; compare_const_vals(&a, &b) } - - -// returns Err if callee is not `Function` -// `e` is only used for error reporting/spans -fn get_fn_def<'a>(tcx: &'a ty::ctxt, - e: &hir::Expr, - callee: ConstVal) - -> Result<(&'a hir::FnDecl, &'a hir::Block, hir::Constness), ConstEvalErr> { - let did = match callee { - Function(did) => did, - callee => signal!(e, CallOn(callee)), - }; - debug!("fn call: {:?}", tcx.map.get_if_local(did)); - match tcx.map.get_if_local(did) { - None => signal!(e, UnimplementedConstVal("calling non-local const fn")), // non-local - Some(ast_map::NodeItem(it)) => match it.node { - hir::ItemFn( - ref decl, - hir::Unsafety::Normal, - constness, - abi::Abi::Rust, - _, // ducktype generics? types are funky in const_eval - ref block, - ) => Ok((&**decl, &**block, constness)), - _ => signal!(e, NonConstPath), - }, - Some(ast_map::NodeImplItem(it)) => match it.node { - hir::ImplItemKind::Method( - hir::MethodSig { - ref decl, - unsafety: hir::Unsafety::Normal, - constness, - abi: abi::Abi::Rust, - .. // ducktype generics? types are funky in const_eval - }, - ref block, - ) => Ok((decl, block, constness)), - _ => signal!(e, NonConstPath), - }, - Some(ast_map::NodeTraitItem(..)) => signal!(e, NonConstPath), - Some(_) => signal!(e, UnimplementedConstVal("calling struct, tuple or variant")), - } -} diff --git a/src/test/compile-fail/const-call.rs b/src/test/compile-fail/const-call.rs index d49da47a87c83..1143d3bd5cd96 100644 --- a/src/test/compile-fail/const-call.rs +++ b/src/test/compile-fail/const-call.rs @@ -10,15 +10,10 @@ #![feature(const_fn)] -const unsafe fn g(x: usize) -> usize { - x -} - fn f(x: usize) -> usize { x } fn main() { let _ = [0; f(2)]; //~ ERROR: non-constant path in constant expression [E0307] - let _ = [0; g(2)]; //~ ERROR: non-constant path in constant expression [E0307] } diff --git a/src/test/compile-fail/const-fn-stability-calls-2.rs b/src/test/compile-fail/const-fn-stability-calls-2.rs deleted file mode 100644 index 592a312d80048..0000000000000 --- a/src/test/compile-fail/const-fn-stability-calls-2.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2015 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 use of const fn from another crate without a feature gate. - -// aux-build:const_fn_lib.rs - -extern crate const_fn_lib; - -use const_fn_lib::foo; - -fn main() { - let x: [usize; foo()] = []; - //~^ ERROR unimplemented constant expression: calling non-local const fn [E0250] -} diff --git a/src/test/run-pass/const-fn-cross-crate.rs b/src/test/run-pass/const-fn-cross-crate.rs index 5d0c17af71719..7b4b751fd7f25 100644 --- a/src/test/run-pass/const-fn-cross-crate.rs +++ b/src/test/run-pass/const-fn-cross-crate.rs @@ -22,4 +22,5 @@ const FOO: usize = foo(); fn main() { assert_eq!(FOO, 22); + let _: [i32; foo()] = [42; 22]; } diff --git a/src/test/run-pass/const-fn.rs b/src/test/run-pass/const-fn.rs index 38c73febc3108..5961ed8d3390d 100644 --- a/src/test/run-pass/const-fn.rs +++ b/src/test/run-pass/const-fn.rs @@ -20,14 +20,20 @@ const fn sub(x: u32, y: u32) -> u32 { x - y } +const unsafe fn div(x: u32, y: u32) -> u32 { + x / y +} + const SUM: u32 = add(44, 22); const DIFF: u32 = sub(44, 22); +const DIV: u32 = unsafe{div(44, 22)}; fn main() { assert_eq!(SUM, 66); assert!(SUM != 88); assert_eq!(DIFF, 22); + assert_eq!(DIV, 2); let _: [&'static str; sub(100, 99) as usize] = ["hi"]; } From b1b6b33c6dbae1c72eebc50ba86a267704c2fade Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Wed, 20 Jan 2016 17:15:03 +1300 Subject: [PATCH 02/23] Some basic error correction in the parser after a dot --- src/libsyntax/parse/parser.rs | 104 ++++++++++++++++++++-------------- 1 file changed, 62 insertions(+), 42 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index bfa42e761294b..34b94b883a4dc 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2355,6 +2355,55 @@ impl<'a> Parser<'a> { ) } + // Assuming we have just parsed `.foo` (i.e., a dot and an ident), continue + // parsing into an expression. + fn parse_dot_suffix(&mut self, ident: Ident, ident_span: Span, self_value: P) -> PResult<'a, P> { + let (_, tys, bindings) = if self.eat(&token::ModSep) { + try!(self.expect_lt()); + try!(self.parse_generic_values_after_lt()) + } else { + (Vec::new(), Vec::new(), Vec::new()) + }; + + if !bindings.is_empty() { + let last_span = self.last_span; + self.span_err(last_span, "type bindings are only permitted on trait paths"); + } + + let lo = self_value.span.lo; + + Ok(match self.token { + // expr.f() method call. + token::OpenDelim(token::Paren) => { + let mut es = try!(self.parse_unspanned_seq( + &token::OpenDelim(token::Paren), + &token::CloseDelim(token::Paren), + seq_sep_trailing_allowed(token::Comma), + |p| Ok(try!(p.parse_expr())) + )); + let hi = self.last_span.hi; + + es.insert(0, self_value); + let id = spanned(ident_span.lo, ident_span.hi, ident); + let nd = self.mk_method_call(id, tys, es); + self.mk_expr(lo, hi, nd, None) + } + // Field access. + _ => { + if !tys.is_empty() { + let last_span = self.last_span; + self.span_err(last_span, + "field expressions may not \ + have type parameters"); + } + + let id = spanned(ident_span.lo, ident_span.hi, ident); + let field = self.mk_field(self_value, id); + self.mk_expr(lo, ident_span.hi, field, None) + } + }) + } + fn parse_dot_or_call_expr_with_(&mut self, e0: P) -> PResult<'a, P> { let mut e = e0; let lo = e.span.lo; @@ -2364,50 +2413,11 @@ impl<'a> Parser<'a> { if self.eat(&token::Dot) { match self.token { token::Ident(i, _) => { - let dot = self.last_span.hi; + let dot_pos = self.last_span.hi; hi = self.span.hi; self.bump(); - let (_, tys, bindings) = if self.eat(&token::ModSep) { - try!(self.expect_lt()); - try!(self.parse_generic_values_after_lt()) - } else { - (Vec::new(), Vec::new(), Vec::new()) - }; - - if !bindings.is_empty() { - let last_span = self.last_span; - self.span_err(last_span, "type bindings are only permitted on trait paths"); - } - // expr.f() method call - match self.token { - token::OpenDelim(token::Paren) => { - let mut es = try!(self.parse_unspanned_seq( - &token::OpenDelim(token::Paren), - &token::CloseDelim(token::Paren), - seq_sep_trailing_allowed(token::Comma), - |p| Ok(try!(p.parse_expr())) - )); - hi = self.last_span.hi; - - es.insert(0, e); - let id = spanned(dot, hi, i); - let nd = self.mk_method_call(id, tys, es); - e = self.mk_expr(lo, hi, nd, None); - } - _ => { - if !tys.is_empty() { - let last_span = self.last_span; - self.span_err(last_span, - "field expressions may not \ - have type parameters"); - } - - let id = spanned(dot, hi, i); - let field = self.mk_field(e, id); - e = self.mk_expr(lo, hi, field, None); - } - } + e = try!(self.parse_dot_suffix(i, mk_sp(dot_pos, hi), e)); } token::Literal(token::Integer(n), suf) => { let sp = self.span; @@ -2452,7 +2462,17 @@ impl<'a> Parser<'a> { self.abort_if_errors(); } - _ => return self.unexpected() + _ => { + // TODO special case lifetime + // FIXME Could factor this out into non_fatal_unexpected or something. + let actual = self.this_token_to_string(); + self.span_err(self.span, &format!("unexpected token: `{}`", actual)); + + let dot_pos = self.last_span.hi; + e = try!(self.parse_dot_suffix(special_idents::invalid, + mk_sp(dot_pos, dot_pos), + e)); + } } continue; } From 0ac8915875596db90167701c447d9c76396358bb Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Wed, 20 Jan 2016 22:07:33 +1300 Subject: [PATCH 03/23] The war on abort_if_errors --- src/librustc/middle/cstore.rs | 8 +--- src/librustc/middle/lang_items.rs | 1 - src/librustc/session/mod.rs | 7 ++-- src/librustc_driver/driver.rs | 64 ++++++++++++++++--------------- src/librustc_metadata/creader.rs | 19 ++++----- src/librustc_passes/const_fn.rs | 5 ++- src/librustc_resolve/lib.rs | 2 - src/libsyntax/errors/mod.rs | 7 +++- src/libsyntax/ext/expand.rs | 7 +++- src/libsyntax/parse/mod.rs | 20 ++++------ src/libsyntax/parse/parser.rs | 7 +++- 11 files changed, 73 insertions(+), 74 deletions(-) diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index 27745a85935ab..756d708732a4e 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -270,8 +270,8 @@ pub fn validate_crate_name(sess: Option<&Session>, s: &str, sp: Option) { let say = |s: &str| { match (sp, sess) { (_, None) => panic!("{}", s), - (Some(sp), Some(sess)) => sess.span_err(sp, s), - (None, Some(sess)) => sess.err(s), + (Some(sp), Some(sess)) => sess.span_fatal(sp, s), + (None, Some(sess)) => sess.fatal(s), } }; if s.is_empty() { @@ -282,10 +282,6 @@ pub fn validate_crate_name(sess: Option<&Session>, s: &str, sp: Option) { if c == '_' { continue } say(&format!("invalid character `{}` in crate name: `{}`", c, s)); } - match sess { - Some(sess) => sess.abort_if_errors(), - None => {} - } } /// A dummy crate store that does not support any non-local crates, diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index ec55daca9ecdf..6e57d5dd1ba8d 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -239,7 +239,6 @@ pub fn collect_language_items(session: &Session, collector.collect(krate); let LanguageItemCollector { mut items, .. } = collector; weak_lang_items::check_crate(krate, session, &mut items); - session.abort_if_errors(); items } diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 2f3af1c0d09b5..975ec0e709b7d 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -176,14 +176,15 @@ impl Session { pub fn abort_if_errors(&self) { self.diagnostic().abort_if_errors(); } - pub fn abort_if_new_errors(&self, mut f: F) - where F: FnMut() + pub fn abort_if_new_errors(&self, f: F) -> T + where F: FnOnce() -> T { let count = self.err_count(); - f(); + let result = f(); if self.err_count() > count { self.abort_if_errors(); } + result } pub fn span_warn(&self, sp: Span, msg: &str) { self.diagnostic().span_warn(sp, msg) diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index fd5f711c9d639..1db04033f940e 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -69,7 +69,6 @@ pub fn compile_input(sess: Session, let state = $make_state; (control.$point.callback)(state); - $tsess.abort_if_errors(); if control.$point.stop == Compilation::Stop { return; } @@ -481,13 +480,15 @@ pub fn phase_2_configure_and_expand(sess: &Session, }); time(time_passes, "gated macro checking", || { - let features = syntax::feature_gate::check_crate_macros(sess.codemap(), - &sess.parse_sess.span_diagnostic, - &krate); - - // these need to be set "early" so that expansion sees `quote` if enabled. - *sess.features.borrow_mut() = features; - sess.abort_if_errors(); + sess.abort_if_new_errors(|| { + let features = + syntax::feature_gate::check_crate_macros(sess.codemap(), + &sess.parse_sess.span_diagnostic, + &krate); + + // these need to be set "early" so that expansion sees `quote` if enabled. + *sess.features.borrow_mut() = features; + }); }); @@ -525,7 +526,7 @@ pub fn phase_2_configure_and_expand(sess: &Session, let Registry { syntax_exts, early_lint_passes, late_lint_passes, lint_groups, llvm_passes, attributes, .. } = registry; - { + sess.abort_if_new_errors(|| { let mut ls = sess.lint_store.borrow_mut(); for pass in early_lint_passes { ls.register_early_pass(Some(sess), true, pass); @@ -540,17 +541,14 @@ pub fn phase_2_configure_and_expand(sess: &Session, *sess.plugin_llvm_passes.borrow_mut() = llvm_passes; *sess.plugin_attributes.borrow_mut() = attributes.clone(); - } + }); // Lint plugins are registered; now we can process command line flags. if sess.opts.describe_lints { super::describe_lints(&*sess.lint_store.borrow(), true); return None; } - sess.lint_store.borrow_mut().process_command_line(sess); - - // Abort if there are errors from lint processing or a plugin registrar. - sess.abort_if_errors(); + sess.abort_if_new_errors(|| sess.lint_store.borrow_mut().process_command_line(sess)); krate = time(time_passes, "expansion", || { // Windows dlls do not have rpaths, so they don't know how to find their @@ -594,13 +592,14 @@ pub fn phase_2_configure_and_expand(sess: &Session, // much as possible (e.g. help the programmer avoid platform // specific differences) time(time_passes, "complete gated feature checking 1", || { - let features = syntax::feature_gate::check_crate(sess.codemap(), - &sess.parse_sess.span_diagnostic, - &krate, - &attributes, - sess.opts.unstable_features); - *sess.features.borrow_mut() = features; - sess.abort_if_errors(); + sess.abort_if_new_errors(|| { + let features = syntax::feature_gate::check_crate(sess.codemap(), + &sess.parse_sess.span_diagnostic, + &krate, + &attributes, + sess.opts.unstable_features); + *sess.features.borrow_mut() = features; + }); }); // JBC: make CFG processing part of expansion to avoid this problem: @@ -639,13 +638,14 @@ pub fn phase_2_configure_and_expand(sess: &Session, // later, to make sure we've got everything (e.g. configuration // can insert new attributes via `cfg_attr`) time(time_passes, "complete gated feature checking 2", || { - let features = syntax::feature_gate::check_crate(sess.codemap(), - &sess.parse_sess.span_diagnostic, - &krate, - &attributes, - sess.opts.unstable_features); - *sess.features.borrow_mut() = features; - sess.abort_if_errors(); + sess.abort_if_new_errors(|| { + let features = syntax::feature_gate::check_crate(sess.codemap(), + &sess.parse_sess.span_diagnostic, + &krate, + &attributes, + sess.opts.unstable_features); + *sess.features.borrow_mut() = features; + }); }); time(time_passes, @@ -711,9 +711,11 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, "external crate/lib resolution", || LocalCrateReader::new(sess, cstore, &hir_map).read_crates(krate)); - let lang_items = time(time_passes, - "language item collection", - || middle::lang_items::collect_language_items(&sess, &hir_map)); + let lang_items = time(time_passes, "language item collection", || { + sess.abort_if_new_errors(|| { + middle::lang_items::collect_language_items(&sess, &hir_map) + }) + }); let resolve::CrateMap { def_map, diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 9122148a8cc05..9c75007a8db76 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -258,15 +258,14 @@ impl<'a> CrateReader<'a> { metadata: &MetadataBlob) { let crate_rustc_version = decoder::crate_rustc_version(metadata.as_slice()); if crate_rustc_version != Some(rustc_version()) { - span_err!(self.sess, span, E0514, - "the crate `{}` has been compiled with {}, which is \ - incompatible with this version of rustc", - name, - crate_rustc_version - .as_ref().map(|s|&**s) - .unwrap_or("an old version of rustc") + span_fatal!(self.sess, span, E0514, + "the crate `{}` has been compiled with {}, which is \ + incompatible with this version of rustc", + name, + crate_rustc_version + .as_ref().map(|s|&**s) + .unwrap_or("an old version of rustc") ); - self.sess.abort_if_errors(); } } @@ -511,7 +510,6 @@ impl<'a> CrateReader<'a> { } }; let span = mk_sp(lo, p.last_span.hi); - p.abort_if_errors(); // Mark the attrs as used for attr in &attrs { @@ -554,8 +552,7 @@ impl<'a> CrateReader<'a> { name, config::host_triple(), self.sess.opts.target_triple); - span_err!(self.sess, span, E0456, "{}", &message[..]); - self.sess.abort_if_errors(); + span_fatal!(self.sess, span, E0456, "{}", &message[..]); } let registrar = diff --git a/src/librustc_passes/const_fn.rs b/src/librustc_passes/const_fn.rs index cda5267f7271b..f422a47572b8d 100644 --- a/src/librustc_passes/const_fn.rs +++ b/src/librustc_passes/const_fn.rs @@ -18,8 +18,9 @@ use syntax::visit::{self, Visitor, FnKind}; use syntax::codemap::Span; pub fn check_crate(sess: &Session, krate: &ast::Crate) { - visit::walk_crate(&mut CheckConstFn{ sess: sess }, krate); - sess.abort_if_errors(); + sess.abort_if_new_errors(|| { + visit::walk_crate(&mut CheckConstFn{ sess: sess }, krate); + }); } struct CheckConstFn<'a> { diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 444c43163e3c3..fb4eb61ceb4d4 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -4019,10 +4019,8 @@ pub fn create_resolver<'a, 'tcx>(session: &'a Session, resolver.callback = callback; build_reduced_graph::build_reduced_graph(&mut resolver, krate); - session.abort_if_errors(); resolve_imports::resolve_imports(&mut resolver); - session.abort_if_errors(); resolver } diff --git a/src/libsyntax/errors/mod.rs b/src/libsyntax/errors/mod.rs index 6983c74696ac8..a7a4ddc3b2a63 100644 --- a/src/libsyntax/errors/mod.rs +++ b/src/libsyntax/errors/mod.rs @@ -555,6 +555,9 @@ impl Handler { pub enum Level { Bug, Fatal, + // An error which while not immediately fatal, should stop the compiler + // progressing beyond the current phase. + PhaseFatal, Error, Warning, Note, @@ -573,7 +576,7 @@ impl fmt::Display for Level { impl Level { fn color(self) -> term::color::Color { match self { - Bug | Fatal | Error => term::color::BRIGHT_RED, + Bug | Fatal | PhaseFatal | Error => term::color::BRIGHT_RED, Warning => term::color::BRIGHT_YELLOW, Note => term::color::BRIGHT_GREEN, Help => term::color::BRIGHT_CYAN, @@ -584,7 +587,7 @@ impl Level { fn to_str(self) -> &'static str { match self { Bug => "error: internal compiler error", - Fatal | Error => "error", + Fatal | PhaseFatal | Error => "error", Warning => "warning", Note => "note", Help => "help", diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 5f27bdfc98a41..72537f6c7b26a 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1304,9 +1304,14 @@ pub fn expand_crate(mut cx: ExtCtxt, expander.cx.syntax_env.insert(name, extension); } + let err_count = cx.parse_sess.span_diagnostic.err_count(); let mut ret = expander.fold_crate(c); ret.exported_macros = expander.cx.exported_macros.clone(); - cx.parse_sess.span_diagnostic.abort_if_errors(); + + if cx.parse_sess.span_diagnostic.err_count() > err_count { + cx.parse_sess.span_diagnostic.abort_if_errors(); + } + ret }; return (ret, cx.syntax_env.names); diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 090b070433f46..32372ccc13b47 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -98,7 +98,7 @@ pub fn parse_crate_from_source_str(name: String, cfg, name, source); - maybe_aborted(panictry!(p.parse_crate_mod()),p) + panictry!(p.parse_crate_mod()) } pub fn parse_crate_attrs_from_source_str(name: String, @@ -110,7 +110,7 @@ pub fn parse_crate_attrs_from_source_str(name: String, cfg, name, source); - maybe_aborted(panictry!(p.parse_inner_attributes()), p) + panictry!(p.parse_inner_attributes()) } pub fn parse_expr_from_source_str(name: String, @@ -119,7 +119,7 @@ pub fn parse_expr_from_source_str(name: String, sess: &ParseSess) -> P { let mut p = new_parser_from_source_str(sess, cfg, name, source); - maybe_aborted(panictry!(p.parse_expr()), p) + panictry!(p.parse_expr()) } pub fn parse_item_from_source_str(name: String, @@ -128,7 +128,7 @@ pub fn parse_item_from_source_str(name: String, sess: &ParseSess) -> Option> { let mut p = new_parser_from_source_str(sess, cfg, name, source); - maybe_aborted(panictry!(p.parse_item()), p) + panictry!(p.parse_item()) } pub fn parse_meta_from_source_str(name: String, @@ -137,7 +137,7 @@ pub fn parse_meta_from_source_str(name: String, sess: &ParseSess) -> P { let mut p = new_parser_from_source_str(sess, cfg, name, source); - maybe_aborted(panictry!(p.parse_meta_item()), p) + panictry!(p.parse_meta_item()) } pub fn parse_stmt_from_source_str(name: String, @@ -151,7 +151,7 @@ pub fn parse_stmt_from_source_str(name: String, name, source ); - maybe_aborted(panictry!(p.parse_stmt()), p) + panictry!(p.parse_stmt()) } // Warning: This parses with quote_depth > 0, which is not the default. @@ -168,7 +168,7 @@ pub fn parse_tts_from_source_str(name: String, ); p.quote_depth += 1; // right now this is re-creating the token trees from ... token trees. - maybe_aborted(panictry!(p.parse_all_token_trees()),p) + panictry!(p.parse_all_token_trees()) } // Create a new parser from a source string @@ -265,16 +265,10 @@ pub fn tts_to_parser<'a>(sess: &'a ParseSess, p } -/// Abort if necessary -pub fn maybe_aborted(result: T, p: Parser) -> T { - p.abort_if_errors(); - result -} fn abort_if_errors<'a, T>(result: PResult<'a, T>, p: &Parser) -> T { match result { Ok(c) => { - p.abort_if_errors(); c } Err(mut e) => { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 34b94b883a4dc..acce6ed87d00b 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2357,7 +2357,11 @@ impl<'a> Parser<'a> { // Assuming we have just parsed `.foo` (i.e., a dot and an ident), continue // parsing into an expression. - fn parse_dot_suffix(&mut self, ident: Ident, ident_span: Span, self_value: P) -> PResult<'a, P> { + fn parse_dot_suffix(&mut self, + ident: Ident, + ident_span: Span, + self_value: P) + -> PResult<'a, P> { let (_, tys, bindings) = if self.eat(&token::ModSep) { try!(self.expect_lt()); try!(self.parse_generic_values_after_lt()) @@ -2463,7 +2467,6 @@ impl<'a> Parser<'a> { } _ => { - // TODO special case lifetime // FIXME Could factor this out into non_fatal_unexpected or something. let actual = self.this_token_to_string(); self.span_err(self.span, &format!("unexpected token: `{}`", actual)); From 585cf6fb5f0c2736c781b6efb4651ba6d317a753 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Wed, 20 Jan 2016 22:29:47 +1300 Subject: [PATCH 04/23] Prevent missing idents from causing problems down the line --- src/librustc_resolve/lib.rs | 4 ++++ src/librustc_typeck/check/mod.rs | 18 +++++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index fb4eb61ceb4d4..e30fafda63cb4 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -3037,6 +3037,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { check_ribs: bool, record_used: bool) -> Option { + if identifier.name == special_idents::invalid.name { + return Some(LocalDef::from_def(DefErr)); + } + // First, check to see whether the name is a primitive type. if namespace == TypeNS { if let Some(&prim_ty) = self.primitive_type_table diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index f49b25df66e87..922ebb3683e80 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -121,7 +121,7 @@ use syntax::attr; use syntax::attr::AttrMetaMethods; use syntax::codemap::{self, Span, Spanned}; use syntax::errors::DiagnosticBuilder; -use syntax::parse::token::{self, InternedString}; +use syntax::parse::token::{self, InternedString, special_idents}; use syntax::ptr::P; use syntax::util::lev_distance::find_best_match_for_name; @@ -2839,8 +2839,10 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, method_ty } Err(error) => { - method::report_error(fcx, method_name.span, expr_t, - method_name.node, Some(rcvr), error); + if method_name.node != special_idents::invalid.name { + method::report_error(fcx, method_name.span, expr_t, + method_name.node, Some(rcvr), error); + } fcx.write_error(expr.id); fcx.tcx().types.err } @@ -2938,6 +2940,11 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, None => {} } + if field.node == special_idents::invalid.name { + fcx.write_error(expr.id); + return; + } + if method::exists(fcx, field.span, field.node, expr_t, expr.id) { fcx.type_error_struct(field.span, |actual| { @@ -3788,8 +3795,9 @@ pub fn resolve_ty_and_def_ufcs<'a, 'b, 'tcx>(fcx: &FnCtxt<'b, 'tcx>, Some((Some(ty), slice::ref_slice(item_segment), def)) } Err(error) => { - method::report_error(fcx, span, ty, - item_name, None, error); + if item_name != special_idents::invalid.name { + method::report_error(fcx, span, ty, item_name, None, error); + } fcx.write_error(node_id); None } From f3b525fb97c48976c740da50a38ec8c9e5f29b7e Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Thu, 21 Jan 2016 12:16:59 +1300 Subject: [PATCH 05/23] test fallout --- src/librustc_driver/driver.rs | 33 ++++++++++++------- src/librustc_typeck/check/mod.rs | 13 +++----- src/test/compile-fail/cfg-non-opt-expr.rs | 2 ++ src/test/compile-fail/double-type-import.rs | 2 +- src/test/compile-fail/import-from-missing.rs | 1 + src/test/compile-fail/import.rs | 2 +- src/test/compile-fail/import2.rs | 3 +- .../macro-reexport-malformed-1.rs | 3 +- .../macro-reexport-malformed-2.rs | 3 +- .../macro-reexport-malformed-3.rs | 3 +- src/test/compile-fail/macro-reexport-undef.rs | 2 ++ src/test/compile-fail/macro-use-bad-args-1.rs | 5 ++- src/test/compile-fail/macro-use-bad-args-2.rs | 5 ++- src/test/compile-fail/privacy3.rs | 1 + src/test/compile-fail/self_type_keyword.rs | 1 + src/test/compile-fail/use-mod.rs | 1 + 16 files changed, 46 insertions(+), 34 deletions(-) diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 1db04033f940e..36074a7ae0225 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -70,6 +70,7 @@ pub fn compile_input(sess: Session, (control.$point.callback)(state); if control.$point.stop == Compilation::Stop { + $tsess.abort_if_errors(); return; } })} @@ -469,7 +470,11 @@ pub fn phase_2_configure_and_expand(sess: &Session, let mut feature_gated_cfgs = vec![]; krate = time(time_passes, "configuration 1", || { - syntax::config::strip_unconfigured_items(sess.diagnostic(), krate, &mut feature_gated_cfgs) + sess.abort_if_new_errors(|| { + syntax::config::strip_unconfigured_items(sess.diagnostic(), + krate, + &mut feature_gated_cfgs) + }) }); *sess.crate_types.borrow_mut() = collect_crate_types(sess, &krate.attrs); @@ -605,17 +610,23 @@ pub fn phase_2_configure_and_expand(sess: &Session, // JBC: make CFG processing part of expansion to avoid this problem: // strip again, in case expansion added anything with a #[cfg]. - krate = time(time_passes, "configuration 2", || { - syntax::config::strip_unconfigured_items(sess.diagnostic(), krate, &mut feature_gated_cfgs) - }); + krate = sess.abort_if_new_errors(|| { + let krate = time(time_passes, "configuration 2", || { + syntax::config::strip_unconfigured_items(sess.diagnostic(), + krate, + &mut feature_gated_cfgs) + }); - time(time_passes, "gated configuration checking", || { - let features = sess.features.borrow(); - feature_gated_cfgs.sort(); - feature_gated_cfgs.dedup(); - for cfg in &feature_gated_cfgs { - cfg.check_and_emit(sess.diagnostic(), &features, sess.codemap()); - } + time(time_passes, "gated configuration checking", || { + let features = sess.features.borrow(); + feature_gated_cfgs.sort(); + feature_gated_cfgs.dedup(); + for cfg in &feature_gated_cfgs { + cfg.check_and_emit(sess.diagnostic(), &features, sess.codemap()); + } + }); + + krate }); krate = time(time_passes, "maybe building test harness", || { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 922ebb3683e80..a8697f45d9156 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -4229,7 +4229,9 @@ pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, } // Check for unrepresentable discriminant values match hint { - attr::ReprAny | attr::ReprExtern => (), + attr::ReprAny | attr::ReprExtern => { + disr_vals.push(current_disr_val); + } attr::ReprInt(sp, ity) => { if !disr_in_range(ccx, ity, current_disr_val) { let mut err = struct_span_err!(ccx.tcx.sess, v.span, E0082, @@ -4239,14 +4241,9 @@ pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, err.emit(); } } - attr::ReprSimd => { - ccx.tcx.sess.bug("range_to_inttype: found ReprSimd on an enum"); - } - attr::ReprPacked => { - ccx.tcx.sess.bug("range_to_inttype: found ReprPacked on an enum"); - } + // Error reported elsewhere. + attr::ReprSimd | attr::ReprPacked => {} } - disr_vals.push(current_disr_val); } } diff --git a/src/test/compile-fail/cfg-non-opt-expr.rs b/src/test/compile-fail/cfg-non-opt-expr.rs index d9d379ddc7dd5..b3ef3d72ca3bf 100644 --- a/src/test/compile-fail/cfg-non-opt-expr.rs +++ b/src/test/compile-fail/cfg-non-opt-expr.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(stmt_expr_attributes)] + fn main() { let _ = #[cfg(unset)] (); //~^ ERROR removing an expression is not supported in this position diff --git a/src/test/compile-fail/double-type-import.rs b/src/test/compile-fail/double-type-import.rs index 923f95e69d122..d6d7dbb4aecd8 100644 --- a/src/test/compile-fail/double-type-import.rs +++ b/src/test/compile-fail/double-type-import.rs @@ -20,5 +20,5 @@ mod foo { } fn main() { - let _ = foo::X; + let _ = foo::X; //~ ERROR unresolved name `foo::X` } diff --git a/src/test/compile-fail/import-from-missing.rs b/src/test/compile-fail/import-from-missing.rs index f393442de1011..489bcfbdefdd6 100644 --- a/src/test/compile-fail/import-from-missing.rs +++ b/src/test/compile-fail/import-from-missing.rs @@ -16,3 +16,4 @@ mod spam { } fn main() { ham(); eggs(); } +//~^ ERROR unresolved name `eggs` diff --git a/src/test/compile-fail/import.rs b/src/test/compile-fail/import.rs index 844d527a54607..86c4ce8b0380b 100644 --- a/src/test/compile-fail/import.rs +++ b/src/test/compile-fail/import.rs @@ -16,4 +16,4 @@ use zed::baz; mod zed { pub fn bar() { println!("bar"); } } -fn main(args: Vec) { bar(); } +fn main() { bar(); } diff --git a/src/test/compile-fail/import2.rs b/src/test/compile-fail/import2.rs index 6533bd5ddc610..1d2aecd4e3b7f 100644 --- a/src/test/compile-fail/import2.rs +++ b/src/test/compile-fail/import2.rs @@ -16,4 +16,5 @@ mod baz {} mod zed { pub fn bar() { println!("bar3"); } } -fn main(args: Vec) { bar(); } +fn main() { bar(); } +//~^ ERROR unresolved name `bar` diff --git a/src/test/compile-fail/macro-reexport-malformed-1.rs b/src/test/compile-fail/macro-reexport-malformed-1.rs index 6c85cf5c7f5db..ea2dfca0714fc 100644 --- a/src/test/compile-fail/macro-reexport-malformed-1.rs +++ b/src/test/compile-fail/macro-reexport-malformed-1.rs @@ -8,9 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![no_std] #![feature(macro_reexport)] #[macro_reexport] //~ ERROR bad macro reexport extern crate std; - -fn main() { } diff --git a/src/test/compile-fail/macro-reexport-malformed-2.rs b/src/test/compile-fail/macro-reexport-malformed-2.rs index 1dd0168181f83..844955fb7e664 100644 --- a/src/test/compile-fail/macro-reexport-malformed-2.rs +++ b/src/test/compile-fail/macro-reexport-malformed-2.rs @@ -8,9 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![no_std] #![feature(macro_reexport)] #[macro_reexport="foo"] //~ ERROR bad macro reexport extern crate std; - -fn main() { } diff --git a/src/test/compile-fail/macro-reexport-malformed-3.rs b/src/test/compile-fail/macro-reexport-malformed-3.rs index 7ae045f6e4f51..381c22854e654 100644 --- a/src/test/compile-fail/macro-reexport-malformed-3.rs +++ b/src/test/compile-fail/macro-reexport-malformed-3.rs @@ -8,9 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![no_std] #![feature(macro_reexport)] #[macro_reexport(foo="bar")] //~ ERROR bad macro reexport extern crate std; - -fn main() { } diff --git a/src/test/compile-fail/macro-reexport-undef.rs b/src/test/compile-fail/macro-reexport-undef.rs index 8fa6b32905ceb..5bb0b8759f486 100644 --- a/src/test/compile-fail/macro-reexport-undef.rs +++ b/src/test/compile-fail/macro-reexport-undef.rs @@ -10,6 +10,8 @@ // aux-build:two_macros.rs +#![feature(macro_reexport)] + #[macro_use(macro_two)] #[macro_reexport(no_way)] //~ ERROR reexported macro not found extern crate two_macros; diff --git a/src/test/compile-fail/macro-use-bad-args-1.rs b/src/test/compile-fail/macro-use-bad-args-1.rs index a73c4adb71f9f..39c09c6977963 100644 --- a/src/test/compile-fail/macro-use-bad-args-1.rs +++ b/src/test/compile-fail/macro-use-bad-args-1.rs @@ -8,8 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![no_std] + #[macro_use(foo(bar))] //~ ERROR bad macro import extern crate std; - -fn main() { -} diff --git a/src/test/compile-fail/macro-use-bad-args-2.rs b/src/test/compile-fail/macro-use-bad-args-2.rs index 31efe857605b4..11a0108b99b89 100644 --- a/src/test/compile-fail/macro-use-bad-args-2.rs +++ b/src/test/compile-fail/macro-use-bad-args-2.rs @@ -8,8 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![no_std] + #[macro_use(foo="bar")] //~ ERROR bad macro import extern crate std; - -fn main() { -} diff --git a/src/test/compile-fail/privacy3.rs b/src/test/compile-fail/privacy3.rs index da6266bc7ee6b..6a203993ccf2d 100644 --- a/src/test/compile-fail/privacy3.rs +++ b/src/test/compile-fail/privacy3.rs @@ -28,6 +28,7 @@ fn test1() { use bar::gpriv; //~^ ERROR unresolved import `bar::gpriv`. There is no `gpriv` in `bar` gpriv(); + //~^ ERROR unresolved name `gpriv` } #[start] fn main(_: isize, _: *const *const u8) -> isize { 3 } diff --git a/src/test/compile-fail/self_type_keyword.rs b/src/test/compile-fail/self_type_keyword.rs index 6f5aeead57ecb..e28197e81faf9 100644 --- a/src/test/compile-fail/self_type_keyword.rs +++ b/src/test/compile-fail/self_type_keyword.rs @@ -29,6 +29,7 @@ pub fn main() { //~^ ERROR expected identifier, found keyword `Self` Self!() => (), //~^ ERROR expected identifier, found keyword `Self` + //~^^ ERROR macro undefined: 'Self!' Foo { x: Self } => (), //~^ ERROR expected identifier, found keyword `Self` Foo { Self } => (), diff --git a/src/test/compile-fail/use-mod.rs b/src/test/compile-fail/use-mod.rs index 15640e386dfa0..9cc3c92e2e376 100644 --- a/src/test/compile-fail/use-mod.rs +++ b/src/test/compile-fail/use-mod.rs @@ -14,6 +14,7 @@ use foo::bar::{ Bar, self //~^ NOTE another `self` import appears here +//~^^ ERROR a module named `bar` has already been imported in this module }; use {self}; From ff009d1d3606e98b210fb423802cd3711fc82ee6 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Thu, 21 Jan 2016 12:32:56 +1300 Subject: [PATCH 06/23] test --- src/test/compile-fail/parse-error-correct.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/test/compile-fail/parse-error-correct.rs diff --git a/src/test/compile-fail/parse-error-correct.rs b/src/test/compile-fail/parse-error-correct.rs new file mode 100644 index 0000000000000..7715ed41841cf --- /dev/null +++ b/src/test/compile-fail/parse-error-correct.rs @@ -0,0 +1,19 @@ +// Copyright 2016 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 the parser is error correcting missing idents. Despite a parsing +// error (or two), we still run type checking (and don't get extra errors there). + +fn main() { + let y = 42; + let x = y.; //~ ERROR unexpected token + let x = y.(); //~ ERROR unexpected token + let x = y.foo; //~ ERROR no field +} From 616bfb6f15c377bc8850030f6239f14b8608b554 Mon Sep 17 00:00:00 2001 From: Daniel Campbell Date: Fri, 22 Jan 2016 11:58:09 +1300 Subject: [PATCH 07/23] Extended save-analysis to support generated code, alterned some spans in format_args! and derive to maintain compatability --- src/librustc_trans/save/dump_csv.rs | 213 ++++++++++--------------- src/librustc_trans/save/mod.rs | 102 ++++++------ src/librustc_trans/save/recorder.rs | 1 + src/librustc_trans/save/span_utils.rs | 55 +++++-- src/libsyntax/codemap.rs | 25 ++- src/libsyntax_ext/deriving/debug.rs | 6 +- src/libsyntax_ext/format.rs | 6 +- src/test/run-make/save-analysis/foo.rs | 38 +++++ 8 files changed, 248 insertions(+), 198 deletions(-) diff --git a/src/librustc_trans/save/dump_csv.rs b/src/librustc_trans/save/dump_csv.rs index b109353fac0bf..2968f86d2c49d 100644 --- a/src/librustc_trans/save/dump_csv.rs +++ b/src/librustc_trans/save/dump_csv.rs @@ -135,6 +135,9 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { // always using the first ones. So, only error out if we don't have enough spans. // What could go wrong...? if spans.len() < path.segments.len() { + if generated_code(path.span) { + return vec!(); + } error!("Mis-calculated spans for path '{}'. Found {} spans, expected {}. Found spans:", path_to_string(path), spans.len(), @@ -308,28 +311,26 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { id: ast::NodeId, name: ast::Name, span: Span) { - if generated_code(span) { - return; - } - debug!("process_method: {}:{}", id, name); - let method_data = self.save_ctxt.get_method_data(id, name, span); + if let Some(method_data) = self.save_ctxt.get_method_data(id, name, span) { - if body.is_some() { - self.fmt.method_str(span, - Some(method_data.span), - method_data.id, - &method_data.qualname, - method_data.declaration, - method_data.scope); - self.process_formals(&sig.decl.inputs, &method_data.qualname); - } else { - self.fmt.method_decl_str(span, - Some(method_data.span), - method_data.id, - &method_data.qualname, - method_data.scope); + if body.is_some() { + self.fmt.method_str(span, + Some(method_data.span), + method_data.id, + &method_data.qualname, + method_data.declaration, + method_data.scope); + self.process_formals(&sig.decl.inputs, &method_data.qualname); + } else { + self.fmt.method_decl_str(span, + Some(method_data.span), + method_data.id, + &method_data.qualname, + method_data.scope); + } + self.process_generic_params(&sig.generics, span, &method_data.qualname, id); } // walk arg and return types @@ -345,8 +346,6 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { if let Some(body) = body { self.nest(id, |v| v.visit_block(body)); } - - self.process_generic_params(&sig.generics, span, &method_data.qualname, id); } fn process_trait_ref(&mut self, trait_ref: &ast::TraitRef) { @@ -402,17 +401,17 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { decl: &ast::FnDecl, ty_params: &ast::Generics, body: &ast::Block) { - let fn_data = self.save_ctxt.get_item_data(item); - down_cast_data!(fn_data, FunctionData, self, item.span); - self.fmt.fn_str(item.span, - Some(fn_data.span), - fn_data.id, - &fn_data.qualname, - fn_data.scope); - - - self.process_formals(&decl.inputs, &fn_data.qualname); - self.process_generic_params(ty_params, item.span, &fn_data.qualname, item.id); + if let Some(fn_data) = self.save_ctxt.get_item_data(item) { + down_cast_data!(fn_data, FunctionData, self, item.span); + self.fmt.fn_str(item.span, + Some(fn_data.span), + fn_data.id, + &fn_data.qualname, + fn_data.scope); + + self.process_formals(&decl.inputs, &fn_data.qualname); + self.process_generic_params(ty_params, item.span, &fn_data.qualname, item.id); + } for arg in &decl.inputs { self.visit_ty(&arg.ty); @@ -426,17 +425,17 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { } fn process_static_or_const_item(&mut self, item: &ast::Item, typ: &ast::Ty, expr: &ast::Expr) { - let var_data = self.save_ctxt.get_item_data(item); - down_cast_data!(var_data, VariableData, self, item.span); - self.fmt.static_str(item.span, - Some(var_data.span), - var_data.id, - &var_data.name, - &var_data.qualname, - &var_data.value, - &var_data.type_value, - var_data.scope); - + if let Some(var_data) = self.save_ctxt.get_item_data(item) { + down_cast_data!(var_data, VariableData, self, item.span); + self.fmt.static_str(item.span, + Some(var_data.span), + var_data.id, + &var_data.name, + &var_data.qualname, + &var_data.value, + &var_data.type_value, + var_data.scope); + } self.visit_ty(&typ); self.visit_expr(expr); } @@ -495,6 +494,10 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { enum_definition: &ast::EnumDef, ty_params: &ast::Generics) { let enum_data = self.save_ctxt.get_item_data(item); + let enum_data = match enum_data { + None => return, + Some(data) => data, + }; down_cast_data!(enum_data, EnumData, self, item.span); self.fmt.enum_str(item.span, Some(enum_data.span), @@ -533,36 +536,36 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { trait_ref: &Option, typ: &ast::Ty, impl_items: &[P]) { - let impl_data = self.save_ctxt.get_item_data(item); - down_cast_data!(impl_data, ImplData, self, item.span); - match impl_data.self_ref { - Some(ref self_ref) => { + let mut has_self_ref = false; + if let Some(impl_data) = self.save_ctxt.get_item_data(item) { + down_cast_data!(impl_data, ImplData, self, item.span); + if let Some(ref self_ref) = impl_data.self_ref { + has_self_ref = true; self.fmt.ref_str(recorder::TypeRef, item.span, Some(self_ref.span), self_ref.ref_id, self_ref.scope); } - None => { - self.visit_ty(&typ); + if let Some(ref trait_ref_data) = impl_data.trait_ref { + self.fmt.ref_str(recorder::TypeRef, + item.span, + Some(trait_ref_data.span), + trait_ref_data.ref_id, + trait_ref_data.scope); + visit::walk_path(self, &trait_ref.as_ref().unwrap().path); } + + self.fmt.impl_str(item.span, + Some(impl_data.span), + impl_data.id, + impl_data.self_ref.map(|data| data.ref_id), + impl_data.trait_ref.map(|data| data.ref_id), + impl_data.scope); } - if let Some(ref trait_ref_data) = impl_data.trait_ref { - self.fmt.ref_str(recorder::TypeRef, - item.span, - Some(trait_ref_data.span), - trait_ref_data.ref_id, - trait_ref_data.scope); - visit::walk_path(self, &trait_ref.as_ref().unwrap().path); + if !has_self_ref { + self.visit_ty(&typ); } - - self.fmt.impl_str(item.span, - Some(impl_data.span), - impl_data.id, - impl_data.self_ref.map(|data| data.ref_id), - impl_data.trait_ref.map(|data| data.ref_id), - impl_data.scope); - self.process_generic_params(type_parameters, item.span, "", item.id); for impl_item in impl_items { self.visit_impl_item(impl_item); @@ -619,22 +622,23 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { // `item` is the module in question, represented as an item. fn process_mod(&mut self, item: &ast::Item) { - let mod_data = self.save_ctxt.get_item_data(item); - down_cast_data!(mod_data, ModData, self, item.span); - self.fmt.mod_str(item.span, - Some(mod_data.span), - mod_data.id, - &mod_data.qualname, - mod_data.scope, - &mod_data.filename); + if let Some(mod_data) = self.save_ctxt.get_item_data(item) { + down_cast_data!(mod_data, ModData, self, item.span); + self.fmt.mod_str(item.span, + Some(mod_data.span), + mod_data.id, + &mod_data.qualname, + mod_data.scope, + &mod_data.filename); + } } fn process_path(&mut self, id: NodeId, path: &ast::Path, ref_kind: Option) { - if generated_code(path.span) { + let path_data = self.save_ctxt.get_path_data(id, path); + if generated_code(path.span) && path_data.is_none() { return; } - let path_data = self.save_ctxt.get_path_data(id, path); let path_data = match path_data { Some(pd) => pd, None => { @@ -705,10 +709,6 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { fields: &Vec, variant: ty::VariantDef, base: &Option>) { - if generated_code(path.span) { - return - } - self.write_sub_paths_truncated(path, false); if let Some(struct_lit_data) = self.save_ctxt.get_expr_data(ex) { @@ -721,16 +721,15 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { let scope = self.save_ctxt.enclosing_scope(ex.id); for field in fields { - if generated_code(field.ident.span) { - continue; - } + if let Some(field_data) = self.save_ctxt + .get_field_ref_data(field, variant, scope) { - let field_data = self.save_ctxt.get_field_ref_data(field, variant, scope); - self.fmt.ref_str(recorder::VarRef, - field.ident.span, - Some(field_data.span), - field_data.ref_id, - field_data.scope); + self.fmt.ref_str(recorder::VarRef, + field.ident.span, + Some(field_data.span), + field_data.ref_id, + field_data.scope); + } self.visit_expr(&field.expr) } @@ -754,10 +753,6 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { } fn process_pat(&mut self, p: &ast::Pat) { - if generated_code(p.span) { - return; - } - match p.node { ast::PatStruct(ref path, ref fields, _) => { visit::walk_path(self, path); @@ -766,10 +761,6 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { let variant = adt.variant_of_def(def); for &Spanned { node: ref field, span } in fields { - if generated_code(span) { - continue; - } - let sub_span = self.span.span_for_first_ident(span); if let Some(f) = variant.find_field_named(field.ident.name) { self.fmt.ref_str(recorder::VarRef, span, sub_span, f.did, self.cur_scope); @@ -813,10 +804,6 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { fn visit_item(&mut self, item: &ast::Item) { - if generated_code(item.span) { - return - } - match item.node { ast::ItemUse(ref use_item) => { match use_item.node { @@ -1011,10 +998,6 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { } fn visit_ty(&mut self, t: &ast::Ty) { - if generated_code(t.span) { - return - } - match t.node { ast::TyPath(_, ref path) => { match self.lookup_type_ref(t.id) { @@ -1034,10 +1017,6 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { } fn visit_expr(&mut self, ex: &ast::Expr) { - if generated_code(ex.span) { - return - } - match ex.node { ast::ExprCall(ref _f, ref _args) => { // Don't need to do anything for function calls, @@ -1056,10 +1035,6 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { } ast::ExprMethodCall(_, _, ref args) => self.process_method_call(ex, args), ast::ExprField(ref sub_ex, _) => { - if generated_code(sub_ex.span) { - return - } - self.visit_expr(&sub_ex); if let Some(field_data) = self.save_ctxt.get_expr_data(ex) { @@ -1072,10 +1047,6 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { } } ast::ExprTupField(ref sub_ex, idx) => { - if generated_code(sub_ex.span) { - return - } - self.visit_expr(&**sub_ex); let hir_node = lower_expr(self.save_ctxt.lcx, sub_ex); @@ -1096,10 +1067,6 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { } } ast::ExprClosure(_, ref decl, ref body) => { - if generated_code(body.span) { - return - } - let mut id = String::from("$"); id.push_str(&ex.id.to_string()); self.process_formals(&decl.inputs, &id); @@ -1196,18 +1163,10 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { } fn visit_stmt(&mut self, s: &ast::Stmt) { - if generated_code(s.span) { - return - } - visit::walk_stmt(self, s) } fn visit_local(&mut self, l: &ast::Local) { - if generated_code(l.span) { - return - } - let value = self.span.snippet(l.span); self.process_var_decl(&l.pat, value); diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index 00554419e649c..37b23d6ee9ce9 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -30,7 +30,7 @@ use syntax::print::pprust::ty_to_string; use self::span_utils::SpanUtils; - +#[macro_use] pub mod span_utils; pub mod recorder; @@ -209,21 +209,21 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { result } - pub fn get_item_data(&self, item: &ast::Item) -> Data { + pub fn get_item_data(&self, item: &ast::Item) -> Option { match item.node { ast::ItemFn(..) => { let name = self.tcx.map.path_to_string(item.id); let qualname = format!("::{}", name); let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Fn); - - Data::FunctionData(FunctionData { + filter!(self.span_utils, sub_span, item.span, None); + Some(Data::FunctionData(FunctionData { id: item.id, name: name, qualname: qualname, declaration: None, span: sub_span.unwrap(), scope: self.enclosing_scope(item.id), - }) + })) } ast::ItemStatic(ref typ, mt, ref expr) => { let qualname = format!("::{}", self.tcx.map.path_to_string(item.id)); @@ -235,8 +235,8 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { }; let sub_span = self.span_utils.sub_span_after_keyword(item.span, keyword); - - Data::VariableData(VariableData { + filter!(self.span_utils, sub_span, item.span, None); + Some(Data::VariableData(VariableData { id: item.id, name: item.ident.to_string(), qualname: qualname, @@ -244,13 +244,13 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { scope: self.enclosing_scope(item.id), value: value, type_value: ty_to_string(&typ), - }) + })) } ast::ItemConst(ref typ, ref expr) => { let qualname = format!("::{}", self.tcx.map.path_to_string(item.id)); let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Const); - - Data::VariableData(VariableData { + filter!(self.span_utils, sub_span, item.span, None); + Some(Data::VariableData(VariableData { id: item.id, name: item.ident.to_string(), qualname: qualname, @@ -258,7 +258,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { scope: self.enclosing_scope(item.id), value: self.span_utils.snippet(expr.span), type_value: ty_to_string(&typ), - }) + })) } ast::ItemMod(ref m) => { let qualname = format!("::{}", self.tcx.map.path_to_string(item.id)); @@ -267,28 +267,28 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { let filename = cm.span_to_filename(m.inner); let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Mod); - - Data::ModData(ModData { + filter!(self.span_utils, sub_span, item.span, None); + Some(Data::ModData(ModData { id: item.id, name: item.ident.to_string(), qualname: qualname, span: sub_span.unwrap(), scope: self.enclosing_scope(item.id), filename: filename, - }) + })) } ast::ItemEnum(..) => { let enum_name = format!("::{}", self.tcx.map.path_to_string(item.id)); let val = self.span_utils.snippet(item.span); let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Enum); - - Data::EnumData(EnumData { + filter!(self.span_utils, sub_span, item.span, None); + Some(Data::EnumData(EnumData { id: item.id, value: val, span: sub_span.unwrap(), qualname: enum_name, scope: self.enclosing_scope(item.id), - }) + })) } ast::ItemImpl(_, _, _, ref trait_ref, ref typ, _) => { let mut type_data = None; @@ -299,10 +299,11 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { match typ.node { // Common case impl for a struct or something basic. ast::TyPath(None, ref path) => { - sub_span = self.span_utils.sub_span_for_type_name(path.span).unwrap(); + sub_span = self.span_utils.sub_span_for_type_name(path.span); + filter!(self.span_utils, sub_span, path.span, None); type_data = self.lookup_ref_id(typ.id).map(|id| { TypeRefData { - span: sub_span, + span: sub_span.unwrap(), scope: parent, ref_id: id, } @@ -311,20 +312,21 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { _ => { // Less useful case, impl for a compound type. let span = typ.span; - sub_span = self.span_utils.sub_span_for_type_name(span).unwrap_or(span); + sub_span = self.span_utils.sub_span_for_type_name(span).or(Some(span)); } } let trait_data = trait_ref.as_ref() .and_then(|tr| self.get_trait_ref_data(tr, parent)); - Data::ImplData(ImplData { + filter!(self.span_utils, sub_span, typ.span, None); + Some(Data::ImplData(ImplData { id: item.id, - span: sub_span, + span: sub_span.unwrap(), scope: parent, trait_ref: trait_data, self_ref: type_data, - }) + })) } _ => { // FIXME @@ -333,12 +335,14 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { } } - pub fn get_field_data(&self, field: &ast::StructField, scope: NodeId) -> Option { + pub fn get_field_data(&self, field: &ast::StructField, + scope: NodeId) -> Option { match field.node.kind { ast::NamedField(ident, _) => { let qualname = format!("::{}::{}", self.tcx.map.path_to_string(scope), ident); let typ = self.tcx.node_types().get(&field.node.id).unwrap().to_string(); let sub_span = self.span_utils.sub_span_before_token(field.span, token::Colon); + filter!(self.span_utils, sub_span, field.span, None); Some(VariableData { id: field.node.id, name: ident.to_string(), @@ -355,7 +359,8 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { // FIXME would be nice to take a MethodItem here, but the ast provides both // trait and impl flavours, so the caller must do the disassembly. - pub fn get_method_data(&self, id: ast::NodeId, name: ast::Name, span: Span) -> FunctionData { + pub fn get_method_data(&self, id: ast::NodeId, + name: ast::Name, span: Span) -> Option { // The qualname for a method is the trait name or name of the struct in an impl in // which the method is declared in, followed by the method's name. let qualname = match self.tcx.impl_of_method(self.tcx.map.local_def_id(id)) { @@ -430,29 +435,30 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { }); let sub_span = self.span_utils.sub_span_after_keyword(span, keywords::Fn); - - FunctionData { + filter!(self.span_utils, sub_span, span, None); + Some(FunctionData { id: id, name: name.to_string(), qualname: qualname, declaration: decl_id, span: sub_span.unwrap(), scope: self.enclosing_scope(id), - } + }) } pub fn get_trait_ref_data(&self, trait_ref: &ast::TraitRef, parent: NodeId) -> Option { - self.lookup_ref_id(trait_ref.ref_id).map(|def_id| { + self.lookup_ref_id(trait_ref.ref_id).and_then(|def_id| { let span = trait_ref.path.span; - let sub_span = self.span_utils.sub_span_for_type_name(span).unwrap_or(span); - TypeRefData { - span: sub_span, + let sub_span = self.span_utils.sub_span_for_type_name(span).or(Some(span)); + filter!(self.span_utils, sub_span, span, None); + Some(TypeRefData { + span: sub_span.unwrap(), scope: parent, ref_id: def_id, - } + }) }) } @@ -465,6 +471,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { ty::TyStruct(def, _) => { let f = def.struct_variant().field_named(ident.node.name); let sub_span = self.span_utils.span_for_last_ident(expr.span); + filter!(self.span_utils, sub_span, expr.span, None); return Some(Data::VariableRefData(VariableRefData { name: ident.node.to_string(), span: sub_span.unwrap(), @@ -484,6 +491,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { match *ty { ty::TyStruct(def, _) => { let sub_span = self.span_utils.span_for_last_ident(path.span); + filter!(self.span_utils, sub_span, path.span, None); Some(Data::TypeRefData(TypeRefData { span: sub_span.unwrap(), scope: self.enclosing_scope(expr.id), @@ -506,6 +514,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { ty::TraitContainer(_) => (None, Some(method_id)), }; let sub_span = self.span_utils.sub_span_for_meth_name(expr.span); + filter!(self.span_utils, sub_span, expr.span, None); let parent = self.enclosing_scope(expr.id); Some(Data::MethodCallData(MethodCallData { span: sub_span.unwrap(), @@ -532,6 +541,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { } let def = def_map.get(&id).unwrap().full_def(); let sub_span = self.span_utils.span_for_last_ident(path.span); + filter!(self.span_utils, sub_span, path.span, None); match def { Def::Upvar(..) | Def::Local(..) | @@ -559,6 +569,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { } Def::Method(decl_id) => { let sub_span = self.span_utils.sub_span_for_meth_name(path.span); + filter!(self.span_utils, sub_span, path.span, None); let def_id = if decl_id.is_local() { let ti = self.tcx.impl_or_trait_item(decl_id); match ti.container() { @@ -628,16 +639,17 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { field_ref: &ast::Field, variant: ty::VariantDef, parent: NodeId) - -> VariableRefData { + -> Option { let f = variant.field_named(field_ref.ident.node.name); // We don't really need a sub-span here, but no harm done let sub_span = self.span_utils.span_for_last_ident(field_ref.ident.span); - VariableRefData { + filter!(self.span_utils, sub_span, field_ref.ident.span, None); + Some(VariableRefData { name: field_ref.ident.node.to_string(), span: sub_span.unwrap(), scope: parent, ref_id: f.did, - } + }) } pub fn get_data_for_id(&self, _id: &NodeId) -> Data { @@ -677,17 +689,15 @@ impl PathCollector { impl<'v> Visitor<'v> for PathCollector { fn visit_pat(&mut self, p: &ast::Pat) { - if generated_code(p.span) { - return; - } - match p.node { ast::PatStruct(ref path, _, _) => { - self.collected_paths.push((p.id, path.clone(), ast::MutMutable, recorder::TypeRef)); + self.collected_paths.push((p.id, path.clone(), + ast::MutMutable, recorder::TypeRef)); } ast::PatEnum(ref path, _) | ast::PatQPath(_, ref path) => { - self.collected_paths.push((p.id, path.clone(), ast::MutMutable, recorder::VarRef)); + self.collected_paths.push((p.id, path.clone(), + ast::MutMutable, recorder::VarRef)); } ast::PatIdent(bm, ref path1, _) => { debug!("PathCollector, visit ident in pat {}: {:?} {:?}", @@ -719,10 +729,6 @@ pub fn process_crate<'l, 'tcx>(tcx: &'l ty::ctxt<'tcx>, odir: Option<&Path>) { let _ignore = tcx.dep_graph.in_ignore(); - if generated_code(krate.span) { - return; - } - assert!(analysis.glob_map.is_some()); info!("Dumping crate {}", cratename); @@ -780,8 +786,8 @@ fn escape(s: String) -> String { s.replace("\"", "\"\"") } -// If the expression is a macro expansion or other generated code, run screaming -// and don't index. +// Helper function to determine if a span came from a +// macro expansion or syntax extension. pub fn generated_code(span: Span) -> bool { span.expn_id != NO_EXPANSION || span == DUMMY_SP } diff --git a/src/librustc_trans/save/recorder.rs b/src/librustc_trans/save/recorder.rs index 1db31baf30dcc..682a74c8f0c38 100644 --- a/src/librustc_trans/save/recorder.rs +++ b/src/librustc_trans/save/recorder.rs @@ -318,6 +318,7 @@ impl<'a, 'tcx: 'a> FmtStrs<'a, 'tcx> { span: Span, sub_span: Option, values: Vec) { + filter!(self.span, sub_span, span); match sub_span { Some(sub_span) => self.record_with_span(kind, span, sub_span, values), None => { diff --git a/src/librustc_trans/save/span_utils.rs b/src/librustc_trans/save/span_utils.rs index 773d5caea5f1a..344431032d694 100644 --- a/src/librustc_trans/save/span_utils.rs +++ b/src/librustc_trans/save/span_utils.rs @@ -66,13 +66,6 @@ impl<'a> SpanUtils<'a> { // sub_span starts at span.lo, so we need to adjust the positions etc. // If sub_span is None, we don't need to adjust. pub fn make_sub_span(&self, span: Span, sub_span: Option) -> Option { - let loc = self.sess.codemap().lookup_char_pos(span.lo); - assert!(!generated_code(span), - "generated code; we should not be processing this `{}` in {}, line {}", - self.snippet(span), - loc.file.name, - loc.line); - match sub_span { None => None, Some(sub) => { @@ -81,7 +74,7 @@ impl<'a> SpanUtils<'a> { Some(Span { lo: base + self.sess.codemap().lookup_byte_offset(sub.lo).pos, hi: base + self.sess.codemap().lookup_byte_offset(sub.hi).pos, - expn_id: NO_EXPANSION, + expn_id: span.expn_id, }) } } @@ -259,6 +252,9 @@ impl<'a> SpanUtils<'a> { let ts = toks.real_token(); if ts.tok == token::Eof { if bracket_count != 0 { + if generated_code(span) { + return vec!(); + } let loc = self.sess.codemap().lookup_char_pos(span.lo); self.sess.span_bug(span, &format!("Mis-counted brackets when breaking path? \ @@ -358,19 +354,12 @@ impl<'a> SpanUtils<'a> { // Returns a list of the spans of idents in a path. // E.g., For foo::bar::baz, we return [foo, bar, baz] (well, their spans) pub fn spans_for_path_segments(&self, path: &ast::Path) -> Vec { - if generated_code(path.span) { - return vec!(); - } - self.spans_with_brackets(path.span, 0, -1) } // Return an owned vector of the subspans of the param identifier // tokens found in span. pub fn spans_for_ty_params(&self, span: Span, number: isize) -> Vec { - if generated_code(span) { - return vec!(); - } // Type params are nested within one level of brackets: // i.e. we want Vec from Foo> self.spans_with_brackets(span, 1, number) @@ -388,4 +377,40 @@ impl<'a> SpanUtils<'a> { self.sess.bug("span errors reached 1000, giving up"); } } + + /// Return true if the span is generated code, and + /// it is not a subspan of the root callsite. + /// + /// Used to filter out spans of minimal value, + /// such as references to macro internal variables. + pub fn filter_generated(&self, sub_span: Option, parent: Span) -> bool { + if !generated_code(parent) { + if sub_span.is_none() { + // Edge case - this occurs on generated code with incorrect expansion info. + return true; + } + return false; + } + // If sub_span is none, filter out generated code. + if sub_span.is_none() { + return true; + } + // A generated span is deemed invalid if it is not a sub-span of the root + // callsite. This filters out macro internal variables and most malformed spans. + let span = self.sess.codemap().source_callsite(parent); + !(parent.lo >= span.lo && parent.hi <= span.hi) + } +} + +macro_rules! filter { + ($util: expr, $span: ident, $parent: expr, None) => { + if $util.filter_generated($span, $parent) { + return None; + } + }; + ($util: expr, $span: ident, $parent: expr) => { + if $util.filter_generated($span, $parent) { + return; + } + }; } diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index 8d6c0df981f23..432c1688536bb 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -858,10 +858,15 @@ impl CodeMap { let span_str = self.span_to_string(sp); let mut span_snip = self.span_to_snippet(sp) .unwrap_or("Snippet unavailable".to_owned()); - if span_snip.len() > 50 { - span_snip.truncate(50); + + // Truncate by code points - in worst case this will be more than 50 characters, + // but ensures at least 50 characters and respects byte boundaries. + let char_vec: Vec<(usize, char)> = span_snip.char_indices().collect(); + if char_vec.len() > 50 { + span_snip.truncate(char_vec[49].0); span_snip.push_str("..."); } + output.push_str(&format!("{}{}\n{}`{}`\n", indent, span_str, indent, span_snip)); if sp.expn_id == NO_EXPANSION || sp.expn_id == COMMAND_LINE_EXPN { @@ -909,6 +914,22 @@ impl CodeMap { output } + /// Return the source span - this is either the supplied span, or the span for + /// the macro callsite that expanded to it. + pub fn source_callsite(&self, sp: Span) -> Span { + let mut span = sp; + while span.expn_id != NO_EXPANSION && span.expn_id != COMMAND_LINE_EXPN { + if let Some(callsite) = self.with_expn_info(span.expn_id, + |ei| ei.map(|ei| ei.call_site.clone())) { + span = callsite; + } + else { + break; + } + } + span + } + pub fn span_to_filename(&self, sp: Span) -> FileName { self.lookup_char_pos(sp.lo).file.name.to_string() } diff --git a/src/libsyntax_ext/deriving/debug.rs b/src/libsyntax_ext/deriving/debug.rs index ed3f764c1d2aa..008067f39a352 100644 --- a/src/libsyntax_ext/deriving/debug.rs +++ b/src/libsyntax_ext/deriving/debug.rs @@ -13,7 +13,7 @@ use deriving::generic::ty::*; use syntax::ast; use syntax::ast::{MetaItem, Expr}; -use syntax::codemap::{Span, respan}; +use syntax::codemap::{Span, respan, DUMMY_SP}; use syntax::ext::base::{ExtCtxt, Annotatable}; use syntax::ext::build::AstBuilder; use syntax::parse::token; @@ -87,7 +87,7 @@ fn show_substructure(cx: &mut ExtCtxt, span: Span, fmt, token::str_to_ident("debug_tuple"), vec![name]); - stmts.push(cx.stmt_let(span, true, builder, expr)); + stmts.push(cx.stmt_let(DUMMY_SP, true, builder, expr)); for field in fields { // Use double indirection to make sure this works for unsized types @@ -109,7 +109,7 @@ fn show_substructure(cx: &mut ExtCtxt, span: Span, fmt, token::str_to_ident("debug_struct"), vec![name]); - stmts.push(cx.stmt_let(span, true, builder, expr)); + stmts.push(cx.stmt_let(DUMMY_SP, true, builder, expr)); for field in fields { let name = cx.expr_lit(field.span, ast::Lit_::LitStr( diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs index 1fb2b55215ded..77bf90abbcc14 100644 --- a/src/libsyntax_ext/format.rs +++ b/src/libsyntax_ext/format.rs @@ -14,7 +14,7 @@ use self::Position::*; use fmt_macros as parse; use syntax::ast; -use syntax::codemap::{Span, respan}; +use syntax::codemap::{Span, respan, DUMMY_SP}; use syntax::ext::base::*; use syntax::ext::base; use syntax::ext::build::AstBuilder; @@ -501,7 +501,7 @@ impl<'a, 'b> Context<'a, 'b> { }; let name = self.ecx.ident_of(&format!("__arg{}", i)); - pats.push(self.ecx.pat_ident(e.span, name)); + pats.push(self.ecx.pat_ident(DUMMY_SP, name)); locals.push(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty, self.ecx.expr_ident(e.span, name))); heads.push(self.ecx.expr_addr_of(e.span, e)); @@ -518,7 +518,7 @@ impl<'a, 'b> Context<'a, 'b> { let lname = self.ecx.ident_of(&format!("__arg{}", *name)); - pats.push(self.ecx.pat_ident(e.span, lname)); + pats.push(self.ecx.pat_ident(DUMMY_SP, lname)); names[*self.name_positions.get(name).unwrap()] = Some(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty, self.ecx.expr_ident(e.span, lname))); diff --git a/src/test/run-make/save-analysis/foo.rs b/src/test/run-make/save-analysis/foo.rs index 3e4ba5af80c4d..7a1c200ba20e8 100644 --- a/src/test/run-make/save-analysis/foo.rs +++ b/src/test/run-make/save-analysis/foo.rs @@ -287,6 +287,26 @@ pub struct blah { used_link_args: RefCell<[&'static str; 0]>, } +#[macro_use] +mod macro_use_test { + macro_rules! test_rec { + (q, $src: expr) => {{ + print!("{}", $src); + test_rec!($src); + }}; + ($src: expr) => { + print!("{}", $src); + }; + } + + macro_rules! internal_vars { + ($src: ident) => {{ + let mut x = $src; + x += 100; + }}; + } +} + fn main() { // foo let s = box some_fields {field1: 43}; hello((43, "a".to_string()), *s); @@ -356,6 +376,11 @@ fn main() { // foo while let Some(z) = None { foo_foo(z); } + + let mut x = 4; + test_rec!(q, "Hello"); + assert_eq!(x, 4); + internal_vars!(x); } fn foo_foo(_: i32) {} @@ -398,3 +423,16 @@ impl Error + 'static + Send { ::is::(self) } } +extern crate serialize; +#[derive(Clone, Copy, Hash, Encodable, Decodable, PartialEq, Eq, PartialOrd, Ord, Debug, Default)] +struct AllDerives(i32); + +fn test_format_args() { + let x = 1; + let y = 2; + let name = "Joe Blogg"; + println!("Hello {}", name); + print!("Hello {0}", name); + print!("{0} + {} = {}", x, y); + print!("x is {}, y is {1}, name is {n}", x, y, n = name); +} \ No newline at end of file From 276fae11ea999e946a48b2150e59438eb8a5e442 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Sat, 23 Jan 2016 13:37:54 -0500 Subject: [PATCH 08/23] thorough follow-set tests --- src/test/compile-fail/macro-follow.rs | 122 +++++++++++++++++ src/test/run-pass/macro-follow.rs | 190 ++++++++++++++++++++++++++ 2 files changed, 312 insertions(+) create mode 100644 src/test/compile-fail/macro-follow.rs create mode 100644 src/test/run-pass/macro-follow.rs diff --git a/src/test/compile-fail/macro-follow.rs b/src/test/compile-fail/macro-follow.rs new file mode 100644 index 0000000000000..35944bada4d64 --- /dev/null +++ b/src/test/compile-fail/macro-follow.rs @@ -0,0 +1,122 @@ +// Copyright 2016 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. +// +// Check the macro follow sets (see corresponding rpass test). + +// FOLLOW(pat) = {FatArrow, Comma, Eq, Or, Ident(if), Ident(in)} +macro_rules! follow_pat { + ($p:pat ()) => {}; //~WARN `$p:pat` is followed by `(` + ($p:pat []) => {}; //~WARN `$p:pat` is followed by `[` + ($p:pat {}) => {}; //~WARN `$p:pat` is followed by `{` + ($p:pat :) => {}; //~ERROR `$p:pat` is followed by `:` + ($p:pat >) => {}; //~ERROR `$p:pat` is followed by `>` + ($p:pat +) => {}; //~ERROR `$p:pat` is followed by `+` + ($p:pat ident) => {}; //~ERROR `$p:pat` is followed by `ident` + ($p:pat $p:pat) => {}; //~ERROR `$p:pat` is followed by `$p:pat` + ($p:pat $e:expr) => {}; //~ERROR `$p:pat` is followed by `$e:expr` + ($p:pat $t:ty) => {}; //~ERROR `$p:pat` is followed by `$t:ty` + ($p:pat $s:stmt) => {}; //~ERROR `$p:pat` is followed by `$s:stmt` + ($p:pat $p:path) => {}; //~ERROR `$p:pat` is followed by `$p:path` + ($p:pat $b:block) => {}; //~ERROR `$p:pat` is followed by `$b:block` + ($p:pat $i:ident) => {}; //~ERROR `$p:pat` is followed by `$i:ident` + ($p:pat $t:tt) => {}; //~ERROR `$p:pat` is followed by `$t:tt` + ($p:pat $i:item) => {}; //~ERROR `$p:pat` is followed by `$i:item` + ($p:pat $m:meta) => {}; //~ERROR `$p:pat` is followed by `$m:meta` +} +// FOLLOW(expr) = {FatArrow, Comma, Semicolon} +macro_rules! follow_expr { + ($e:expr ()) => {}; //~WARN `$e:expr` is followed by `(` + ($e:expr []) => {}; //~WARN `$e:expr` is followed by `[` + ($e:expr {}) => {}; //~WARN `$e:expr` is followed by `{` + ($e:expr =) => {}; //~ERROR `$e:expr` is followed by `=` + ($e:expr |) => {}; //~ERROR `$e:expr` is followed by `|` + ($e:expr :) => {}; //~ERROR `$e:expr` is followed by `:` + ($e:expr >) => {}; //~ERROR `$e:expr` is followed by `>` + ($e:expr +) => {}; //~ERROR `$e:expr` is followed by `+` + ($e:expr ident) => {}; //~ERROR `$e:expr` is followed by `ident` + ($e:expr if) => {}; //~ERROR `$e:expr` is followed by `if` + ($e:expr in) => {}; //~ERROR `$e:expr` is followed by `in` + ($e:expr $p:pat) => {}; //~ERROR `$e:expr` is followed by `$p:pat` + ($e:expr $e:expr) => {}; //~ERROR `$e:expr` is followed by `$e:expr` + ($e:expr $t:ty) => {}; //~ERROR `$e:expr` is followed by `$t:ty` + ($e:expr $s:stmt) => {}; //~ERROR `$e:expr` is followed by `$s:stmt` + ($e:expr $p:path) => {}; //~ERROR `$e:expr` is followed by `$p:path` + ($e:expr $b:block) => {}; //~ERROR `$e:expr` is followed by `$b:block` + ($e:expr $i:ident) => {}; //~ERROR `$e:expr` is followed by `$i:ident` + ($e:expr $t:tt) => {}; //~ERROR `$e:expr` is followed by `$t:tt` + ($e:expr $i:item) => {}; //~ERROR `$e:expr` is followed by `$i:item` + ($e:expr $m:meta) => {}; //~ERROR `$e:expr` is followed by `$m:meta` +} +// FOLLOW(ty) = {OpenDelim(Brace), Comma, FatArrow, Colon, Eq, Gt, Semi, Or, +// Ident(as), Ident(where), OpenDelim(Bracket)} +macro_rules! follow_ty { + ($t:ty ()) => {}; //~WARN `$t:ty` is followed by `(` + ($t:ty []) => {}; // ok (RFC 1462) + ($t:ty +) => {}; //~ERROR `$t:ty` is followed by `+` + ($t:ty ident) => {}; //~ERROR `$t:ty` is followed by `ident` + ($t:ty if) => {}; //~ERROR `$t:ty` is followed by `if` + ($t:ty $p:pat) => {}; //~ERROR `$t:ty` is followed by `$p:pat` + ($t:ty $e:expr) => {}; //~ERROR `$t:ty` is followed by `$e:expr` + ($t:ty $t:ty) => {}; //~ERROR `$t:ty` is followed by `$t:ty` + ($t:ty $s:stmt) => {}; //~ERROR `$t:ty` is followed by `$s:stmt` + ($t:ty $p:path) => {}; //~ERROR `$t:ty` is followed by `$p:path` + ($t:ty $b:block) => {}; //~ERROR `$t:ty` is followed by `$b:block` + ($t:ty $i:ident) => {}; //~ERROR `$t:ty` is followed by `$i:ident` + ($t:ty $t:tt) => {}; //~ERROR `$t:ty` is followed by `$t:tt` + ($t:ty $i:item) => {}; //~ERROR `$t:ty` is followed by `$i:item` + ($t:ty $m:meta) => {}; //~ERROR `$t:ty` is followed by `$m:meta` +} +// FOLLOW(stmt) = FOLLOW(expr) +macro_rules! follow_stmt { + ($s:stmt ()) => {}; //~WARN `$s:stmt` is followed by `(` + ($s:stmt []) => {}; //~WARN `$s:stmt` is followed by `[` + ($s:stmt {}) => {}; //~WARN `$s:stmt` is followed by `{` + ($s:stmt =) => {}; //~ERROR `$s:stmt` is followed by `=` + ($s:stmt |) => {}; //~ERROR `$s:stmt` is followed by `|` + ($s:stmt :) => {}; //~ERROR `$s:stmt` is followed by `:` + ($s:stmt >) => {}; //~ERROR `$s:stmt` is followed by `>` + ($s:stmt +) => {}; //~ERROR `$s:stmt` is followed by `+` + ($s:stmt ident) => {}; //~ERROR `$s:stmt` is followed by `ident` + ($s:stmt if) => {}; //~ERROR `$s:stmt` is followed by `if` + ($s:stmt in) => {}; //~ERROR `$s:stmt` is followed by `in` + ($s:stmt $p:pat) => {}; //~ERROR `$s:stmt` is followed by `$p:pat` + ($s:stmt $e:expr) => {}; //~ERROR `$s:stmt` is followed by `$e:expr` + ($s:stmt $t:ty) => {}; //~ERROR `$s:stmt` is followed by `$t:ty` + ($s:stmt $s:stmt) => {}; //~ERROR `$s:stmt` is followed by `$s:stmt` + ($s:stmt $p:path) => {}; //~ERROR `$s:stmt` is followed by `$p:path` + ($s:stmt $b:block) => {}; //~ERROR `$s:stmt` is followed by `$b:block` + ($s:stmt $i:ident) => {}; //~ERROR `$s:stmt` is followed by `$i:ident` + ($s:stmt $t:tt) => {}; //~ERROR `$s:stmt` is followed by `$t:tt` + ($s:stmt $i:item) => {}; //~ERROR `$s:stmt` is followed by `$i:item` + ($s:stmt $m:meta) => {}; //~ERROR `$s:stmt` is followed by `$m:meta` +} +// FOLLOW(path) = FOLLOW(ty) +macro_rules! follow_path { + ($p:path ()) => {}; //~WARN `$p:path` is followed by `(` + ($p:path []) => {}; // ok (RFC 1462) + ($p:path +) => {}; //~ERROR `$p:path` is followed by `+` + ($p:path ident) => {}; //~ERROR `$p:path` is followed by `ident` + ($p:path if) => {}; //~ERROR `$p:path` is followed by `if` + ($p:path $p:pat) => {}; //~ERROR `$p:path` is followed by `$p:pat` + ($p:path $e:expr) => {}; //~ERROR `$p:path` is followed by `$e:expr` + ($p:path $t:ty) => {}; //~ERROR `$p:path` is followed by `$t:ty` + ($p:path $s:stmt) => {}; //~ERROR `$p:path` is followed by `$s:stmt` + ($p:path $p:path) => {}; //~ERROR `$p:path` is followed by `$p:path` + ($p:path $b:block) => {}; //~ERROR `$p:path` is followed by `$b:block` + ($p:path $i:ident) => {}; //~ERROR `$p:path` is followed by `$i:ident` + ($p:path $t:tt) => {}; //~ERROR `$p:path` is followed by `$t:tt` + ($p:path $i:item) => {}; //~ERROR `$p:path` is followed by `$i:item` + ($p:path $m:meta) => {}; //~ERROR `$p:path` is followed by `$m:meta` +} +// FOLLOW(block) = any token +// FOLLOW(ident) = any token + +fn main() {} + diff --git a/src/test/run-pass/macro-follow.rs b/src/test/run-pass/macro-follow.rs new file mode 100644 index 0000000000000..ce6498f67f9ee --- /dev/null +++ b/src/test/run-pass/macro-follow.rs @@ -0,0 +1,190 @@ +// Copyright 2016 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. + +// Check the macro follow sets (see corresponding cfail test). + +// FOLLOW(pat) = {FatArrow, Comma, Eq, Or, Ident(if), Ident(in)} +macro_rules! follow_pat { + ($p:pat =>) => {}; + ($p:pat ,) => {}; + ($p:pat =) => {}; + ($p:pat |) => {}; + ($p:pat if) => {}; + ($p:pat in) => {}; +} +// FOLLOW(expr) = {FatArrow, Comma, Semicolon} +macro_rules! follow_expr { + ($e:expr =>) => {}; + ($e:expr ,) => {}; + ($e:expr ;) => {}; +} +// FOLLOW(ty) = {OpenDelim(Brace), Comma, FatArrow, Colon, Eq, Gt, Semi, Or, +// Ident(as), Ident(where), OpenDelim(Bracket)} +macro_rules! follow_ty { + ($t:ty {}) => {}; + ($t:ty ,) => {}; + ($t:ty =>) => {}; + ($t:ty :) => {}; + ($t:ty =) => {}; + ($t:ty >) => {}; + ($t:ty ;) => {}; + ($t:ty |) => {}; + ($t:ty as) => {}; + ($t:ty where) => {}; + ($t:ty []) => {}; +} +// FOLLOW(stmt) = FOLLOW(expr) +macro_rules! follow_stmt { + ($s:stmt =>) => {}; + ($s:stmt ,) => {}; + ($s:stmt ;) => {}; +} +// FOLLOW(path) = FOLLOW(ty) +macro_rules! follow_path { + ($p:path {}) => {}; + ($p:path ,) => {}; + ($p:path =>) => {}; + ($p:path :) => {}; + ($p:path =) => {}; + ($p:path >) => {}; + ($p:path ;) => {}; + ($p:path |) => {}; + ($p:path as) => {}; + ($p:path where) => {}; + ($p:path []) => {}; +} +// FOLLOW(block) = any token +macro_rules! follow_block { + ($b:block ()) => {}; + ($b:block []) => {}; + ($b:block {}) => {}; + ($b:block ,) => {}; + ($b:block =>) => {}; + ($b:block :) => {}; + ($b:block =) => {}; + ($b:block >) => {}; + ($b:block ;) => {}; + ($b:block |) => {}; + ($b:block +) => {}; + ($b:block ident) => {}; + ($b:block $p:pat) => {}; + ($b:block $e:expr) => {}; + ($b:block $t:ty) => {}; + ($b:block $s:stmt) => {}; + ($b:block $p:path) => {}; + ($b:block $b:block) => {}; + ($b:block $i:ident) => {}; + ($b:block $t:tt) => {}; + ($b:block $i:item) => {}; + ($b:block $m:meta) => {}; +} +// FOLLOW(ident) = any token +macro_rules! follow_ident { + ($i:ident ()) => {}; + ($i:ident []) => {}; + ($i:ident {}) => {}; + ($i:ident ,) => {}; + ($i:ident =>) => {}; + ($i:ident :) => {}; + ($i:ident =) => {}; + ($i:ident >) => {}; + ($i:ident ;) => {}; + ($i:ident |) => {}; + ($i:ident +) => {}; + ($i:ident ident) => {}; + ($i:ident $p:pat) => {}; + ($i:ident $e:expr) => {}; + ($i:ident $t:ty) => {}; + ($i:ident $s:stmt) => {}; + ($i:ident $p:path) => {}; + ($i:ident $b:block) => {}; + ($i:ident $i:ident) => {}; + ($i:ident $t:tt) => {}; + ($i:ident $i:item) => {}; + ($i:ident $m:meta) => {}; +} +// FOLLOW(tt) = any token +macro_rules! follow_tt { + ($t:tt ()) => {}; + ($t:tt []) => {}; + ($t:tt {}) => {}; + ($t:tt ,) => {}; + ($t:tt =>) => {}; + ($t:tt :) => {}; + ($t:tt =) => {}; + ($t:tt >) => {}; + ($t:tt ;) => {}; + ($t:tt |) => {}; + ($t:tt +) => {}; + ($t:tt ident) => {}; + ($t:tt $p:pat) => {}; + ($t:tt $e:expr) => {}; + ($t:tt $t:ty) => {}; + ($t:tt $s:stmt) => {}; + ($t:tt $p:path) => {}; + ($t:tt $b:block) => {}; + ($t:tt $i:ident) => {}; + ($t:tt $t:tt) => {}; + ($t:tt $i:item) => {}; + ($t:tt $m:meta) => {}; +} +// FOLLOW(item) = any token +macro_rules! follow_item { + ($i:item ()) => {}; + ($i:item []) => {}; + ($i:item {}) => {}; + ($i:item ,) => {}; + ($i:item =>) => {}; + ($i:item :) => {}; + ($i:item =) => {}; + ($i:item >) => {}; + ($i:item ;) => {}; + ($i:item |) => {}; + ($i:item +) => {}; + ($i:item ident) => {}; + ($i:item $p:pat) => {}; + ($i:item $e:expr) => {}; + ($i:item $t:ty) => {}; + ($i:item $s:stmt) => {}; + ($i:item $p:path) => {}; + ($i:item $b:block) => {}; + ($i:item $i:ident) => {}; + ($i:item $t:tt) => {}; + ($i:item $i:item) => {}; + ($i:item $m:meta) => {}; +} +// FOLLOW(meta) = any token +macro_rules! follow_meta { + ($m:meta ()) => {}; + ($m:meta []) => {}; + ($m:meta {}) => {}; + ($m:meta ,) => {}; + ($m:meta =>) => {}; + ($m:meta :) => {}; + ($m:meta =) => {}; + ($m:meta >) => {}; + ($m:meta ;) => {}; + ($m:meta |) => {}; + ($m:meta +) => {}; + ($m:meta ident) => {}; + ($m:meta $p:pat) => {}; + ($m:meta $e:expr) => {}; + ($m:meta $t:ty) => {}; + ($m:meta $s:stmt) => {}; + ($m:meta $p:path) => {}; + ($m:meta $b:block) => {}; + ($m:meta $i:ident) => {}; + ($m:meta $t:tt) => {}; + ($m:meta $i:item) => {}; + ($m:meta $m:meta) => {}; +} + +fn main() {} + From e1e0de86ec7ae5c572560a39282b54b410b49f83 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Sat, 23 Jan 2016 13:38:18 -0500 Subject: [PATCH 09/23] add `[` to FOLLOW(ty) and FOLLOW(path) Following RFC 1462 (amending 550). Closes #31135. --- src/libsyntax/ext/tt/macro_rules.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 9f069cb17ed90..bfd76db0359bd 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -1005,7 +1005,7 @@ fn is_in_follow(_: &ExtCtxt, tok: &Token, frag: &str) -> Result { }, "path" | "ty" => { match *tok { - OpenDelim(token::DelimToken::Brace) | + OpenDelim(token::DelimToken::Brace) | OpenDelim(token::DelimToken::Bracket) | Comma | FatArrow | Colon | Eq | Gt | Semi | BinOp(token::Or) => Ok(true), Ident(i, _) if (i.name.as_str() == "as" || i.name.as_str() == "where") => Ok(true), From a469cef1abecbb0bb850f785ef6541792fdc9827 Mon Sep 17 00:00:00 2001 From: Dirk Gadsden Date: Sat, 23 Jan 2016 23:39:38 -0500 Subject: [PATCH 10/23] Clarify when `Cargo.lock` is created by `cargo build` in the book Also remove a "finally" in the section about building for release to make it feel a bit friendlier. --- src/doc/book/getting-started.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/doc/book/getting-started.md b/src/doc/book/getting-started.md index 18d5ca5be1ae3..77d468254b6c5 100644 --- a/src/doc/book/getting-started.md +++ b/src/doc/book/getting-started.md @@ -511,15 +511,17 @@ programming languages. For complex projects composed of multiple crates, it’s much easier to let Cargo coordinate the build. Using Cargo, you can run `cargo build`, and it should work the right way. -## Building for Release +### Building for Release -When your project is finally ready for release, you can use `cargo build +When your project is ready for release, you can use `cargo build --release` to compile your project with optimizations. These optimizations make your Rust code run faster, but turning them on makes your program take longer to compile. This is why there are two different profiles, one for development, and one for building the final program you’ll give to a user. -Running this command also causes Cargo to create a new file called +### What Is That `Cargo.lock`? + +Running `cargo build` also causes Cargo to create a new file called *Cargo.lock*, which looks like this: ```toml From 012d68a92e83c68e53c8c6015bfa78aa335ac332 Mon Sep 17 00:00:00 2001 From: Tshepang Lekhonkhobe Date: Thu, 21 Jan 2016 23:41:06 +0200 Subject: [PATCH 11/23] doc: miscellaneous OpenOptions:append improvements --- src/libstd/fs.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index 414a0ebd11fa2..16f2df39402f2 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -436,19 +436,19 @@ impl OpenOptions { /// Note that setting `.write(true).append(true)` has the same effect as /// setting only `.append(true)`. /// - /// For most filesystems the operating system guarantees all writes are + /// For most filesystems, the operating system guarantees that all writes are /// atomic: no writes get mangled because another process writes at the same /// time. /// /// One maybe obvious note when using append-mode: make sure that all data - /// that belongs together, is written the the file in one operation. This + /// that belongs together is written to the file in one operation. This /// can be done by concatenating strings before passing them to `write()`, - /// or using a buffered writer (with a more than adequately sized buffer) + /// or using a buffered writer (with a buffer of adequate size), /// and calling `flush()` when the message is complete. /// /// If a file is opened with both read and append access, beware that after - /// opening and after every write the position for reading may be set at the - /// end of the file. So before writing save the current position (using + /// opening, and after every write, the position for reading may be set at the + /// end of the file. So, before writing, save the current position (using /// `seek(SeekFrom::Current(0))`, and restore it before the next read. /// /// # Examples From c0984e42bd598ea119b128b4a52066dae7cb64d3 Mon Sep 17 00:00:00 2001 From: mopp Date: Sun, 24 Jan 2016 19:36:02 +0900 Subject: [PATCH 12/23] fix condition for clang --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 7df98d1bd565d..f284d13ee3bbd 100755 --- a/configure +++ b/configure @@ -1051,7 +1051,7 @@ then esac else case $CFG_CLANG_VERSION in - (3.2* | 3.3* | 3.4* | 3.5* | 3.6* | 3.7* | 3.8*) + (3.2* | 3.3* | 3.4* | 3.5* | 3.6* | 3.7* | 3.8* | 3.9*) step_msg "found ok version of CLANG: $CFG_CLANG_VERSION" ;; (*) From 47593dade78814eb8fe3c58d6ec3094e458bb80a Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sun, 24 Jan 2016 17:16:04 +0200 Subject: [PATCH 13/23] Improve the error explanations for check_const Fixes #30705 --- src/librustc_passes/diagnostics.rs | 86 +++++++++++++++++------------- 1 file changed, 50 insertions(+), 36 deletions(-) diff --git a/src/librustc_passes/diagnostics.rs b/src/librustc_passes/diagnostics.rs index 2c08cbd32338d..27354c28d85e6 100644 --- a/src/librustc_passes/diagnostics.rs +++ b/src/librustc_passes/diagnostics.rs @@ -150,26 +150,36 @@ If you really want global mutable state, try using `static mut` or a global "##, E0018: r##" -The value of static and const variables must be known at compile time. You -can't cast a pointer as an integer because we can't know what value the -address will take. -However, pointers to other constants' addresses are allowed in constants, -example: +The value of static and constant integers must be known at compile time. You +can't cast a pointer to an integer because the address of a pointer can +vary. +For example, if you write: ``` -const X: u32 = 50; -const Y: *const u32 = &X; +static MY_STATIC: u32 = 42; +static MY_STATIC_ADDR: usize = &MY_STATIC as *const _ as usize; +static WHAT: usize = (MY_STATIC_ADDR^17) + MY_STATIC_ADDR; ``` -Therefore, casting one of these non-constant pointers to an integer results -in a non-constant integer which lead to this error. Example: +Then `MY_STATIC_ADDR` would contain the address of `MY_STATIC`. However, +the address can change when the program is linked, as well as change +between different executions due to ASLR, and many linkers would +not be able to calculate the value of `WHAT`. + +On the other hand, static and constant pointers can point either to +a known numeric address or to the address of a symbol. ``` -const X: u32 = 1; -const Y: usize = &X as *const u32 as usize; -println!("{}", Y); +static MY_STATIC_ADDR: &'static u32 = &MY_STATIC; +// ... and also +static MY_STATIC_ADDR2: *const u32 = &MY_STATIC; + +const CONST_ADDR: *const u8 = 0x5f3759df as *const u8; ``` + +This does not pose a problem by itself because they can't be +accessed directly. "##, E0019: r##" @@ -347,55 +357,59 @@ From [RFC 246]: [RFC 246]: https://github.com/rust-lang/rfcs/pull/246 "##, + E0395: r##" -The value assigned to a constant expression must be known at compile time, -which is not the case when comparing raw pointers. Erroneous code example: +The value assigned to a constant scalar must be known at compile time, +which is not the case when comparing raw pointers. + +Erroneous code example: ``` -static foo: i32 = 42; -static bar: i32 = 43; +static FOO: i32 = 42; +static BAR: i32 = 42; -static baz: bool = { (&foo as *const i32) == (&bar as *const i32) }; +static BAZ: bool = { (&FOO as *const i32) == (&BAR as *const i32) }; // error: raw pointers cannot be compared in statics! ``` -Please check that the result of the comparison can be determined at compile time -or isn't assigned to a constant expression. Example: +The address assigned by the linker to `FOO` and `BAR` may or may not +be identical, so the value of `BAZ` can't be determined. + +If you want to do the comparison, please do it at run-time. + +For example: ``` -static foo: i32 = 42; -static bar: i32 = 43; +static FOO: i32 = 42; +static BAR: i32 = 42; -let baz: bool = { (&foo as *const i32) == (&bar as *const i32) }; +let baz: bool = { (&FOO as *const i32) == (&BAR as *const i32) }; // baz isn't a constant expression so it's ok ``` "##, E0396: r##" -The value assigned to a constant expression must be known at compile time, -which is not the case when dereferencing raw pointers. Erroneous code -example: +The value behind a raw pointer can't be determined at compile-time +(or even link-time), which means it can't be used in a constant +expression. +For example: ``` -const foo: i32 = 42; -const baz: *const i32 = (&foo as *const i32); +const REG_ADDR: *const u8 = 0x5f3759df as *const u8; -const deref: i32 = *baz; +const VALUE: u8 = unsafe { *REG_ADDR }; // error: raw pointers cannot be dereferenced in constants ``` -To fix this error, please do not assign this value to a constant expression. -Example: +A possible fix is to dereference your pointer at some point in run-time. -``` -const foo: i32 = 42; -const baz: *const i32 = (&foo as *const i32); +For example: -unsafe { let deref: i32 = *baz; } -// baz isn't a constant expression so it's ok ``` +const REG_ADDR: *const u8 = 0x5f3759df as *const u8; -You'll also note that this assignment must be done in an unsafe block! +let reg_value = unsafe { *REG_ADDR }; +``` "##, E0397: r##" From 73854b1619a71c9f43b3fd621f35698a1224f8ed Mon Sep 17 00:00:00 2001 From: Geoffrey Thomas Date: Wed, 20 Jan 2016 17:12:28 -0500 Subject: [PATCH 14/23] sys/unix/process.rs: Update comments in make_argv and make_envp The implementation changed in 33a2191d, but the comments did not change to match. --- src/libstd/sys/unix/process.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs index bb9d37f93ab8b..4a91cece143a9 100644 --- a/src/libstd/sys/unix/process.rs +++ b/src/libstd/sys/unix/process.rs @@ -439,11 +439,9 @@ fn make_argv(prog: &CString, args: &[CString]) { let mut ptrs: Vec<*const libc::c_char> = Vec::with_capacity(args.len()+1); - // Convert the CStrings into an array of pointers. Note: the - // lifetime of the various CStrings involved is guaranteed to be - // larger than the lifetime of our invocation of cb, but this is - // technically unsafe as the callback could leak these pointers - // out of our scope. + // Convert the CStrings into an array of pointers. Also return the + // vector that owns the raw pointers, to ensure they live long + // enough. ptrs.push(prog.as_ptr()); ptrs.extend(args.iter().map(|tmp| tmp.as_ptr())); @@ -457,10 +455,9 @@ fn make_envp(env: Option<&HashMap>) -> (*const c_void, Vec>, Vec<*const libc::c_char>) { // On posixy systems we can pass a char** for envp, which is a - // null-terminated array of "k=v\0" strings. Since we must create - // these strings locally, yet expose a raw pointer to them, we - // create a temporary vector to own the CStrings that outlives the - // call to cb. + // null-terminated array of "k=v\0" strings. As with make_argv, we + // return two vectors that own the data to ensure that they live + // long enough. if let Some(env) = env { let mut tmps = Vec::with_capacity(env.len()); From d9426210b197cb0be1db9838342349f38db135e3 Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Sun, 24 Jan 2016 20:22:24 -0500 Subject: [PATCH 15/23] Register LLVM passes with the correct LLVM pass manager. LLVM was upgraded to a new version in this commit: https://github.com/rust-lang/rust/commit/f9d4149c29e8b989fa3624993be379f380e48dcf which was part of this pull request: https://github.com/rust-lang/rust/issues/26025 Consider the following two lines from that commit: https://github.com/rust-lang/rust/commit/f9d4149c29e8b989fa3624993be379f380e48dcf#diff-a3b24dbe2ea7c1981f9ac79f9745f40aL462 https://github.com/rust-lang/rust/commit/f9d4149c29e8b989fa3624993be379f380e48dcf#diff-a3b24dbe2ea7c1981f9ac79f9745f40aL469 The purpose of these lines is to register LLVM passes. Prior to the that commit, the passes being handled were assumed to be ModulePasses (a specific type of LLVM pass) since they were being added to a ModulePass manager. After that commit, both lines were refactored (presumably in an attempt to DRY out the code), but the ModulePasses were changed to be registered to a FunctionPass manager. This change resulted in ModulePasses being run, but a Function object was being passed as a parameter to the pass instead of a Module, which resulted in segmentation faults. In this commit, I changed relevant sections of the code to check the type of the passes being added and register them to the appropriate pass manager. Closes https://github.com/rust-lang/rust/issues/31067 --- src/librustc_llvm/lib.rs | 14 ++++++++++++- src/librustc_trans/back/lto.rs | 8 +++++-- src/librustc_trans/back/write.rs | 19 ++++++++++++++--- src/rustllvm/PassWrapper.cpp | 36 ++++++++++++++++++++++++++------ 4 files changed, 65 insertions(+), 12 deletions(-) diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index fc7fa299fb8fa..0f85c4528d2e9 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -461,6 +461,15 @@ pub enum ArchiveKind { K_COFF, } +/// Represents the different LLVM passes Rust supports +#[derive(Copy, Clone, PartialEq, Debug)] +#[repr(C)] +pub enum SupportedPassKind { + Function, + Module, + Unsupported, +} + // Opaque pointer types #[allow(missing_copy_implementations)] pub enum Module_opaque {} @@ -2008,7 +2017,10 @@ extern { pub fn LLVMIsAAllocaInst(value_ref: ValueRef) -> ValueRef; pub fn LLVMIsAConstantInt(value_ref: ValueRef) -> ValueRef; - pub fn LLVMRustAddPass(PM: PassManagerRef, Pass: *const c_char) -> bool; + pub fn LLVMRustPassKind(Pass: PassRef) -> SupportedPassKind; + pub fn LLVMRustFindAndCreatePass(Pass: *const c_char) -> PassRef; + pub fn LLVMRustAddPass(PM: PassManagerRef, Pass: PassRef); + pub fn LLVMRustCreateTargetMachine(Triple: *const c_char, CPU: *const c_char, Features: *const c_char, diff --git a/src/librustc_trans/back/lto.rs b/src/librustc_trans/back/lto.rs index 85419a072503a..06d32b8f6015d 100644 --- a/src/librustc_trans/back/lto.rs +++ b/src/librustc_trans/back/lto.rs @@ -145,7 +145,9 @@ pub fn run(sess: &session::Session, llmod: ModuleRef, unsafe { let pm = llvm::LLVMCreatePassManager(); llvm::LLVMRustAddAnalysisPasses(tm, pm, llmod); - llvm::LLVMRustAddPass(pm, "verify\0".as_ptr() as *const _); + let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr() as *const _); + assert!(!pass.is_null()); + llvm::LLVMRustAddPass(pm, pass); with_llvm_pmb(llmod, config, &mut |b| { llvm::LLVMPassManagerBuilderPopulateLTOPassManager(b, pm, @@ -153,7 +155,9 @@ pub fn run(sess: &session::Session, llmod: ModuleRef, /* RunInliner = */ True); }); - llvm::LLVMRustAddPass(pm, "verify\0".as_ptr() as *const _); + let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr() as *const _); + assert!(!pass.is_null()); + llvm::LLVMRustAddPass(pm, pass); time(sess.time_passes(), "LTO passes", || llvm::LLVMRunPassManager(pm, llmod)); diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 544df1798eaf9..bc007da7174a7 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -446,9 +446,22 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, // If we're verifying or linting, add them to the function pass // manager. - let addpass = |pass: &str| { - let pass = CString::new(pass).unwrap(); - llvm::LLVMRustAddPass(fpm, pass.as_ptr()) + let addpass = |pass_name: &str| { + let pass_name = CString::new(pass_name).unwrap(); + let pass = llvm::LLVMRustFindAndCreatePass(pass_name.as_ptr()); + if pass.is_null() { + return false; + } + let pass_manager = match llvm::LLVMRustPassKind(pass) { + llvm::SupportedPassKind::Function => fpm, + llvm::SupportedPassKind::Module => mpm, + llvm::SupportedPassKind::Unsupported => { + cgcx.handler.err("Encountered LLVM pass kind we can't handle"); + return true + }, + }; + llvm::LLVMRustAddPass(pass_manager, pass); + true }; if !config.no_verify { assert!(addpass("verify")); } diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp index 30096677aa4ad..d6985719acbfa 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/src/rustllvm/PassWrapper.cpp @@ -58,19 +58,43 @@ LLVMInitializePasses() { initializeTarget(Registry); } -extern "C" bool -LLVMRustAddPass(LLVMPassManagerRef PM, const char *PassName) { - PassManagerBase *pm = unwrap(PM); +enum class SupportedPassKind { + Function, + Module, + Unsupported +}; + +extern "C" Pass* +LLVMRustFindAndCreatePass(const char *PassName) { StringRef SR(PassName); PassRegistry *PR = PassRegistry::getPassRegistry(); const PassInfo *PI = PR->getPassInfo(SR); if (PI) { - pm->add(PI->createPass()); - return true; + return PI->createPass(); } - return false; + return NULL; +} + +extern "C" SupportedPassKind +LLVMRustPassKind(Pass *pass) { + assert(pass); + PassKind passKind = pass->getPassKind(); + if (passKind == PT_Module) { + return SupportedPassKind::Module; + } else if (passKind == PT_Function) { + return SupportedPassKind::Function; + } else { + return SupportedPassKind::Unsupported; + } +} + +extern "C" void +LLVMRustAddPass(LLVMPassManagerRef PM, Pass *pass) { + assert(pass); + PassManagerBase *pm = unwrap(PM); + pm->add(pass); } extern "C" LLVMTargetMachineRef From 71656d2e6f690d8ffb67a20479bcd34ae6117a1e Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Mon, 25 Jan 2016 09:59:52 +0100 Subject: [PATCH 16/23] librustc/middle/dataflow.rs: Debug to STDERR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In 95d904625b4d45af80b4e40d51a3a0fde1abaa8a output was accidentally moved from STDERR to STDOUT. This commit also changes the order of debug output. Previously, it was: ``` /* id 22: … */ { … } DEBUG:rustc::middle::dataflow: ``` Now, it is: ``` DEBUG:rustc::middle::dataflow: /* id 22: … */ { … } ``` --- src/librustc/middle/dataflow.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/librustc/middle/dataflow.rs b/src/librustc/middle/dataflow.rs index e9029958880bd..933857269cbcd 100644 --- a/src/librustc/middle/dataflow.rs +++ b/src/librustc/middle/dataflow.rs @@ -527,8 +527,7 @@ impl<'a, 'tcx, O:DataFlowOperator+Clone+'static> DataFlowContext<'a, 'tcx, O> { debug!("{}", { let mut v = Vec::new(); self.pretty_print_to(box &mut v, blk).unwrap(); - println!("{}", String::from_utf8(v).unwrap()); - "" + String::from_utf8(v).unwrap() }); } From e0fd9c3b00a4d2818a1add4840263d936f8748cd Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Mon, 25 Jan 2016 15:17:31 +0200 Subject: [PATCH 17/23] remove implicator it is pre-RFC1214 junk --- src/librustc/lib.rs | 1 - src/librustc/middle/implicator.rs | 454 -------------------------- src/librustc_typeck/check/regionck.rs | 49 +-- 3 files changed, 1 insertion(+), 503 deletions(-) delete mode 100644 src/librustc/middle/implicator.rs diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 4d772de783594..4f6c1305f7683 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -108,7 +108,6 @@ pub mod middle { pub mod free_region; pub mod intrinsicck; pub mod infer; - pub mod implicator; pub mod lang_items; pub mod liveness; pub mod mem_categorization; diff --git a/src/librustc/middle/implicator.rs b/src/librustc/middle/implicator.rs deleted file mode 100644 index d25084bbdffb5..0000000000000 --- a/src/librustc/middle/implicator.rs +++ /dev/null @@ -1,454 +0,0 @@ -// Copyright 2012 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. - -// #![warn(deprecated_mode)] - -use middle::def_id::DefId; -use middle::infer::{InferCtxt, GenericKind}; -use middle::subst::Substs; -use middle::traits; -use middle::ty::{self, ToPredicate, Ty}; -use middle::ty::fold::{TypeFoldable, TypeFolder}; - -use syntax::ast; -use syntax::codemap::Span; - -use util::common::ErrorReported; -use util::nodemap::FnvHashSet; - -// Helper functions related to manipulating region types. - -#[derive(Debug)] -pub enum Implication<'tcx> { - RegionSubRegion(Option>, ty::Region, ty::Region), - RegionSubGeneric(Option>, ty::Region, GenericKind<'tcx>), - Predicate(DefId, ty::Predicate<'tcx>), -} - -struct Implicator<'a, 'tcx: 'a> { - infcx: &'a InferCtxt<'a,'tcx>, - body_id: ast::NodeId, - stack: Vec<(ty::Region, Option>)>, - span: Span, - out: Vec>, - visited: FnvHashSet>, -} - -/// This routine computes the well-formedness constraints that must hold for the type `ty` to -/// appear in a context with lifetime `outer_region` -pub fn implications<'a,'tcx>( - infcx: &'a InferCtxt<'a,'tcx>, - body_id: ast::NodeId, - ty: Ty<'tcx>, - outer_region: ty::Region, - span: Span) - -> Vec> -{ - debug!("implications(body_id={}, ty={:?}, outer_region={:?})", - body_id, - ty, - outer_region); - - let mut stack = Vec::new(); - stack.push((outer_region, None)); - let mut wf = Implicator { infcx: infcx, - body_id: body_id, - span: span, - stack: stack, - out: Vec::new(), - visited: FnvHashSet() }; - wf.accumulate_from_ty(ty); - debug!("implications: out={:?}", wf.out); - wf.out -} - -impl<'a, 'tcx> Implicator<'a, 'tcx> { - fn tcx(&self) -> &'a ty::ctxt<'tcx> { - self.infcx.tcx - } - - fn accumulate_from_ty(&mut self, ty: Ty<'tcx>) { - debug!("accumulate_from_ty(ty={:?})", - ty); - - // When expanding out associated types, we can visit a cyclic - // set of types. Issue #23003. - if !self.visited.insert(ty) { - return; - } - - match ty.sty { - ty::TyBool | - ty::TyChar | - ty::TyInt(..) | - ty::TyUint(..) | - ty::TyFloat(..) | - ty::TyBareFn(..) | - ty::TyError | - ty::TyStr => { - // No borrowed content reachable here. - } - - ty::TyClosure(_, ref substs) => { - // FIXME(#27086). We do not accumulate from substs, since they - // don't represent reachable data. This means that, in - // practice, some of the lifetime parameters might not - // be in scope when the body runs, so long as there is - // no reachable data with that lifetime. For better or - // worse, this is consistent with fn types, however, - // which can also encapsulate data in this fashion - // (though it's somewhat harder, and typically - // requires virtual dispatch). - // - // Note that changing this (in a naive way, at least) - // causes regressions for what appears to be perfectly - // reasonable code like this: - // - // ``` - // fn foo<'a>(p: &Data<'a>) { - // bar(|q: &mut Parser| q.read_addr()) - // } - // fn bar(p: Box) { - // } - // ``` - // - // Note that `p` (and `'a`) are not used in the - // closure at all, but to meet the requirement that - // the closure type `C: 'static` (so it can be coerced - // to the object type), we get the requirement that - // `'a: 'static` since `'a` appears in the closure - // type `C`. - // - // A smarter fix might "prune" unused `func_substs` -- - // this would avoid breaking simple examples like - // this, but would still break others (which might - // indeed be invalid, depending on your POV). Pruning - // would be a subtle process, since we have to see - // what func/type parameters are used and unused, - // taking into consideration UFCS and so forth. - - for &upvar_ty in &substs.upvar_tys { - self.accumulate_from_ty(upvar_ty); - } - } - - ty::TyTrait(ref t) => { - let required_region_bounds = - object_region_bounds(self.tcx(), &t.principal, t.bounds.builtin_bounds); - self.accumulate_from_object_ty(ty, t.bounds.region_bound, required_region_bounds) - } - - ty::TyEnum(def, substs) | - ty::TyStruct(def, substs) => { - let item_scheme = def.type_scheme(self.tcx()); - self.accumulate_from_adt(ty, def.did, &item_scheme.generics, substs) - } - - ty::TyArray(t, _) | - ty::TySlice(t) | - ty::TyRawPtr(ty::TypeAndMut { ty: t, .. }) | - ty::TyBox(t) => { - self.accumulate_from_ty(t) - } - - ty::TyRef(r_b, mt) => { - self.accumulate_from_rptr(ty, *r_b, mt.ty); - } - - ty::TyParam(p) => { - self.push_param_constraint_from_top(p); - } - - ty::TyProjection(ref data) => { - // `>::Name` - - self.push_projection_constraint_from_top(data); - } - - ty::TyTuple(ref tuptys) => { - for &tupty in tuptys { - self.accumulate_from_ty(tupty); - } - } - - ty::TyInfer(_) => { - // This should not happen, BUT: - // - // Currently we uncover region relationships on - // entering the fn check. We should do this after - // the fn check, then we can call this case a bug(). - } - } - } - - fn accumulate_from_rptr(&mut self, - ty: Ty<'tcx>, - r_b: ty::Region, - ty_b: Ty<'tcx>) { - // We are walking down a type like this, and current - // position is indicated by caret: - // - // &'a &'b ty_b - // ^ - // - // At this point, top of stack will be `'a`. We must - // require that `'a <= 'b`. - - self.push_region_constraint_from_top(r_b); - - // Now we push `'b` onto the stack, because it must - // constrain any borrowed content we find within `T`. - - self.stack.push((r_b, Some(ty))); - self.accumulate_from_ty(ty_b); - self.stack.pop().unwrap(); - } - - /// Pushes a constraint that `r_b` must outlive the top region on the stack. - fn push_region_constraint_from_top(&mut self, - r_b: ty::Region) { - - // Indicates that we have found borrowed content with a lifetime - // of at least `r_b`. This adds a constraint that `r_b` must - // outlive the region `r_a` on top of the stack. - // - // As an example, imagine walking a type like: - // - // &'a &'b T - // ^ - // - // when we hit the inner pointer (indicated by caret), `'a` will - // be on top of stack and `'b` will be the lifetime of the content - // we just found. So we add constraint that `'a <= 'b`. - - let &(r_a, opt_ty) = self.stack.last().unwrap(); - self.push_sub_region_constraint(opt_ty, r_a, r_b); - } - - /// Pushes a constraint that `r_a <= r_b`, due to `opt_ty` - fn push_sub_region_constraint(&mut self, - opt_ty: Option>, - r_a: ty::Region, - r_b: ty::Region) { - self.out.push(Implication::RegionSubRegion(opt_ty, r_a, r_b)); - } - - /// Pushes a constraint that `param_ty` must outlive the top region on the stack. - fn push_param_constraint_from_top(&mut self, - param_ty: ty::ParamTy) { - let &(region, opt_ty) = self.stack.last().unwrap(); - self.push_param_constraint(region, opt_ty, param_ty); - } - - /// Pushes a constraint that `projection_ty` must outlive the top region on the stack. - fn push_projection_constraint_from_top(&mut self, - projection_ty: &ty::ProjectionTy<'tcx>) { - let &(region, opt_ty) = self.stack.last().unwrap(); - self.out.push(Implication::RegionSubGeneric( - opt_ty, region, GenericKind::Projection(projection_ty.clone()))); - } - - /// Pushes a constraint that `region <= param_ty`, due to `opt_ty` - fn push_param_constraint(&mut self, - region: ty::Region, - opt_ty: Option>, - param_ty: ty::ParamTy) { - self.out.push(Implication::RegionSubGeneric( - opt_ty, region, GenericKind::Param(param_ty))); - } - - fn accumulate_from_adt(&mut self, - ty: Ty<'tcx>, - def_id: DefId, - _generics: &ty::Generics<'tcx>, - substs: &Substs<'tcx>) - { - let predicates = - self.tcx().lookup_predicates(def_id).instantiate(self.tcx(), substs); - let predicates = match self.fully_normalize(&predicates) { - Ok(predicates) => predicates, - Err(ErrorReported) => { return; } - }; - - for predicate in predicates.predicates.as_slice() { - match *predicate { - ty::Predicate::Trait(..) => { } - ty::Predicate::Equate(..) => { } - ty::Predicate::Projection(..) => { } - ty::Predicate::RegionOutlives(ref data) => { - match self.tcx().no_late_bound_regions(data) { - None => { } - Some(ty::OutlivesPredicate(r_a, r_b)) => { - self.push_sub_region_constraint(Some(ty), r_b, r_a); - } - } - } - ty::Predicate::TypeOutlives(ref data) => { - match self.tcx().no_late_bound_regions(data) { - None => { } - Some(ty::OutlivesPredicate(ty_a, r_b)) => { - self.stack.push((r_b, Some(ty))); - self.accumulate_from_ty(ty_a); - self.stack.pop().unwrap(); - } - } - } - ty::Predicate::ObjectSafe(_) | - ty::Predicate::WellFormed(_) => { - } - } - } - - let obligations = predicates.predicates - .into_iter() - .map(|pred| Implication::Predicate(def_id, pred)); - self.out.extend(obligations); - - let variances = self.tcx().item_variances(def_id); - self.accumulate_from_substs(substs, Some(&variances)); - } - - fn accumulate_from_substs(&mut self, - substs: &Substs<'tcx>, - variances: Option<&ty::ItemVariances>) - { - let mut tmp_variances = None; - let variances = variances.unwrap_or_else(|| { - tmp_variances = Some(ty::ItemVariances { - types: substs.types.map(|_| ty::Variance::Invariant), - regions: substs.regions().map(|_| ty::Variance::Invariant), - }); - tmp_variances.as_ref().unwrap() - }); - - for (®ion, &variance) in substs.regions().iter().zip(&variances.regions) { - match variance { - ty::Contravariant | ty::Invariant => { - // If any data with this lifetime is reachable - // within, it must be at least contravariant. - self.push_region_constraint_from_top(region) - } - ty::Covariant | ty::Bivariant => { } - } - } - - for (&ty, &variance) in substs.types.iter().zip(&variances.types) { - match variance { - ty::Covariant | ty::Invariant => { - // If any data of this type is reachable within, - // it must be at least covariant. - self.accumulate_from_ty(ty); - } - ty::Contravariant | ty::Bivariant => { } - } - } - } - - fn accumulate_from_object_ty(&mut self, - ty: Ty<'tcx>, - region_bound: ty::Region, - required_region_bounds: Vec) - { - // Imagine a type like this: - // - // trait Foo { } - // trait Bar<'c> : 'c { } - // - // &'b (Foo+'c+Bar<'d>) - // ^ - // - // In this case, the following relationships must hold: - // - // 'b <= 'c - // 'd <= 'c - // - // The first conditions is due to the normal region pointer - // rules, which say that a reference cannot outlive its - // referent. - // - // The final condition may be a bit surprising. In particular, - // you may expect that it would have been `'c <= 'd`, since - // usually lifetimes of outer things are conservative - // approximations for inner things. However, it works somewhat - // differently with trait objects: here the idea is that if the - // user specifies a region bound (`'c`, in this case) it is the - // "master bound" that *implies* that bounds from other traits are - // all met. (Remember that *all bounds* in a type like - // `Foo+Bar+Zed` must be met, not just one, hence if we write - // `Foo<'x>+Bar<'y>`, we know that the type outlives *both* 'x and - // 'y.) - // - // Note: in fact we only permit builtin traits, not `Bar<'d>`, I - // am looking forward to the future here. - - // The content of this object type must outlive - // `bounds.region_bound`: - let r_c = region_bound; - self.push_region_constraint_from_top(r_c); - - // And then, in turn, to be well-formed, the - // `region_bound` that user specified must imply the - // region bounds required from all of the trait types: - for &r_d in &required_region_bounds { - // Each of these is an instance of the `'c <= 'b` - // constraint above - self.out.push(Implication::RegionSubRegion(Some(ty), r_d, r_c)); - } - } - - fn fully_normalize(&self, value: &T) -> Result - where T : TypeFoldable<'tcx> - { - let value = - traits::fully_normalize(self.infcx, - traits::ObligationCause::misc(self.span, self.body_id), - value); - match value { - Ok(value) => Ok(value), - Err(errors) => { - // I don't like reporting these errors here, but I - // don't know where else to report them just now. And - // I don't really expect errors to arise here - // frequently. I guess the best option would be to - // propagate them out. - traits::report_fulfillment_errors(self.infcx, &errors); - Err(ErrorReported) - } - } - } -} - -/// Given an object type like `SomeTrait+Send`, computes the lifetime -/// bounds that must hold on the elided self type. These are derived -/// from the declarations of `SomeTrait`, `Send`, and friends -- if -/// they declare `trait SomeTrait : 'static`, for example, then -/// `'static` would appear in the list. The hard work is done by -/// `ty::required_region_bounds`, see that for more information. -pub fn object_region_bounds<'tcx>( - tcx: &ty::ctxt<'tcx>, - principal: &ty::PolyTraitRef<'tcx>, - others: ty::BuiltinBounds) - -> Vec -{ - // Since we don't actually *know* the self type for an object, - // this "open(err)" serves as a kind of dummy standin -- basically - // a skolemized type. - let open_ty = tcx.mk_infer(ty::FreshTy(0)); - - // Note that we preserve the overall binding levels here. - assert!(!open_ty.has_escaping_regions()); - let substs = tcx.mk_substs(principal.0.substs.with_self_ty(open_ty)); - let trait_refs = vec!(ty::Binder(ty::TraitRef::new(principal.0.def_id, substs))); - - let mut predicates = others.to_predicates(tcx, open_ty); - predicates.extend(trait_refs.iter().map(|t| t.to_predicate())); - - tcx.required_region_bounds(open_ty, predicates) -} diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 47cd31d9898d1..56b02412c31b2 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -86,7 +86,6 @@ use astconv::AstConv; use check::dropck; use check::FnCtxt; use middle::free_region::FreeRegionMap; -use middle::implicator::{self, Implication}; use middle::mem_categorization as mc; use middle::mem_categorization::Categorization; use middle::region::{self, CodeExtent}; @@ -365,12 +364,7 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> { r_o, r_o.cause); let sup_type = self.resolve_type(r_o.sup_type); let origin = self.code_to_origin(r_o.cause.span, sup_type, &r_o.cause.code); - - if r_o.sub_region != ty::ReEmpty { - type_must_outlive(self, origin, sup_type, r_o.sub_region); - } else { - self.visit_old_school_wf(node_id, sup_type, origin); - } + type_must_outlive(self, origin, sup_type, r_o.sub_region); } // Processing the region obligations should not cause the list to grow further: @@ -378,47 +372,6 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> { self.fcx.inh.infcx.fulfillment_cx.borrow().region_obligations(node_id).len()); } - fn visit_old_school_wf(&mut self, - body_id: ast::NodeId, - ty: Ty<'tcx>, - origin: infer::SubregionOrigin<'tcx>) { - // As a weird kind of hack, we use a region of empty as a signal - // to mean "old-school WF rules". The only reason the old-school - // WF rules are not encoded using WF is that this leads to errors, - // and we want to phase those in gradually. - - // FIXME(#27579) remove this weird special case once we phase in new WF rules completely - let implications = implicator::implications(self.infcx(), - body_id, - ty, - ty::ReEmpty, - origin.span()); - let origin_for_ty = |ty: Option>| match ty { - None => origin.clone(), - Some(ty) => infer::ReferenceOutlivesReferent(ty, origin.span()), - }; - for implication in implications { - match implication { - Implication::RegionSubRegion(ty, r1, r2) => { - self.fcx.mk_subr(origin_for_ty(ty), r1, r2); - } - Implication::RegionSubGeneric(ty, r1, GenericKind::Param(param_ty)) => { - param_ty_must_outlive(self, origin_for_ty(ty), r1, param_ty); - } - Implication::RegionSubGeneric(ty, r1, GenericKind::Projection(proj_ty)) => { - projection_must_outlive(self, origin_for_ty(ty), r1, proj_ty); - } - Implication::Predicate(def_id, predicate) => { - let cause = traits::ObligationCause::new(origin.span(), - body_id, - traits::ItemObligation(def_id)); - let obligation = traits::Obligation::new(cause, predicate); - self.fcx.register_predicate(obligation); - } - } - } - } - fn code_to_origin(&self, span: Span, sup_type: Ty<'tcx>, From 43b3681588ef40c78c794548d67a8f50101bc8ad Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Fri, 22 Jan 2016 08:55:54 +1300 Subject: [PATCH 18/23] Fix a rebasing issue and addressed reviewer comment --- src/librustc/middle/cstore.rs | 32 ++++++++++++++++++++------------ src/librustc_resolve/lib.rs | 2 +- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index 756d708732a4e..973fd65beb3d8 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -267,20 +267,28 @@ impl InlinedItem { // FIXME: find a better place for this? pub fn validate_crate_name(sess: Option<&Session>, s: &str, sp: Option) { - let say = |s: &str| { - match (sp, sess) { - (_, None) => panic!("{}", s), - (Some(sp), Some(sess)) => sess.span_fatal(sp, s), - (None, Some(sess)) => sess.fatal(s), + let mut err_count = 0; + { + let mut say = |s: &str| { + match (sp, sess) { + (_, None) => panic!("{}", s), + (Some(sp), Some(sess)) => sess.span_err(sp, s), + (None, Some(sess)) => sess.err(s), + } + err_count += 1; + }; + if s.is_empty() { + say("crate name must not be empty"); + } + for c in s.chars() { + if c.is_alphanumeric() { continue } + if c == '_' { continue } + say(&format!("invalid character `{}` in crate name: `{}`", c, s)); } - }; - if s.is_empty() { - say("crate name must not be empty"); } - for c in s.chars() { - if c.is_alphanumeric() { continue } - if c == '_' { continue } - say(&format!("invalid character `{}` in crate name: `{}`", c, s)); + + if err_count > 0 { + sess.unwrap().abort_if_errors(); } } diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index e30fafda63cb4..c698f72955320 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -3038,7 +3038,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { record_used: bool) -> Option { if identifier.name == special_idents::invalid.name { - return Some(LocalDef::from_def(DefErr)); + return Some(LocalDef::from_def(Def::Err)); } // First, check to see whether the name is a primitive type. From 03681b16ce1fd5c36069363ef8134f36b01a9022 Mon Sep 17 00:00:00 2001 From: Oliver Middleton Date: Mon, 25 Jan 2016 18:34:34 +0000 Subject: [PATCH 19/23] Fix link to hello-cargo in the book It was moved in #29538. --- src/doc/book/error-handling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/book/error-handling.md b/src/doc/book/error-handling.md index 9b1d16170b97f..40891dbe191e9 100644 --- a/src/doc/book/error-handling.md +++ b/src/doc/book/error-handling.md @@ -1512,7 +1512,7 @@ and [`rustc-serialize`](https://crates.io/crates/rustc-serialize) crates. We're not going to spend a lot of time on setting up a project with Cargo because it is already covered well in [the Cargo -section](../book/hello-cargo.html) and [Cargo's documentation][14]. +section](getting-started.html#hello-cargo) and [Cargo's documentation][14]. To get started from scratch, run `cargo new --bin city-pop` and make sure your `Cargo.toml` looks something like this: From ace39cbc15488eb949ef4dc13a10137b170709b1 Mon Sep 17 00:00:00 2001 From: Oliver Middleton Date: Mon, 25 Jan 2016 18:40:28 +0000 Subject: [PATCH 20/23] Replace link to learn-rust in the book It was removed in #30595. Also delete the old learn-rust.md. --- src/doc/book/getting-started.md | 6 +++--- src/doc/book/learn-rust.md | 9 --------- 2 files changed, 3 insertions(+), 12 deletions(-) delete mode 100644 src/doc/book/learn-rust.md diff --git a/src/doc/book/getting-started.md b/src/doc/book/getting-started.md index 77d468254b6c5..ed4d7cc40bed6 100644 --- a/src/doc/book/getting-started.md +++ b/src/doc/book/getting-started.md @@ -604,11 +604,11 @@ This chapter covered the basics that will serve you well through the rest of this book, and the rest of your time with Rust. Now that you’ve got the tools down, we'll cover more about the Rust language itself. -You have two options: Dive into a project with ‘[Learn Rust][learnrust]’, or +You have two options: Dive into a project with ‘[Tutorial: Guessing Game][guessinggame]’, or start from the bottom and work your way up with ‘[Syntax and Semantics][syntax]’. More experienced systems programmers will probably prefer -‘Learn Rust’, while those from dynamic backgrounds may enjoy either. Different +‘Tutorial: Guessing Game’, while those from dynamic backgrounds may enjoy either. Different people learn differently! Choose whatever’s right for you. -[learnrust]: learn-rust.html +[guessinggame]: guessing-game.html [syntax]: syntax-and-semantics.html diff --git a/src/doc/book/learn-rust.md b/src/doc/book/learn-rust.md deleted file mode 100644 index 7be7fa4f039a7..0000000000000 --- a/src/doc/book/learn-rust.md +++ /dev/null @@ -1,9 +0,0 @@ -% Learn Rust - -Welcome! This chapter has a few tutorials that teach you Rust through building -projects. You’ll get a high-level overview, but we’ll skim over the details. - -If you’d prefer a more ‘from the ground up’-style experience, check -out [Syntax and Semantics][ss]. - -[ss]: syntax-and-semantics.html From a19353643bf9ad38dfa05fbea94c01b795989569 Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Mon, 25 Jan 2016 17:07:55 -0500 Subject: [PATCH 21/23] RefCell::borrow_mut example should demonstrate mut --- src/libcore/cell.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index 789b75836d0a7..6041355e9dbc0 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -414,7 +414,9 @@ impl RefCell { /// /// let c = RefCell::new(5); /// - /// let borrowed_five = c.borrow_mut(); + /// *c.borrow_mut() = 7; + /// + /// assert_eq!(*c.borrow(), 7); /// ``` /// /// An example of panic: From b3ebe949e2db2f6c2d2d10ce050370de5fd40d02 Mon Sep 17 00:00:00 2001 From: Andrew Paseltiner Date: Mon, 25 Jan 2016 21:33:23 -0500 Subject: [PATCH 22/23] Fix typo in "Loops" section of the book Closes #31195 --- src/doc/book/loops.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/book/loops.md b/src/doc/book/loops.md index 68bb49d2c2966..5b08c2fb04dbd 100644 --- a/src/doc/book/loops.md +++ b/src/doc/book/loops.md @@ -195,7 +195,7 @@ for x in 0..10 { You may also encounter situations where you have nested loops and need to specify which one your `break` or `continue` statement is for. Like most other languages, by default a `break` or `continue` will apply to innermost -loop. In a situation where you would like to a `break` or `continue` for one +loop. In a situation where you would like to `break` or `continue` for one of the outer loops, you can use labels to specify which loop the `break` or `continue` statement applies to. This will only print when both `x` and `y` are odd: From 6c56260b302d5b21ff780fc60aede25b40e3df76 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 25 Jan 2016 22:26:56 -0500 Subject: [PATCH 23/23] Mention the need for a linker Fixes #31181 --- src/doc/book/getting-started.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/doc/book/getting-started.md b/src/doc/book/getting-started.md index 18d5ca5be1ae3..ad68540a97815 100644 --- a/src/doc/book/getting-started.md +++ b/src/doc/book/getting-started.md @@ -167,6 +167,10 @@ variable. If it isn't, run the installer again, select "Change" on the "Change, repair, or remove installation" page and ensure "Add to PATH" is installed on the local hard drive. +Rust does not do its own linking, and so you’ll need to have a linker +installed. Doing so will depend on your specific system, consult its +documentation for more details. + If not, there are a number of places where we can get help. The easiest is [the #rust IRC channel on irc.mozilla.org][irc], which we can access through [Mibbit][mibbit]. Click that link, and we'll be chatting with other Rustaceans