Rollup merge of #37412 - eddyb:lazy-6, r=nikomatsakis
[6/n] rustc: transition HIR function bodies from Block to Expr. _This is part of a series ([prev](https://github.com/rust-lang/rust/pull/37408) | [next](https://github.com/rust-lang/rust/pull/37676)) of patches designed to rework rustc into an out-of-order on-demand pipeline model for both better feature support (e.g. [MIR-based](https://github.com/solson/miri) early constant evaluation) and incremental execution of compiler passes (e.g. type-checking), with beneficial consequences to IDE support as well. If any motivation is unclear, please ask for additional PR description clarifications or code comments._ <hr> The main change here is that functions and closures both use `Expr` instead of `Block` for their bodies. For closures this actually allows a honest representation of brace-less closure bodies, e.g. `|x| x + 1` is now distinguishable from `|x| { x + 1 }`, therefore this PR is `[syntax-breaking]` (cc @Manishearth). Using `Expr` allows more logic to be shared between constant bodies and function bodies, with some small such changes already part of this PR, and eventually easing #35078 and per-body type tables. Incidentally, there used to be some corners cut here and there and as such I had to (re)write divergence tracking for type-checking so that it is capable of understanding basic structured control-flow: ``` rust fn a(x: bool) -> i32 { // match also works (as long as all arms diverge) if x { panic!("true") } else { return 1; } 0 // "unreachable expression" after this PR } ``` And since liveness' "not all control paths return a value" moved to type-checking we can have nice things: ``` rust // before & after: fn b() -> i32 { 0; } // help: consider removing this semicolon // only after this PR fn c() -> i32 { { 0; } } // help: consider removing this semicolon fn d() { let x: i32 = { 0; }; } // help: consider removing this semicolon fn e() { f({ 0; }); } // help: consider removing this semicolon ```
This commit is contained in:
commit
368281a110
@ -53,7 +53,7 @@ pub unsafe extern fn __rust_maybe_catch_panic(f: fn(*mut u8),
|
||||
// now hopefully.
|
||||
#[no_mangle]
|
||||
pub unsafe extern fn __rust_start_panic(_data: usize, _vtable: usize) -> u32 {
|
||||
return abort();
|
||||
abort();
|
||||
|
||||
#[cfg(unix)]
|
||||
unsafe fn abort() -> ! {
|
||||
|
@ -33,16 +33,16 @@ struct LoopScope {
|
||||
}
|
||||
|
||||
pub fn construct<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
blk: &hir::Block) -> CFG {
|
||||
body: &hir::Expr) -> CFG {
|
||||
let mut graph = graph::Graph::new();
|
||||
let entry = graph.add_node(CFGNodeData::Entry);
|
||||
|
||||
// `fn_exit` is target of return exprs, which lies somewhere
|
||||
// outside input `blk`. (Distinguishing `fn_exit` and `block_exit`
|
||||
// outside input `body`. (Distinguishing `fn_exit` and `body_exit`
|
||||
// also resolves chicken-and-egg problem that arises if you try to
|
||||
// have return exprs jump to `block_exit` during construction.)
|
||||
// have return exprs jump to `body_exit` during construction.)
|
||||
let fn_exit = graph.add_node(CFGNodeData::Exit);
|
||||
let block_exit;
|
||||
let body_exit;
|
||||
|
||||
let mut cfg_builder = CFGBuilder {
|
||||
graph: graph,
|
||||
@ -50,8 +50,8 @@ pub fn construct<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
tcx: tcx,
|
||||
loop_scopes: Vec::new()
|
||||
};
|
||||
block_exit = cfg_builder.block(blk, entry);
|
||||
cfg_builder.add_contained_edge(block_exit, fn_exit);
|
||||
body_exit = cfg_builder.expr(body, entry);
|
||||
cfg_builder.add_contained_edge(body_exit, fn_exit);
|
||||
let CFGBuilder {graph, ..} = cfg_builder;
|
||||
CFG {graph: graph,
|
||||
entry: entry,
|
||||
|
@ -59,8 +59,8 @@ pub type CFGEdge = graph::Edge<CFGEdgeData>;
|
||||
|
||||
impl CFG {
|
||||
pub fn new<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
blk: &hir::Block) -> CFG {
|
||||
construct::construct(tcx, blk)
|
||||
body: &hir::Expr) -> CFG {
|
||||
construct::construct(tcx, body)
|
||||
}
|
||||
|
||||
pub fn node_is_reachable(&self, id: ast::NodeId) -> bool {
|
||||
|
@ -672,120 +672,6 @@ extern "C" {
|
||||
```
|
||||
"##,
|
||||
|
||||
E0269: r##"
|
||||
A returned value was expected but not all control paths return one.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,E0269
|
||||
fn abracada_FAIL() -> String {
|
||||
"this won't work".to_string();
|
||||
// error: not all control paths return a value
|
||||
}
|
||||
```
|
||||
|
||||
In the previous code, the function is supposed to return a `String`, however,
|
||||
the code returns nothing (because of the ';'). Another erroneous code would be:
|
||||
|
||||
```compile_fail
|
||||
fn abracada_FAIL(b: bool) -> u32 {
|
||||
if b {
|
||||
0
|
||||
} else {
|
||||
"a" // It fails because an `u32` was expected and something else is
|
||||
// returned.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
It is advisable to find out what the unhandled cases are and check for them,
|
||||
returning an appropriate value or panicking if necessary. Check if you need
|
||||
to remove a semicolon from the last expression, like in the first erroneous
|
||||
code example.
|
||||
"##,
|
||||
|
||||
E0270: r##"
|
||||
Rust lets you define functions which are known to never return, i.e. are
|
||||
'diverging', by marking its return type as `!`.
|
||||
|
||||
For example, the following functions never return:
|
||||
|
||||
```no_run
|
||||
fn foo() -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn bar() -> ! {
|
||||
foo() // foo() is diverging, so this will diverge too
|
||||
}
|
||||
|
||||
fn baz() -> ! {
|
||||
panic!(); // this macro internally expands to a call to a diverging function
|
||||
}
|
||||
```
|
||||
|
||||
Such functions can be used in a place where a value is expected without
|
||||
returning a value of that type, for instance:
|
||||
|
||||
```no_run
|
||||
fn foo() -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
let x = 3;
|
||||
|
||||
let y = match x {
|
||||
1 => 1,
|
||||
2 => 4,
|
||||
_ => foo() // diverging function called here
|
||||
};
|
||||
|
||||
println!("{}", y)
|
||||
```
|
||||
|
||||
If the third arm of the match block is reached, since `foo()` doesn't ever
|
||||
return control to the match block, it is fine to use it in a place where an
|
||||
integer was expected. The `match` block will never finish executing, and any
|
||||
point where `y` (like the print statement) is needed will not be reached.
|
||||
|
||||
However, if we had a diverging function that actually does finish execution:
|
||||
|
||||
```ignore
|
||||
fn foo() -> ! {
|
||||
loop {break;}
|
||||
}
|
||||
```
|
||||
|
||||
Then we would have an unknown value for `y` in the following code:
|
||||
|
||||
```no_run
|
||||
fn foo() -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
let x = 3;
|
||||
|
||||
let y = match x {
|
||||
1 => 1,
|
||||
2 => 4,
|
||||
_ => foo()
|
||||
};
|
||||
|
||||
println!("{}", y);
|
||||
```
|
||||
|
||||
In the previous example, the print statement was never reached when the
|
||||
wildcard match arm was hit, so we were okay with `foo()` not returning an
|
||||
integer that we could set to `y`. But in this example, `foo()` actually does
|
||||
return control, so the print statement will be executed with an uninitialized
|
||||
value.
|
||||
|
||||
Obviously we cannot have functions which are allowed to be used in such
|
||||
positions and yet can return control. So, if you are defining a function that
|
||||
returns `!`, make sure that there is no way for it to actually finish
|
||||
executing.
|
||||
"##,
|
||||
|
||||
E0271: r##"
|
||||
This is because of a type mismatch between the associated type of some
|
||||
trait (e.g. `T::Bar`, where `T` implements `trait Quux { type Bar; }`)
|
||||
|
@ -138,7 +138,7 @@ pub trait Visitor<'v> : Sized {
|
||||
fn visit_where_predicate(&mut self, predicate: &'v WherePredicate) {
|
||||
walk_where_predicate(self, predicate)
|
||||
}
|
||||
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl, b: &'v Block, s: Span, id: NodeId) {
|
||||
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl, b: &'v Expr, s: Span, id: NodeId) {
|
||||
walk_fn(self, fk, fd, b, s, id)
|
||||
}
|
||||
fn visit_trait_item(&mut self, ti: &'v TraitItem) {
|
||||
@ -635,13 +635,13 @@ pub fn walk_fn_kind<'v, V: Visitor<'v>>(visitor: &mut V, function_kind: FnKind<'
|
||||
pub fn walk_fn<'v, V: Visitor<'v>>(visitor: &mut V,
|
||||
function_kind: FnKind<'v>,
|
||||
function_declaration: &'v FnDecl,
|
||||
function_body: &'v Block,
|
||||
function_body: &'v Expr,
|
||||
_span: Span,
|
||||
id: NodeId) {
|
||||
visitor.visit_id(id);
|
||||
walk_fn_decl(visitor, function_declaration);
|
||||
walk_fn_kind(visitor, function_kind);
|
||||
visitor.visit_block(function_body)
|
||||
visitor.visit_expr(function_body)
|
||||
}
|
||||
|
||||
pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v TraitItem) {
|
||||
@ -925,7 +925,7 @@ impl<'v> Visitor<'v> for IdRangeComputingVisitor {
|
||||
/// Computes the id range for a single fn body, ignoring nested items.
|
||||
pub fn compute_id_range_for_fn_body(fk: FnKind,
|
||||
decl: &FnDecl,
|
||||
body: &Block,
|
||||
body: &Expr,
|
||||
sp: Span,
|
||||
id: NodeId)
|
||||
-> IdRange {
|
||||
|
@ -595,12 +595,13 @@ impl<'a> LoweringContext<'a> {
|
||||
hir::ItemConst(self.lower_ty(t), self.lower_expr(e))
|
||||
}
|
||||
ItemKind::Fn(ref decl, unsafety, constness, abi, ref generics, ref body) => {
|
||||
let body = self.lower_block(body);
|
||||
hir::ItemFn(self.lower_fn_decl(decl),
|
||||
self.lower_unsafety(unsafety),
|
||||
self.lower_constness(constness),
|
||||
abi,
|
||||
self.lower_generics(generics),
|
||||
self.lower_block(body))
|
||||
self.expr_block(body, ThinVec::new()))
|
||||
}
|
||||
ItemKind::Mod(ref m) => hir::ItemMod(self.lower_mod(m)),
|
||||
ItemKind::ForeignMod(ref nm) => hir::ItemForeignMod(self.lower_foreign_mod(nm)),
|
||||
@ -665,7 +666,10 @@ impl<'a> LoweringContext<'a> {
|
||||
}
|
||||
TraitItemKind::Method(ref sig, ref body) => {
|
||||
hir::MethodTraitItem(this.lower_method_sig(sig),
|
||||
body.as_ref().map(|x| this.lower_block(x)))
|
||||
body.as_ref().map(|x| {
|
||||
let body = this.lower_block(x);
|
||||
this.expr_block(body, ThinVec::new())
|
||||
}))
|
||||
}
|
||||
TraitItemKind::Type(ref bounds, ref default) => {
|
||||
hir::TypeTraitItem(this.lower_bounds(bounds),
|
||||
@ -691,8 +695,9 @@ impl<'a> LoweringContext<'a> {
|
||||
hir::ImplItemKind::Const(this.lower_ty(ty), this.lower_expr(expr))
|
||||
}
|
||||
ImplItemKind::Method(ref sig, ref body) => {
|
||||
let body = this.lower_block(body);
|
||||
hir::ImplItemKind::Method(this.lower_method_sig(sig),
|
||||
this.lower_block(body))
|
||||
this.expr_block(body, ThinVec::new()))
|
||||
}
|
||||
ImplItemKind::Type(ref ty) => hir::ImplItemKind::Type(this.lower_ty(ty)),
|
||||
ImplItemKind::Macro(..) => panic!("Shouldn't exist any more"),
|
||||
@ -1110,7 +1115,7 @@ impl<'a> LoweringContext<'a> {
|
||||
self.with_parent_def(e.id, |this| {
|
||||
hir::ExprClosure(this.lower_capture_clause(capture_clause),
|
||||
this.lower_fn_decl(decl),
|
||||
this.lower_block(body),
|
||||
this.lower_expr(body),
|
||||
fn_decl_span)
|
||||
})
|
||||
}
|
||||
|
@ -21,11 +21,9 @@
|
||||
//! nested within a uniquely determined `FnLike`), and users can ask
|
||||
//! for the `Code` associated with a particular NodeId.
|
||||
|
||||
pub use self::Code::*;
|
||||
|
||||
use hir as ast;
|
||||
use hir::map::{self, Node};
|
||||
use hir::{Block, FnDecl};
|
||||
use hir::{Expr, FnDecl};
|
||||
use hir::intravisit::FnKind;
|
||||
use syntax::abi;
|
||||
use syntax::ast::{Attribute, Name, NodeId};
|
||||
@ -50,7 +48,7 @@ pub trait MaybeFnLike { fn is_fn_like(&self) -> bool; }
|
||||
/// Components shared by fn-like things (fn items, methods, closures).
|
||||
pub struct FnParts<'a> {
|
||||
pub decl: &'a FnDecl,
|
||||
pub body: &'a Block,
|
||||
pub body: &'a Expr,
|
||||
pub kind: FnKind<'a>,
|
||||
pub span: Span,
|
||||
pub id: NodeId,
|
||||
@ -77,29 +75,32 @@ impl MaybeFnLike for ast::Expr {
|
||||
}
|
||||
}
|
||||
|
||||
/// Carries either an FnLikeNode or a Block, as these are the two
|
||||
/// Carries either an FnLikeNode or a Expr, as these are the two
|
||||
/// constructs that correspond to "code" (as in, something from which
|
||||
/// we can construct a control-flow graph).
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Code<'a> {
|
||||
FnLikeCode(FnLikeNode<'a>),
|
||||
BlockCode(&'a Block),
|
||||
FnLike(FnLikeNode<'a>),
|
||||
Expr(&'a Expr),
|
||||
}
|
||||
|
||||
impl<'a> Code<'a> {
|
||||
pub fn id(&self) -> NodeId {
|
||||
match *self {
|
||||
FnLikeCode(node) => node.id(),
|
||||
BlockCode(block) => block.id,
|
||||
Code::FnLike(node) => node.id(),
|
||||
Code::Expr(block) => block.id,
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to construct a Code from presumed FnLike or Block node input.
|
||||
pub fn from_node(node: Node) -> Option<Code> {
|
||||
if let map::NodeBlock(block) = node {
|
||||
Some(BlockCode(block))
|
||||
} else {
|
||||
FnLikeNode::from_node(node).map(|fn_like| FnLikeCode(fn_like))
|
||||
/// Attempts to construct a Code from presumed FnLike or Expr node input.
|
||||
pub fn from_node(map: &map::Map<'a>, id: NodeId) -> Option<Code<'a>> {
|
||||
match map.get(id) {
|
||||
map::NodeBlock(_) => {
|
||||
// Use the parent, hopefully an expression node.
|
||||
Code::from_node(map, map.get_parent_node(id))
|
||||
}
|
||||
map::NodeExpr(expr) => Some(Code::Expr(expr)),
|
||||
node => FnLikeNode::from_node(node).map(Code::FnLike)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -114,7 +115,7 @@ struct ItemFnParts<'a> {
|
||||
abi: abi::Abi,
|
||||
vis: &'a ast::Visibility,
|
||||
generics: &'a ast::Generics,
|
||||
body: &'a Block,
|
||||
body: &'a Expr,
|
||||
id: NodeId,
|
||||
span: Span,
|
||||
attrs: &'a [Attribute],
|
||||
@ -124,14 +125,14 @@ struct ItemFnParts<'a> {
|
||||
/// for use when implementing FnLikeNode operations.
|
||||
struct ClosureParts<'a> {
|
||||
decl: &'a FnDecl,
|
||||
body: &'a Block,
|
||||
body: &'a Expr,
|
||||
id: NodeId,
|
||||
span: Span,
|
||||
attrs: &'a [Attribute],
|
||||
}
|
||||
|
||||
impl<'a> ClosureParts<'a> {
|
||||
fn new(d: &'a FnDecl, b: &'a Block, id: NodeId, s: Span, attrs: &'a [Attribute]) -> Self {
|
||||
fn new(d: &'a FnDecl, b: &'a Expr, id: NodeId, s: Span, attrs: &'a [Attribute]) -> Self {
|
||||
ClosureParts {
|
||||
decl: d,
|
||||
body: b,
|
||||
@ -171,9 +172,9 @@ impl<'a> FnLikeNode<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn body(self) -> &'a Block {
|
||||
pub fn body(self) -> &'a Expr {
|
||||
self.handle(|i: ItemFnParts<'a>| &*i.body,
|
||||
|_, _, _: &'a ast::MethodSig, _, body: &'a ast::Block, _, _| body,
|
||||
|_, _, _: &'a ast::MethodSig, _, body: &'a ast::Expr, _, _| body,
|
||||
|c: ClosureParts<'a>| c.body)
|
||||
}
|
||||
|
||||
@ -214,7 +215,7 @@ impl<'a> FnLikeNode<'a> {
|
||||
Name,
|
||||
&'a ast::MethodSig,
|
||||
Option<&'a ast::Visibility>,
|
||||
&'a ast::Block,
|
||||
&'a ast::Expr,
|
||||
Span,
|
||||
&'a [Attribute])
|
||||
-> A,
|
||||
|
@ -211,7 +211,7 @@ impl<'ast> Visitor<'ast> for NodeCollector<'ast> {
|
||||
}
|
||||
|
||||
fn visit_fn(&mut self, fk: intravisit::FnKind<'ast>, fd: &'ast FnDecl,
|
||||
b: &'ast Block, s: Span, id: NodeId) {
|
||||
b: &'ast Expr, s: Span, id: NodeId) {
|
||||
assert_eq!(self.parent_node, id);
|
||||
intravisit::walk_fn(self, fk, fd, b, s, id);
|
||||
}
|
||||
|
@ -904,7 +904,7 @@ pub enum Expr_ {
|
||||
/// A closure (for example, `move |a, b, c| {a + b + c}`).
|
||||
///
|
||||
/// The final span is the span of the argument block `|...|`
|
||||
ExprClosure(CaptureClause, P<FnDecl>, P<Block>, Span),
|
||||
ExprClosure(CaptureClause, P<FnDecl>, P<Expr>, Span),
|
||||
/// A block (`{ ... }`)
|
||||
ExprBlock(P<Block>),
|
||||
|
||||
@ -1035,7 +1035,7 @@ pub enum TraitItem_ {
|
||||
/// must contain a value)
|
||||
ConstTraitItem(P<Ty>, Option<P<Expr>>),
|
||||
/// A method with an optional body
|
||||
MethodTraitItem(MethodSig, Option<P<Block>>),
|
||||
MethodTraitItem(MethodSig, Option<P<Expr>>),
|
||||
/// An associated type with (possibly empty) bounds and optional concrete
|
||||
/// type
|
||||
TypeTraitItem(TyParamBounds, Option<P<Ty>>),
|
||||
@ -1060,7 +1060,7 @@ pub enum ImplItemKind {
|
||||
/// of the expression
|
||||
Const(P<Ty>, P<Expr>),
|
||||
/// A method implementation with the given signature and body
|
||||
Method(MethodSig, P<Block>),
|
||||
Method(MethodSig, P<Expr>),
|
||||
/// An associated type
|
||||
Type(P<Ty>),
|
||||
}
|
||||
@ -1501,7 +1501,7 @@ pub enum Item_ {
|
||||
/// A `const` item
|
||||
ItemConst(P<Ty>, P<Expr>),
|
||||
/// A function declaration
|
||||
ItemFn(P<FnDecl>, Unsafety, Constness, Abi, Generics, P<Block>),
|
||||
ItemFn(P<FnDecl>, Unsafety, Constness, Abi, Generics, P<Expr>),
|
||||
/// A module
|
||||
ItemMod(Mod),
|
||||
/// An external module
|
||||
|
@ -713,7 +713,9 @@ impl<'a> State<'a> {
|
||||
typarams,
|
||||
&item.vis)?;
|
||||
word(&mut self.s, " ")?;
|
||||
self.print_block_with_attrs(&body, &item.attrs)?;
|
||||
self.end()?; // need to close a box
|
||||
self.end()?; // need to close a box
|
||||
self.print_expr(&body)?;
|
||||
}
|
||||
hir::ItemMod(ref _mod) => {
|
||||
self.head(&visibility_qualified(&item.vis, "mod"))?;
|
||||
@ -1002,7 +1004,9 @@ impl<'a> State<'a> {
|
||||
self.print_method_sig(ti.name, sig, &hir::Inherited)?;
|
||||
if let Some(ref body) = *body {
|
||||
self.nbsp()?;
|
||||
self.print_block_with_attrs(body, &ti.attrs)?;
|
||||
self.end()?; // need to close a box
|
||||
self.end()?; // need to close a box
|
||||
self.print_expr(body)?;
|
||||
} else {
|
||||
word(&mut self.s, ";")?;
|
||||
}
|
||||
@ -1034,7 +1038,9 @@ impl<'a> State<'a> {
|
||||
self.head("")?;
|
||||
self.print_method_sig(ii.name, sig, &ii.vis)?;
|
||||
self.nbsp()?;
|
||||
self.print_block_with_attrs(body, &ii.attrs)?;
|
||||
self.end()?; // need to close a box
|
||||
self.end()?; // need to close a box
|
||||
self.print_expr(body)?;
|
||||
}
|
||||
hir::ImplItemKind::Type(ref ty) => {
|
||||
self.print_associated_type(ii.name, None, Some(ty))?;
|
||||
@ -1402,26 +1408,10 @@ impl<'a> State<'a> {
|
||||
self.print_fn_block_args(&decl)?;
|
||||
space(&mut self.s)?;
|
||||
|
||||
let default_return = match decl.output {
|
||||
hir::DefaultReturn(..) => true,
|
||||
_ => false,
|
||||
};
|
||||
// this is a bare expression
|
||||
self.print_expr(body)?;
|
||||
self.end()?; // need to close a box
|
||||
|
||||
if !default_return || !body.stmts.is_empty() || body.expr.is_none() {
|
||||
self.print_block_unclosed(&body)?;
|
||||
} else {
|
||||
// we extract the block, so as not to create another set of boxes
|
||||
match body.expr.as_ref().unwrap().node {
|
||||
hir::ExprBlock(ref blk) => {
|
||||
self.print_block_unclosed(&blk)?;
|
||||
}
|
||||
_ => {
|
||||
// this is a bare expression
|
||||
self.print_expr(body.expr.as_ref().map(|e| &**e).unwrap())?;
|
||||
self.end()?; // need to close a box
|
||||
}
|
||||
}
|
||||
}
|
||||
// a box will be closed by print_expr, but we didn't want an overall
|
||||
// wrapper so we closed the corresponding opening. so create an
|
||||
// empty box to satisfy the close.
|
||||
|
@ -838,7 +838,7 @@ impl<'a, 'tcx, 'v> hir_visit::Visitor<'v> for LateContext<'a, 'tcx> {
|
||||
}
|
||||
|
||||
fn visit_fn(&mut self, fk: hir_visit::FnKind<'v>, decl: &'v hir::FnDecl,
|
||||
body: &'v hir::Block, span: Span, id: ast::NodeId) {
|
||||
body: &'v hir::Expr, span: Span, id: ast::NodeId) {
|
||||
run_lints!(self, check_fn, late_passes, fk, decl, body, span, id);
|
||||
hir_visit::walk_fn(self, fk, decl, body, span, id);
|
||||
run_lints!(self, check_fn_post, late_passes, fk, decl, body, span, id);
|
||||
@ -994,10 +994,10 @@ impl<'a> ast_visit::Visitor for EarlyContext<'a> {
|
||||
}
|
||||
|
||||
fn visit_fn(&mut self, fk: ast_visit::FnKind, decl: &ast::FnDecl,
|
||||
body: &ast::Block, span: Span, id: ast::NodeId) {
|
||||
run_lints!(self, check_fn, early_passes, fk, decl, body, span, id);
|
||||
ast_visit::walk_fn(self, fk, decl, body, span);
|
||||
run_lints!(self, check_fn_post, early_passes, fk, decl, body, span, id);
|
||||
span: Span, id: ast::NodeId) {
|
||||
run_lints!(self, check_fn, early_passes, fk, decl, span, id);
|
||||
ast_visit::walk_fn(self, fk, decl, span);
|
||||
run_lints!(self, check_fn_post, early_passes, fk, decl, span, id);
|
||||
}
|
||||
|
||||
fn visit_variant_data(&mut self,
|
||||
|
@ -151,9 +151,9 @@ pub trait LateLintPass: LintPass {
|
||||
fn check_ty(&mut self, _: &LateContext, _: &hir::Ty) { }
|
||||
fn check_generics(&mut self, _: &LateContext, _: &hir::Generics) { }
|
||||
fn check_fn(&mut self, _: &LateContext,
|
||||
_: FnKind, _: &hir::FnDecl, _: &hir::Block, _: Span, _: ast::NodeId) { }
|
||||
_: FnKind, _: &hir::FnDecl, _: &hir::Expr, _: Span, _: ast::NodeId) { }
|
||||
fn check_fn_post(&mut self, _: &LateContext,
|
||||
_: FnKind, _: &hir::FnDecl, _: &hir::Block, _: Span, _: ast::NodeId) { }
|
||||
_: FnKind, _: &hir::FnDecl, _: &hir::Expr, _: Span, _: ast::NodeId) { }
|
||||
fn check_trait_item(&mut self, _: &LateContext, _: &hir::TraitItem) { }
|
||||
fn check_trait_item_post(&mut self, _: &LateContext, _: &hir::TraitItem) { }
|
||||
fn check_impl_item(&mut self, _: &LateContext, _: &hir::ImplItem) { }
|
||||
@ -200,9 +200,9 @@ pub trait EarlyLintPass: LintPass {
|
||||
fn check_ty(&mut self, _: &EarlyContext, _: &ast::Ty) { }
|
||||
fn check_generics(&mut self, _: &EarlyContext, _: &ast::Generics) { }
|
||||
fn check_fn(&mut self, _: &EarlyContext,
|
||||
_: ast_visit::FnKind, _: &ast::FnDecl, _: &ast::Block, _: Span, _: ast::NodeId) { }
|
||||
_: ast_visit::FnKind, _: &ast::FnDecl, _: Span, _: ast::NodeId) { }
|
||||
fn check_fn_post(&mut self, _: &EarlyContext,
|
||||
_: ast_visit::FnKind, _: &ast::FnDecl, _: &ast::Block, _: Span, _: ast::NodeId) { }
|
||||
_: ast_visit::FnKind, _: &ast::FnDecl, _: Span, _: ast::NodeId) { }
|
||||
fn check_trait_item(&mut self, _: &EarlyContext, _: &ast::TraitItem) { }
|
||||
fn check_trait_item_post(&mut self, _: &EarlyContext, _: &ast::TraitItem) { }
|
||||
fn check_impl_item(&mut self, _: &EarlyContext, _: &ast::ImplItem) { }
|
||||
|
@ -498,7 +498,7 @@ impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
|
||||
|
||||
impl<'a, 'tcx, O:DataFlowOperator+Clone+'static> DataFlowContext<'a, 'tcx, O> {
|
||||
// ^^^^^^^^^^^^^ only needed for pretty printing
|
||||
pub fn propagate(&mut self, cfg: &cfg::CFG, blk: &hir::Block) {
|
||||
pub fn propagate(&mut self, cfg: &cfg::CFG, body: &hir::Expr) {
|
||||
//! Performs the data flow analysis.
|
||||
|
||||
if self.bits_per_id == 0 {
|
||||
@ -524,17 +524,17 @@ impl<'a, 'tcx, O:DataFlowOperator+Clone+'static> DataFlowContext<'a, 'tcx, O> {
|
||||
debug!("Dataflow result for {}:", self.analysis_name);
|
||||
debug!("{}", {
|
||||
let mut v = Vec::new();
|
||||
self.pretty_print_to(box &mut v, blk).unwrap();
|
||||
self.pretty_print_to(box &mut v, body).unwrap();
|
||||
String::from_utf8(v).unwrap()
|
||||
});
|
||||
}
|
||||
|
||||
fn pretty_print_to<'b>(&self, wr: Box<io::Write + 'b>,
|
||||
blk: &hir::Block) -> io::Result<()> {
|
||||
body: &hir::Expr) -> io::Result<()> {
|
||||
let mut ps = pprust::rust_printer_annotated(wr, self, None);
|
||||
ps.cbox(pprust::indent_unit)?;
|
||||
ps.ibox(0)?;
|
||||
ps.print_block(blk)?;
|
||||
ps.print_expr(body)?;
|
||||
pp::eof(&mut ps.s)
|
||||
}
|
||||
}
|
||||
|
@ -566,7 +566,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for DeadVisitor<'a, 'tcx> {
|
||||
self.warn_dead_code(impl_item.id, impl_item.span,
|
||||
impl_item.name, "method");
|
||||
}
|
||||
intravisit::walk_block(self, body)
|
||||
intravisit::walk_expr(self, body)
|
||||
}
|
||||
hir::ImplItemKind::Type(..) => {}
|
||||
}
|
||||
@ -575,11 +575,9 @@ impl<'a, 'tcx, 'v> Visitor<'v> for DeadVisitor<'a, 'tcx> {
|
||||
// Overwrite so that we don't warn the trait item itself.
|
||||
fn visit_trait_item(&mut self, trait_item: &hir::TraitItem) {
|
||||
match trait_item.node {
|
||||
hir::ConstTraitItem(_, Some(ref expr)) => {
|
||||
intravisit::walk_expr(self, expr)
|
||||
}
|
||||
hir::ConstTraitItem(_, Some(ref body))|
|
||||
hir::MethodTraitItem(_, Some(ref body)) => {
|
||||
intravisit::walk_block(self, body)
|
||||
intravisit::walk_expr(self, body)
|
||||
}
|
||||
hir::ConstTraitItem(_, None) |
|
||||
hir::MethodTraitItem(_, None) |
|
||||
|
@ -94,7 +94,7 @@ impl<'a, 'tcx> EffectCheckVisitor<'a, 'tcx> {
|
||||
|
||||
impl<'a, 'tcx, 'v> Visitor<'v> for EffectCheckVisitor<'a, 'tcx> {
|
||||
fn visit_fn(&mut self, fn_kind: FnKind<'v>, fn_decl: &'v hir::FnDecl,
|
||||
block: &'v hir::Block, span: Span, id: ast::NodeId) {
|
||||
block: &'v hir::Expr, span: Span, id: ast::NodeId) {
|
||||
|
||||
let (is_item_fn, is_unsafe_fn) = match fn_kind {
|
||||
FnKind::ItemFn(_, _, unsafety, ..) =>
|
||||
|
@ -290,14 +290,14 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
|
||||
|
||||
pub fn walk_fn(&mut self,
|
||||
decl: &hir::FnDecl,
|
||||
body: &hir::Block) {
|
||||
body: &hir::Expr) {
|
||||
self.walk_arg_patterns(decl, body);
|
||||
self.walk_block(body);
|
||||
self.consume_expr(body);
|
||||
}
|
||||
|
||||
fn walk_arg_patterns(&mut self,
|
||||
decl: &hir::FnDecl,
|
||||
body: &hir::Block) {
|
||||
body: &hir::Expr) {
|
||||
for arg in &decl.inputs {
|
||||
let arg_ty = return_if_err!(self.mc.infcx.node_ty(arg.pat.id));
|
||||
|
||||
|
@ -144,7 +144,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for ItemVisitor<'a, 'tcx> {
|
||||
}
|
||||
|
||||
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl,
|
||||
b: &'v hir::Block, s: Span, id: ast::NodeId) {
|
||||
b: &'v hir::Expr, s: Span, id: ast::NodeId) {
|
||||
if let FnKind::Closure(..) = fk {
|
||||
span_bug!(s, "intrinsicck: closure outside of function")
|
||||
}
|
||||
|
@ -123,10 +123,9 @@ use std::io::prelude::*;
|
||||
use std::io;
|
||||
use std::rc::Rc;
|
||||
use syntax::ast::{self, NodeId};
|
||||
use syntax::codemap::original_sp;
|
||||
use syntax::parse::token::keywords;
|
||||
use syntax::ptr::P;
|
||||
use syntax_pos::{BytePos, Span};
|
||||
use syntax_pos::Span;
|
||||
|
||||
use hir::Expr;
|
||||
use hir;
|
||||
@ -187,7 +186,7 @@ fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt) -> String {
|
||||
|
||||
impl<'a, 'tcx, 'v> Visitor<'v> for IrMaps<'a, 'tcx> {
|
||||
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl,
|
||||
b: &'v hir::Block, s: Span, id: NodeId) {
|
||||
b: &'v hir::Expr, s: Span, id: NodeId) {
|
||||
visit_fn(self, fk, fd, b, s, id);
|
||||
}
|
||||
fn visit_local(&mut self, l: &hir::Local) { visit_local(self, l); }
|
||||
@ -352,9 +351,9 @@ impl<'a, 'tcx> IrMaps<'a, 'tcx> {
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, 'v> Visitor<'v> for Liveness<'a, 'tcx> {
|
||||
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl,
|
||||
b: &'v hir::Block, s: Span, n: NodeId) {
|
||||
check_fn(self, fk, fd, b, s, n);
|
||||
fn visit_fn(&mut self, _: FnKind<'v>, _: &'v hir::FnDecl,
|
||||
_: &'v hir::Expr, _: Span, _: NodeId) {
|
||||
// do not check contents of nested fns
|
||||
}
|
||||
fn visit_local(&mut self, l: &hir::Local) {
|
||||
check_local(self, l);
|
||||
@ -370,7 +369,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for Liveness<'a, 'tcx> {
|
||||
fn visit_fn(ir: &mut IrMaps,
|
||||
fk: FnKind,
|
||||
decl: &hir::FnDecl,
|
||||
body: &hir::Block,
|
||||
body: &hir::Expr,
|
||||
sp: Span,
|
||||
id: ast::NodeId) {
|
||||
debug!("visit_fn");
|
||||
@ -405,10 +404,10 @@ fn visit_fn(ir: &mut IrMaps,
|
||||
|
||||
// compute liveness
|
||||
let mut lsets = Liveness::new(&mut fn_maps, specials);
|
||||
let entry_ln = lsets.compute(decl, body);
|
||||
let entry_ln = lsets.compute(body);
|
||||
|
||||
// check for various error conditions
|
||||
lsets.visit_block(body);
|
||||
lsets.visit_expr(body);
|
||||
lsets.check_ret(id, sp, fk, entry_ln, body);
|
||||
lsets.warn_about_unused_args(decl, entry_ln);
|
||||
}
|
||||
@ -821,17 +820,23 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||
|
||||
// _______________________________________________________________________
|
||||
|
||||
fn compute(&mut self, decl: &hir::FnDecl, body: &hir::Block) -> LiveNode {
|
||||
fn compute(&mut self, body: &hir::Expr) -> LiveNode {
|
||||
// if there is a `break` or `again` at the top level, then it's
|
||||
// effectively a return---this only occurs in `for` loops,
|
||||
// where the body is really a closure.
|
||||
|
||||
debug!("compute: using id for block, {}", block_to_string(body));
|
||||
debug!("compute: using id for body, {}", expr_to_string(body));
|
||||
|
||||
let exit_ln = self.s.exit_ln;
|
||||
let entry_ln: LiveNode =
|
||||
self.with_loop_nodes(body.id, exit_ln, exit_ln,
|
||||
|this| this.propagate_through_fn_block(decl, body));
|
||||
let entry_ln: LiveNode = self.with_loop_nodes(body.id, exit_ln, exit_ln, |this| {
|
||||
// the fallthrough exit is only for those cases where we do not
|
||||
// explicitly return:
|
||||
let s = this.s;
|
||||
this.init_from_succ(s.fallthrough_ln, s.exit_ln);
|
||||
this.acc(s.fallthrough_ln, s.clean_exit_var, ACC_READ);
|
||||
|
||||
this.propagate_through_expr(body, s.fallthrough_ln)
|
||||
});
|
||||
|
||||
// hack to skip the loop unless debug! is enabled:
|
||||
debug!("^^ liveness computation results for body {} (entry={:?})",
|
||||
@ -846,20 +851,6 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||
entry_ln
|
||||
}
|
||||
|
||||
fn propagate_through_fn_block(&mut self, _: &hir::FnDecl, blk: &hir::Block)
|
||||
-> LiveNode {
|
||||
// the fallthrough exit is only for those cases where we do not
|
||||
// explicitly return:
|
||||
let s = self.s;
|
||||
self.init_from_succ(s.fallthrough_ln, s.exit_ln);
|
||||
if blk.expr.is_none() {
|
||||
self.acc(s.fallthrough_ln, s.no_ret_var, ACC_READ)
|
||||
}
|
||||
self.acc(s.fallthrough_ln, s.clean_exit_var, ACC_READ);
|
||||
|
||||
self.propagate_through_block(blk, s.fallthrough_ln)
|
||||
}
|
||||
|
||||
fn propagate_through_block(&mut self, blk: &hir::Block, succ: LiveNode)
|
||||
-> LiveNode {
|
||||
let succ = self.propagate_through_opt_expr(blk.expr.as_ref().map(|e| &**e), succ);
|
||||
@ -1448,15 +1439,6 @@ fn check_expr(this: &mut Liveness, expr: &Expr) {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_fn(_v: &Liveness,
|
||||
_fk: FnKind,
|
||||
_decl: &hir::FnDecl,
|
||||
_body: &hir::Block,
|
||||
_sp: Span,
|
||||
_id: NodeId) {
|
||||
// do not check contents of nested fns
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||
fn fn_ret(&self, id: NodeId) -> ty::Binder<Ty<'tcx>> {
|
||||
let fn_ty = self.ir.tcx.tables().node_id_to_type(id);
|
||||
@ -1472,7 +1454,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||
sp: Span,
|
||||
_fk: FnKind,
|
||||
entry_ln: LiveNode,
|
||||
body: &hir::Block)
|
||||
body: &hir::Expr)
|
||||
{
|
||||
// within the fn body, late-bound regions are liberated
|
||||
// and must outlive the *call-site* of the function.
|
||||
@ -1481,13 +1463,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||
self.ir.tcx.region_maps.call_site_extent(id, body.id),
|
||||
&self.fn_ret(id));
|
||||
|
||||
if fn_ret.is_never() {
|
||||
// FIXME(durka) this rejects code like `fn foo(x: !) -> ! { x }`
|
||||
if self.live_on_entry(entry_ln, self.s.clean_exit_var).is_some() {
|
||||
span_err!(self.ir.tcx.sess, sp, E0270,
|
||||
"computation may converge in a function marked as diverging");
|
||||
}
|
||||
} else if self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() {
|
||||
if !fn_ret.is_never() && self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() {
|
||||
let param_env = ParameterEnvironment::for_item(self.ir.tcx, id);
|
||||
let t_ret_subst = fn_ret.subst(self.ir.tcx, ¶m_env.free_substs);
|
||||
let is_nil = self.ir.tcx.infer_ctxt(None, Some(param_env),
|
||||
@ -1498,32 +1474,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||
|
||||
// for nil return types, it is ok to not return a value expl.
|
||||
if !is_nil {
|
||||
let ends_with_stmt = match body.expr {
|
||||
None if !body.stmts.is_empty() =>
|
||||
match body.stmts.last().unwrap().node {
|
||||
hir::StmtSemi(ref e, _) => {
|
||||
self.ir.tcx.tables().expr_ty(&e) == fn_ret
|
||||
},
|
||||
_ => false
|
||||
},
|
||||
_ => false
|
||||
};
|
||||
let mut err = struct_span_err!(self.ir.tcx.sess,
|
||||
sp,
|
||||
E0269,
|
||||
"not all control paths return a value");
|
||||
if ends_with_stmt {
|
||||
let last_stmt = body.stmts.last().unwrap();
|
||||
let original_span = original_sp(self.ir.tcx.sess.codemap(),
|
||||
last_stmt.span, sp);
|
||||
let span_semicolon = Span {
|
||||
lo: original_span.hi - BytePos(1),
|
||||
hi: original_span.hi,
|
||||
expn_id: original_span.expn_id
|
||||
};
|
||||
err.span_help(span_semicolon, "consider removing this semicolon:");
|
||||
}
|
||||
err.emit();
|
||||
span_bug!(sp, "not all control paths return a value");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -248,9 +248,9 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> {
|
||||
match *node {
|
||||
ast_map::NodeItem(item) => {
|
||||
match item.node {
|
||||
hir::ItemFn(.., ref search_block) => {
|
||||
hir::ItemFn(.., ref body) => {
|
||||
if item_might_be_inlined(&item) {
|
||||
intravisit::walk_block(self, &search_block)
|
||||
self.visit_expr(body);
|
||||
}
|
||||
}
|
||||
|
||||
@ -278,11 +278,9 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> {
|
||||
hir::MethodTraitItem(_, None) => {
|
||||
// Keep going, nothing to get exported
|
||||
}
|
||||
hir::ConstTraitItem(_, Some(ref expr)) => {
|
||||
self.visit_expr(&expr);
|
||||
}
|
||||
hir::ConstTraitItem(_, Some(ref body)) |
|
||||
hir::MethodTraitItem(_, Some(ref body)) => {
|
||||
intravisit::walk_block(self, body);
|
||||
self.visit_expr(body);
|
||||
}
|
||||
hir::TypeTraitItem(..) => {}
|
||||
}
|
||||
@ -295,7 +293,7 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> {
|
||||
hir::ImplItemKind::Method(ref sig, ref body) => {
|
||||
let did = self.tcx.map.get_parent_did(search_item);
|
||||
if method_might_be_inlined(self.tcx, sig, impl_item, did) {
|
||||
intravisit::walk_block(self, body)
|
||||
self.visit_expr(body)
|
||||
}
|
||||
}
|
||||
hir::ImplItemKind::Type(_) => {}
|
||||
|
@ -490,12 +490,7 @@ impl RegionMaps {
|
||||
// if there's one. Static items, for instance, won't
|
||||
// have an enclosing scope, hence no scope will be
|
||||
// returned.
|
||||
let expr_extent = self.node_extent(expr_id);
|
||||
// For some reason, the expr's scope itself is skipped here.
|
||||
let mut id = match scope_map[expr_extent.0 as usize].into_option() {
|
||||
Some(i) => i,
|
||||
_ => return None
|
||||
};
|
||||
let mut id = self.node_extent(expr_id);
|
||||
|
||||
while let Some(p) = scope_map[id.0 as usize].into_option() {
|
||||
match code_extents[p.0 as usize] {
|
||||
@ -1086,7 +1081,7 @@ fn resolve_item(visitor: &mut RegionResolutionVisitor, item: &hir::Item) {
|
||||
fn resolve_fn(visitor: &mut RegionResolutionVisitor,
|
||||
kind: FnKind,
|
||||
decl: &hir::FnDecl,
|
||||
body: &hir::Block,
|
||||
body: &hir::Expr,
|
||||
sp: Span,
|
||||
id: ast::NodeId) {
|
||||
debug!("region::resolve_fn(id={:?}, \
|
||||
@ -1128,7 +1123,7 @@ fn resolve_fn(visitor: &mut RegionResolutionVisitor,
|
||||
parent: fn_decl_scope,
|
||||
var_parent: fn_decl_scope
|
||||
};
|
||||
visitor.visit_block(body);
|
||||
visitor.visit_expr(body);
|
||||
|
||||
// Restore context we had at the start.
|
||||
visitor.cx = outer_cx;
|
||||
@ -1191,7 +1186,7 @@ impl<'a, 'v> Visitor<'v> for RegionResolutionVisitor<'a> {
|
||||
}
|
||||
|
||||
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl,
|
||||
b: &'v Block, s: Span, n: NodeId) {
|
||||
b: &'v Expr, s: Span, n: NodeId) {
|
||||
resolve_fn(self, fk, fd, b, s, n);
|
||||
}
|
||||
fn visit_arm(&mut self, a: &Arm) {
|
||||
|
@ -202,7 +202,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for LifetimeContext<'a, 'tcx> {
|
||||
}
|
||||
|
||||
fn visit_fn(&mut self, fk: FnKind<'v>, decl: &'v hir::FnDecl,
|
||||
b: &'v hir::Block, s: Span, fn_id: ast::NodeId) {
|
||||
b: &'v hir::Expr, s: Span, fn_id: ast::NodeId) {
|
||||
match fk {
|
||||
FnKind::ItemFn(_, generics, ..) => {
|
||||
self.visit_early_late(fn_id,decl, generics, |this| {
|
||||
@ -403,7 +403,7 @@ fn signal_shadowing_problem(sess: &Session, name: ast::Name, orig: Original, sha
|
||||
|
||||
// Adds all labels in `b` to `ctxt.labels_in_fn`, signalling a warning
|
||||
// if one of the label shadows a lifetime or another label.
|
||||
fn extract_labels(ctxt: &mut LifetimeContext, b: &hir::Block) {
|
||||
fn extract_labels(ctxt: &mut LifetimeContext, b: &hir::Expr) {
|
||||
struct GatherLabels<'a> {
|
||||
sess: &'a Session,
|
||||
scope: Scope<'a>,
|
||||
@ -415,7 +415,7 @@ fn extract_labels(ctxt: &mut LifetimeContext, b: &hir::Block) {
|
||||
scope: ctxt.scope,
|
||||
labels_in_fn: &mut ctxt.labels_in_fn,
|
||||
};
|
||||
gather.visit_block(b);
|
||||
gather.visit_expr(b);
|
||||
return;
|
||||
|
||||
impl<'v, 'a> Visitor<'v> for GatherLabels<'a> {
|
||||
@ -493,7 +493,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||
fn add_scope_and_walk_fn<'b>(&mut self,
|
||||
fk: FnKind,
|
||||
fd: &hir::FnDecl,
|
||||
fb: &'b hir::Block,
|
||||
fb: &'b hir::Expr,
|
||||
_span: Span,
|
||||
fn_id: ast::NodeId) {
|
||||
|
||||
@ -516,7 +516,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||
extract_labels(self, fb);
|
||||
|
||||
self.with(FnScope { fn_id: fn_id, body_id: fb.id, s: self.scope },
|
||||
|_old_scope, this| this.visit_block(fb))
|
||||
|_old_scope, this| this.visit_expr(fb))
|
||||
}
|
||||
|
||||
fn with<F>(&mut self, wrap_scope: ScopeChain, f: F) where
|
||||
|
@ -190,7 +190,7 @@ pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
|
||||
all_loans: &[Loan<'tcx>],
|
||||
fn_id: ast::NodeId,
|
||||
decl: &hir::FnDecl,
|
||||
body: &hir::Block) {
|
||||
body: &hir::Expr) {
|
||||
debug!("check_loans(body id={})", body.id);
|
||||
|
||||
let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id);
|
||||
|
@ -42,7 +42,7 @@ mod move_error;
|
||||
pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
|
||||
fn_id: NodeId,
|
||||
decl: &hir::FnDecl,
|
||||
body: &hir::Block)
|
||||
body: &hir::Expr)
|
||||
-> (Vec<Loan<'tcx>>,
|
||||
move_data::MoveData<'tcx>) {
|
||||
let mut glcx = GatherLoanCtxt {
|
||||
|
@ -58,7 +58,7 @@ pub struct MoveDataParamEnv<'tcx> {
|
||||
pub fn borrowck_mir(bcx: &mut BorrowckCtxt,
|
||||
fk: FnKind,
|
||||
_decl: &hir::FnDecl,
|
||||
body: &hir::Block,
|
||||
body: &hir::Expr,
|
||||
_sp: Span,
|
||||
id: ast::NodeId,
|
||||
attributes: &[ast::Attribute]) {
|
||||
|
@ -47,9 +47,7 @@ use syntax_pos::{MultiSpan, Span};
|
||||
use errors::DiagnosticBuilder;
|
||||
|
||||
use rustc::hir;
|
||||
use rustc::hir::{FnDecl, Block};
|
||||
use rustc::hir::intravisit;
|
||||
use rustc::hir::intravisit::{Visitor, FnKind};
|
||||
use rustc::hir::intravisit::{self, Visitor, FnKind};
|
||||
|
||||
pub mod check_loans;
|
||||
|
||||
@ -65,8 +63,8 @@ pub struct LoanDataFlowOperator;
|
||||
pub type LoanDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, LoanDataFlowOperator>;
|
||||
|
||||
impl<'a, 'tcx, 'v> Visitor<'v> for BorrowckCtxt<'a, 'tcx> {
|
||||
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl,
|
||||
b: &'v Block, s: Span, id: ast::NodeId) {
|
||||
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl,
|
||||
b: &'v hir::Expr, s: Span, id: ast::NodeId) {
|
||||
match fk {
|
||||
FnKind::ItemFn(..) |
|
||||
FnKind::Method(..) => {
|
||||
@ -159,7 +157,7 @@ pub struct AnalysisData<'a, 'tcx: 'a> {
|
||||
fn borrowck_fn(this: &mut BorrowckCtxt,
|
||||
fk: FnKind,
|
||||
decl: &hir::FnDecl,
|
||||
body: &hir::Block,
|
||||
body: &hir::Expr,
|
||||
sp: Span,
|
||||
id: ast::NodeId,
|
||||
attributes: &[ast::Attribute]) {
|
||||
@ -200,7 +198,7 @@ fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>,
|
||||
fk: FnKind,
|
||||
decl: &hir::FnDecl,
|
||||
cfg: &cfg::CFG,
|
||||
body: &hir::Block,
|
||||
body: &hir::Expr,
|
||||
sp: Span,
|
||||
id: ast::NodeId)
|
||||
-> AnalysisData<'a, 'tcx>
|
||||
|
@ -656,7 +656,7 @@ impl<'a, 'tcx> FlowedMoveData<'a, 'tcx> {
|
||||
cfg: &cfg::CFG,
|
||||
id_range: IdRange,
|
||||
decl: &hir::FnDecl,
|
||||
body: &hir::Block)
|
||||
body: &hir::Expr)
|
||||
-> FlowedMoveData<'a, 'tcx> {
|
||||
let mut dfcx_moves =
|
||||
DataFlowContext::new(tcx,
|
||||
|
@ -65,7 +65,7 @@ impl<'a, 'v, 'tcx> Visitor<'v> for OuterVisitor<'a, 'tcx> {
|
||||
}
|
||||
|
||||
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl,
|
||||
b: &'v hir::Block, s: Span, id: ast::NodeId) {
|
||||
b: &'v hir::Expr, s: Span, id: ast::NodeId) {
|
||||
if let FnKind::Closure(..) = fk {
|
||||
span_bug!(s, "check_match: closure outside of function")
|
||||
}
|
||||
@ -113,7 +113,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for MatchVisitor<'a, 'tcx> {
|
||||
}
|
||||
|
||||
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl,
|
||||
b: &'v hir::Block, s: Span, n: ast::NodeId) {
|
||||
b: &'v hir::Expr, s: Span, n: ast::NodeId) {
|
||||
intravisit::walk_fn(self, fk, fd, b, s, n);
|
||||
|
||||
for input in &fd.inputs {
|
||||
|
@ -857,11 +857,10 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
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)
|
||||
(fn_like.decl(), fn_like.body())
|
||||
} 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 = DefIdMap();
|
||||
|
@ -455,8 +455,6 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
|
||||
1 => panic!("make_input should have provided valid inputs"),
|
||||
_ => early_error(sopts.error_format, "multiple input filenames provided"),
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn late_callback(&mut self,
|
||||
|
@ -701,8 +701,8 @@ fn print_flowgraph<'a, 'tcx, W: Write>(variants: Vec<borrowck_dot::Variant>,
|
||||
mut out: W)
|
||||
-> io::Result<()> {
|
||||
let cfg = match code {
|
||||
blocks::BlockCode(block) => cfg::CFG::new(tcx, &block),
|
||||
blocks::FnLikeCode(fn_like) => cfg::CFG::new(tcx, &fn_like.body()),
|
||||
blocks::Code::Expr(expr) => cfg::CFG::new(tcx, expr),
|
||||
blocks::Code::FnLike(fn_like) => cfg::CFG::new(tcx, fn_like.body()),
|
||||
};
|
||||
let labelled_edges = mode != PpFlowGraphMode::UnlabelledEdges;
|
||||
let lcfg = LabelledCFG {
|
||||
@ -717,12 +717,12 @@ fn print_flowgraph<'a, 'tcx, W: Write>(variants: Vec<borrowck_dot::Variant>,
|
||||
let r = dot::render(&lcfg, &mut out);
|
||||
return expand_err_details(r);
|
||||
}
|
||||
blocks::BlockCode(_) => {
|
||||
blocks::Code::Expr(_) => {
|
||||
tcx.sess.err("--pretty flowgraph with -Z flowgraph-print annotations requires \
|
||||
fn-like node id.");
|
||||
return Ok(());
|
||||
}
|
||||
blocks::FnLikeCode(fn_like) => {
|
||||
blocks::Code::FnLike(fn_like) => {
|
||||
let (bccx, analysis_data) =
|
||||
borrowck::build_borrowck_dataflow_data_for_fn(tcx, fn_like.to_fn_parts(), &cfg);
|
||||
|
||||
@ -990,8 +990,7 @@ fn print_with_analysis<'tcx, 'a: 'tcx>(sess: &'a Session,
|
||||
tcx.sess.fatal(&format!("--pretty flowgraph couldn't find id: {}", nodeid))
|
||||
});
|
||||
|
||||
let code = blocks::Code::from_node(node);
|
||||
match code {
|
||||
match blocks::Code::from_node(&tcx.map, nodeid) {
|
||||
Some(code) => {
|
||||
let variants = gather_flowgraph_variants(tcx.sess);
|
||||
|
||||
|
@ -250,7 +250,7 @@ impl LateLintPass for NonSnakeCase {
|
||||
cx: &LateContext,
|
||||
fk: FnKind,
|
||||
_: &hir::FnDecl,
|
||||
_: &hir::Block,
|
||||
_: &hir::Expr,
|
||||
span: Span,
|
||||
id: ast::NodeId) {
|
||||
match fk {
|
||||
|
@ -222,7 +222,7 @@ impl LateLintPass for UnsafeCode {
|
||||
cx: &LateContext,
|
||||
fk: FnKind,
|
||||
_: &hir::FnDecl,
|
||||
_: &hir::Block,
|
||||
_: &hir::Expr,
|
||||
span: Span,
|
||||
_: ast::NodeId) {
|
||||
match fk {
|
||||
@ -812,7 +812,7 @@ impl LateLintPass for UnconditionalRecursion {
|
||||
cx: &LateContext,
|
||||
fn_kind: FnKind,
|
||||
_: &hir::FnDecl,
|
||||
blk: &hir::Block,
|
||||
blk: &hir::Expr,
|
||||
sp: Span,
|
||||
id: ast::NodeId) {
|
||||
let method = match fn_kind {
|
||||
|
@ -99,7 +99,7 @@ impl LateLintPass for UnusedMut {
|
||||
cx: &LateContext,
|
||||
_: FnKind,
|
||||
decl: &hir::FnDecl,
|
||||
_: &hir::Block,
|
||||
_: &hir::Expr,
|
||||
_: Span,
|
||||
_: ast::NodeId) {
|
||||
for a in &decl.inputs {
|
||||
|
@ -156,7 +156,7 @@ pub fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
|
||||
fn_id: ast::NodeId,
|
||||
arguments: A,
|
||||
return_ty: Ty<'gcx>,
|
||||
ast_block: &'gcx hir::Block)
|
||||
ast_body: &'gcx hir::Expr)
|
||||
-> (Mir<'tcx>, ScopeAuxiliaryVec)
|
||||
where A: Iterator<Item=(Ty<'gcx>, Option<&'gcx hir::Pat>)>
|
||||
{
|
||||
@ -166,7 +166,7 @@ pub fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
|
||||
let span = tcx.map.span(fn_id);
|
||||
let mut builder = Builder::new(hir, span, arguments.len(), return_ty);
|
||||
|
||||
let body_id = ast_block.id;
|
||||
let body_id = ast_body.id;
|
||||
let call_site_extent =
|
||||
tcx.region_maps.lookup_code_extent(
|
||||
CodeExtentData::CallSiteScope { fn_id: fn_id, body_id: body_id });
|
||||
@ -176,7 +176,7 @@ pub fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
|
||||
let mut block = START_BLOCK;
|
||||
unpack!(block = builder.in_scope(call_site_extent, block, |builder| {
|
||||
unpack!(block = builder.in_scope(arg_extent, block, |builder| {
|
||||
builder.args_and_body(block, return_ty, &arguments, arg_extent, ast_block)
|
||||
builder.args_and_body(block, &arguments, arg_extent, ast_body)
|
||||
}));
|
||||
// Attribute epilogue to function's closing brace
|
||||
let fn_end = Span { lo: span.hi, ..span };
|
||||
@ -310,10 +310,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
|
||||
fn args_and_body(&mut self,
|
||||
mut block: BasicBlock,
|
||||
return_ty: Ty<'tcx>,
|
||||
arguments: &[(Ty<'gcx>, Option<&'gcx hir::Pat>)],
|
||||
argument_extent: CodeExtent,
|
||||
ast_block: &'gcx hir::Block)
|
||||
ast_body: &'gcx hir::Expr)
|
||||
-> BlockAnd<()>
|
||||
{
|
||||
// Allocate locals for the function arguments
|
||||
@ -342,12 +341,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
|
||||
if let Some(pattern) = pattern {
|
||||
let pattern = Pattern::from_hir(self.hir.tcx(), pattern);
|
||||
scope = self.declare_bindings(scope, ast_block.span, &pattern);
|
||||
scope = self.declare_bindings(scope, ast_body.span, &pattern);
|
||||
unpack!(block = self.lvalue_into_pattern(block, pattern, &lvalue));
|
||||
}
|
||||
|
||||
// Make sure we drop (parts of) the argument even when not matched on.
|
||||
self.schedule_drop(pattern.as_ref().map_or(ast_block.span, |pat| pat.span),
|
||||
self.schedule_drop(pattern.as_ref().map_or(ast_body.span, |pat| pat.span),
|
||||
argument_extent, &lvalue, ty);
|
||||
|
||||
}
|
||||
@ -357,13 +356,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
self.visibility_scope = visibility_scope;
|
||||
}
|
||||
|
||||
// FIXME(#32959): temporary hack for the issue at hand
|
||||
let return_is_unit = return_ty.is_nil();
|
||||
// start the first basic block and translate the body
|
||||
unpack!(block = self.ast_block(&Lvalue::Local(RETURN_POINTER),
|
||||
return_is_unit, block, ast_block));
|
||||
|
||||
block.unit()
|
||||
let body = self.hir.mirror(ast_body);
|
||||
self.into(&Lvalue::Local(RETURN_POINTER), block, body)
|
||||
}
|
||||
|
||||
fn get_unit_temp(&mut self) -> Lvalue<'tcx> {
|
||||
|
@ -209,7 +209,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BuildMir<'a, 'tcx> {
|
||||
fn visit_fn(&mut self,
|
||||
fk: FnKind<'tcx>,
|
||||
decl: &'tcx hir::FnDecl,
|
||||
body: &'tcx hir::Block,
|
||||
body: &'tcx hir::Expr,
|
||||
span: Span,
|
||||
id: ast::NodeId) {
|
||||
// fetch the fully liberated fn signature (that is, all bound
|
||||
|
@ -134,7 +134,7 @@ impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> {
|
||||
fn fn_like(&mut self,
|
||||
fk: FnKind,
|
||||
fd: &hir::FnDecl,
|
||||
b: &hir::Block,
|
||||
b: &hir::Expr,
|
||||
s: Span,
|
||||
fn_id: ast::NodeId)
|
||||
-> ConstQualif {
|
||||
@ -265,7 +265,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
|
||||
fn visit_fn(&mut self,
|
||||
fk: FnKind<'v>,
|
||||
fd: &'v hir::FnDecl,
|
||||
b: &'v hir::Block,
|
||||
b: &'v hir::Expr,
|
||||
s: Span,
|
||||
fn_id: ast::NodeId) {
|
||||
self.fn_like(fk, fd, b, s, fn_id);
|
||||
|
@ -164,7 +164,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
|
||||
fn visit_fn(&mut self,
|
||||
fk: hir_visit::FnKind<'v>,
|
||||
fd: &'v hir::FnDecl,
|
||||
b: &'v hir::Block,
|
||||
b: &'v hir::Expr,
|
||||
s: Span,
|
||||
id: NodeId) {
|
||||
self.record("FnDecl", Id::None, fd);
|
||||
@ -295,11 +295,10 @@ impl<'v> ast_visit::Visitor for StatCollector<'v> {
|
||||
fn visit_fn(&mut self,
|
||||
fk: ast_visit::FnKind,
|
||||
fd: &ast::FnDecl,
|
||||
b: &ast::Block,
|
||||
s: Span,
|
||||
_: NodeId) {
|
||||
self.record("FnDecl", Id::None, fd);
|
||||
ast_visit::walk_fn(self, fk, fd, b, s)
|
||||
ast_visit::walk_fn(self, fk, fd, s)
|
||||
}
|
||||
|
||||
fn visit_trait_item(&mut self, ti: &ast::TraitItem) {
|
||||
|
@ -54,7 +54,7 @@ impl<'a, 'v> Visitor<'v> for CheckLoopVisitor<'a> {
|
||||
self.with_context(Loop, |v| v.visit_block(&b));
|
||||
}
|
||||
hir::ExprClosure(.., ref b, _) => {
|
||||
self.with_context(Closure, |v| v.visit_block(&b));
|
||||
self.with_context(Closure, |v| v.visit_expr(&b));
|
||||
}
|
||||
hir::ExprBreak(_) => self.require_loop("break", e.span),
|
||||
hir::ExprAgain(_) => self.require_loop("continue", e.span),
|
||||
|
@ -35,7 +35,7 @@ impl<'a, 'tcx, 'v> intravisit::Visitor<'v> for RvalueContext<'a, 'tcx> {
|
||||
fn visit_fn(&mut self,
|
||||
fk: intravisit::FnKind<'v>,
|
||||
fd: &'v hir::FnDecl,
|
||||
b: &'v hir::Block,
|
||||
b: &'v hir::Expr,
|
||||
s: Span,
|
||||
fn_id: ast::NodeId) {
|
||||
// FIXME (@jroesch) change this to be an inference context
|
||||
|
@ -596,7 +596,6 @@ impl<'a> Visitor for Resolver<'a> {
|
||||
fn visit_fn(&mut self,
|
||||
function_kind: FnKind,
|
||||
declaration: &FnDecl,
|
||||
block: &Block,
|
||||
_: Span,
|
||||
node_id: NodeId) {
|
||||
let rib_kind = match function_kind {
|
||||
@ -604,13 +603,45 @@ impl<'a> Visitor for Resolver<'a> {
|
||||
self.visit_generics(generics);
|
||||
ItemRibKind
|
||||
}
|
||||
FnKind::Method(_, sig, _) => {
|
||||
FnKind::Method(_, sig, _, _) => {
|
||||
self.visit_generics(&sig.generics);
|
||||
MethodRibKind(!sig.decl.has_self())
|
||||
}
|
||||
FnKind::Closure => ClosureRibKind(node_id),
|
||||
FnKind::Closure(_) => ClosureRibKind(node_id),
|
||||
};
|
||||
self.resolve_function(rib_kind, declaration, block);
|
||||
|
||||
// Create a value rib for the function.
|
||||
self.value_ribs.push(Rib::new(rib_kind));
|
||||
|
||||
// Create a label rib for the function.
|
||||
self.label_ribs.push(Rib::new(rib_kind));
|
||||
|
||||
// Add each argument to the rib.
|
||||
let mut bindings_list = FxHashMap();
|
||||
for argument in &declaration.inputs {
|
||||
self.resolve_pattern(&argument.pat, PatternSource::FnParam, &mut bindings_list);
|
||||
|
||||
self.visit_ty(&argument.ty);
|
||||
|
||||
debug!("(resolving function) recorded argument");
|
||||
}
|
||||
visit::walk_fn_ret_ty(self, &declaration.output);
|
||||
|
||||
// Resolve the function body.
|
||||
match function_kind {
|
||||
FnKind::ItemFn(.., body) |
|
||||
FnKind::Method(.., body) => {
|
||||
self.visit_block(body);
|
||||
}
|
||||
FnKind::Closure(body) => {
|
||||
self.visit_expr(body);
|
||||
}
|
||||
};
|
||||
|
||||
debug!("(resolving function) leaving function");
|
||||
|
||||
self.label_ribs.pop();
|
||||
self.value_ribs.pop();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1856,36 +1887,6 @@ impl<'a> Resolver<'a> {
|
||||
self.value_ribs.pop();
|
||||
}
|
||||
|
||||
fn resolve_function(&mut self,
|
||||
rib_kind: RibKind<'a>,
|
||||
declaration: &FnDecl,
|
||||
block: &Block) {
|
||||
// Create a value rib for the function.
|
||||
self.value_ribs.push(Rib::new(rib_kind));
|
||||
|
||||
// Create a label rib for the function.
|
||||
self.label_ribs.push(Rib::new(rib_kind));
|
||||
|
||||
// Add each argument to the rib.
|
||||
let mut bindings_list = FxHashMap();
|
||||
for argument in &declaration.inputs {
|
||||
self.resolve_pattern(&argument.pat, PatternSource::FnParam, &mut bindings_list);
|
||||
|
||||
self.visit_ty(&argument.ty);
|
||||
|
||||
debug!("(resolving function) recorded argument");
|
||||
}
|
||||
visit::walk_fn_ret_ty(self, &declaration.output);
|
||||
|
||||
// Resolve the function body.
|
||||
self.visit_block(block);
|
||||
|
||||
debug!("(resolving function) leaving function");
|
||||
|
||||
self.label_ribs.pop();
|
||||
self.value_ribs.pop();
|
||||
}
|
||||
|
||||
fn resolve_trait_reference(&mut self,
|
||||
id: NodeId,
|
||||
trait_path: &Path,
|
||||
|
@ -1412,7 +1412,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor for DumpVisitor<'l, 'tcx, 'll, D>
|
||||
}
|
||||
|
||||
// walk the body
|
||||
self.nest(ex.id, |v| v.visit_block(&body));
|
||||
self.nest(ex.id, |v| v.visit_expr(body));
|
||||
}
|
||||
ast::ExprKind::ForLoop(ref pattern, ref subexpression, ref block, _) |
|
||||
ast::ExprKind::WhileLet(ref pattern, ref subexpression, ref block, _) => {
|
||||
|
@ -13,7 +13,7 @@ use rustc::hir::def::{Def, CtorKind};
|
||||
use rustc::hir::pat_util::EnumerateAndAdjustIterator;
|
||||
use rustc::infer::{self, InferOk, TypeOrigin};
|
||||
use rustc::ty::{self, Ty, TypeFoldable, LvaluePreference};
|
||||
use check::{FnCtxt, Expectation};
|
||||
use check::{FnCtxt, Expectation, Diverges};
|
||||
use util::nodemap::FxHashMap;
|
||||
|
||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||
@ -360,9 +360,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
pub fn check_match(&self,
|
||||
expr: &'gcx hir::Expr,
|
||||
discrim: &'gcx hir::Expr,
|
||||
@ -390,14 +388,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
discrim_ty = self.next_ty_var();
|
||||
self.check_expr_has_type(discrim, discrim_ty);
|
||||
};
|
||||
let discrim_diverges = self.diverges.get();
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
|
||||
// Typecheck the patterns first, so that we get types for all the
|
||||
// bindings.
|
||||
for arm in arms {
|
||||
let all_arm_pats_diverge: Vec<_> = arms.iter().map(|arm| {
|
||||
let mut all_pats_diverge = Diverges::WarnedAlways;
|
||||
for p in &arm.pats {
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
self.check_pat(&p, discrim_ty);
|
||||
all_pats_diverge &= self.diverges.get();
|
||||
}
|
||||
}
|
||||
all_pats_diverge
|
||||
}).collect();
|
||||
|
||||
// Now typecheck the blocks.
|
||||
//
|
||||
@ -410,6 +414,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
// type in that case)
|
||||
let expected = expected.adjust_for_branches(self);
|
||||
let mut result_ty = self.next_diverging_ty_var();
|
||||
let mut all_arms_diverge = Diverges::WarnedAlways;
|
||||
let coerce_first = match expected {
|
||||
// We don't coerce to `()` so that if the match expression is a
|
||||
// statement it's branches can have any consistent type. That allows
|
||||
@ -422,11 +427,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
_ => result_ty
|
||||
};
|
||||
|
||||
for (i, arm) in arms.iter().enumerate() {
|
||||
for (i, (arm, pats_diverge)) in arms.iter().zip(all_arm_pats_diverge).enumerate() {
|
||||
if let Some(ref e) = arm.guard {
|
||||
self.diverges.set(pats_diverge);
|
||||
self.check_expr_has_type(e, tcx.types.bool);
|
||||
}
|
||||
|
||||
self.diverges.set(pats_diverge);
|
||||
let arm_ty = self.check_expr_with_expectation(&arm.body, expected);
|
||||
all_arms_diverge &= self.diverges.get();
|
||||
|
||||
if result_ty.references_error() || arm_ty.references_error() {
|
||||
result_ty = tcx.types.err;
|
||||
@ -476,11 +485,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
};
|
||||
}
|
||||
|
||||
// We won't diverge unless the discriminant or all arms diverge.
|
||||
self.diverges.set(discrim_diverges | all_arms_diverge);
|
||||
|
||||
result_ty
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
fn check_pat_struct(&self,
|
||||
pat: &'gcx hir::Pat,
|
||||
path: &hir::Path,
|
||||
|
@ -23,7 +23,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
expr: &hir::Expr,
|
||||
_capture: hir::CaptureClause,
|
||||
decl: &'gcx hir::FnDecl,
|
||||
body: &'gcx hir::Block,
|
||||
body: &'gcx hir::Expr,
|
||||
expected: Expectation<'tcx>)
|
||||
-> Ty<'tcx> {
|
||||
debug!("check_expr_closure(expr={:?},expected={:?})",
|
||||
@ -44,7 +44,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
expr: &hir::Expr,
|
||||
opt_kind: Option<ty::ClosureKind>,
|
||||
decl: &'gcx hir::FnDecl,
|
||||
body: &'gcx hir::Block,
|
||||
body: &'gcx hir::Expr,
|
||||
expected_sig: Option<ty::FnSig<'tcx>>)
|
||||
-> Ty<'tcx> {
|
||||
let expr_def_id = self.tcx.map.local_def_id(expr.id);
|
||||
|
@ -106,17 +106,18 @@ use util::common::{block_query, ErrorReported, indenter, loop_query};
|
||||
use util::nodemap::{DefIdMap, FxHashMap, FxHashSet, NodeMap};
|
||||
|
||||
use std::cell::{Cell, Ref, RefCell};
|
||||
use std::cmp;
|
||||
use std::mem::replace;
|
||||
use std::ops::Deref;
|
||||
use std::ops::{self, Deref};
|
||||
use syntax::abi::Abi;
|
||||
use syntax::ast;
|
||||
use syntax::attr;
|
||||
use syntax::codemap::{self, Spanned};
|
||||
use syntax::codemap::{self, original_sp, Spanned};
|
||||
use syntax::feature_gate::{GateIssue, emit_feature_err};
|
||||
use syntax::parse::token::{self, InternedString, keywords};
|
||||
use syntax::ptr::P;
|
||||
use syntax::util::lev_distance::find_best_match_for_name;
|
||||
use syntax_pos::{self, Span};
|
||||
use syntax_pos::{self, BytePos, Span};
|
||||
|
||||
use rustc::hir::intravisit::{self, Visitor};
|
||||
use rustc::hir::{self, PatKind};
|
||||
@ -351,6 +352,59 @@ impl UnsafetyState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether a node ever exits normally or not.
|
||||
/// Tracked semi-automatically (through type variables
|
||||
/// marked as diverging), with some manual adjustments
|
||||
/// for control-flow primitives (approximating a CFG).
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
enum Diverges {
|
||||
/// Potentially unknown, some cases converge,
|
||||
/// others require a CFG to determine them.
|
||||
Maybe,
|
||||
|
||||
/// Definitely known to diverge and therefore
|
||||
/// not reach the next sibling or its parent.
|
||||
Always,
|
||||
|
||||
/// Same as `Always` but with a reachability
|
||||
/// warning already emitted
|
||||
WarnedAlways
|
||||
}
|
||||
|
||||
// Convenience impls for combinig `Diverges`.
|
||||
|
||||
impl ops::BitAnd for Diverges {
|
||||
type Output = Self;
|
||||
fn bitand(self, other: Self) -> Self {
|
||||
cmp::min(self, other)
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::BitOr for Diverges {
|
||||
type Output = Self;
|
||||
fn bitor(self, other: Self) -> Self {
|
||||
cmp::max(self, other)
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::BitAndAssign for Diverges {
|
||||
fn bitand_assign(&mut self, other: Self) {
|
||||
*self = *self & other;
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::BitOrAssign for Diverges {
|
||||
fn bitor_assign(&mut self, other: Self) {
|
||||
*self = *self | other;
|
||||
}
|
||||
}
|
||||
|
||||
impl Diverges {
|
||||
fn always(self) -> bool {
|
||||
self >= Diverges::Always
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
|
||||
ast_ty_to_ty_cache: RefCell<NodeMap<Ty<'tcx>>>,
|
||||
@ -371,6 +425,12 @@ pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
|
||||
|
||||
ps: RefCell<UnsafetyState>,
|
||||
|
||||
/// Whether the last checked node can ever exit.
|
||||
diverges: Cell<Diverges>,
|
||||
|
||||
/// Whether any child nodes have any type errors.
|
||||
has_errors: Cell<bool>,
|
||||
|
||||
inh: &'a Inherited<'a, 'gcx, 'tcx>,
|
||||
}
|
||||
|
||||
@ -534,7 +594,7 @@ pub fn check_drop_impls(ccx: &CrateCtxt) -> CompileResult {
|
||||
|
||||
fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
|
||||
decl: &'tcx hir::FnDecl,
|
||||
body: &'tcx hir::Block,
|
||||
body: &'tcx hir::Expr,
|
||||
fn_id: ast::NodeId,
|
||||
span: Span) {
|
||||
let raw_fty = ccx.tcx.lookup_item_type(ccx.tcx.map.local_def_id(fn_id)).ty;
|
||||
@ -558,7 +618,7 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
|
||||
let fcx = check_fn(&inh, fn_ty.unsafety, fn_id, &fn_sig, decl, fn_id, body);
|
||||
|
||||
fcx.select_all_obligations_and_apply_defaults();
|
||||
fcx.closure_analyze_fn(body);
|
||||
fcx.closure_analyze(body);
|
||||
fcx.select_obligations_where_possible();
|
||||
fcx.check_casts();
|
||||
fcx.select_all_obligations_or_error(); // Casts can introduce new obligations.
|
||||
@ -654,7 +714,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for GatherLocalsVisitor<'a, 'gcx, 'tcx> {
|
||||
|
||||
// Don't descend into the bodies of nested closures
|
||||
fn visit_fn(&mut self, _: intravisit::FnKind<'gcx>, _: &'gcx hir::FnDecl,
|
||||
_: &'gcx hir::Block, _: Span, _: ast::NodeId) { }
|
||||
_: &'gcx hir::Expr, _: Span, _: ast::NodeId) { }
|
||||
}
|
||||
|
||||
/// Helper used by check_bare_fn and check_expr_fn. Does the grungy work of checking a function
|
||||
@ -669,7 +729,7 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
|
||||
fn_sig: &ty::FnSig<'tcx>,
|
||||
decl: &'gcx hir::FnDecl,
|
||||
fn_id: ast::NodeId,
|
||||
body: &'gcx hir::Block)
|
||||
body: &'gcx hir::Expr)
|
||||
-> FnCtxt<'a, 'gcx, 'tcx>
|
||||
{
|
||||
let mut fn_sig = fn_sig.clone();
|
||||
@ -709,18 +769,12 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
|
||||
fcx.write_ty(input.id, arg_ty);
|
||||
}
|
||||
|
||||
visit.visit_block(body);
|
||||
visit.visit_expr(body);
|
||||
}
|
||||
|
||||
inherited.tables.borrow_mut().liberated_fn_sigs.insert(fn_id, fn_sig);
|
||||
|
||||
// FIXME(aburka) do we need this special case? and should it be is_uninhabited?
|
||||
let expected = if fcx.ret_ty.is_never() {
|
||||
NoExpectation
|
||||
} else {
|
||||
ExpectHasType(fcx.ret_ty)
|
||||
};
|
||||
fcx.check_block_with_expected(body, expected);
|
||||
fcx.check_expr_coercable_to_type(body, fcx.ret_ty);
|
||||
|
||||
fcx
|
||||
}
|
||||
@ -1151,7 +1205,7 @@ fn check_const_with_type<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>,
|
||||
fcx.check_expr_coercable_to_type(expr, expected_type);
|
||||
|
||||
fcx.select_all_obligations_and_apply_defaults();
|
||||
fcx.closure_analyze_const(expr);
|
||||
fcx.closure_analyze(expr);
|
||||
fcx.select_obligations_where_possible();
|
||||
fcx.check_casts();
|
||||
fcx.select_all_obligations_or_error();
|
||||
@ -1437,6 +1491,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
ret_ty: rty,
|
||||
ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal,
|
||||
ast::CRATE_NODE_ID)),
|
||||
diverges: Cell::new(Diverges::Maybe),
|
||||
has_errors: Cell::new(false),
|
||||
inh: inh,
|
||||
}
|
||||
}
|
||||
@ -1453,6 +1509,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
self.tcx.sess.err_count() - self.err_count_on_creation
|
||||
}
|
||||
|
||||
/// Produce warning on the given node, if the current point in the
|
||||
/// function is unreachable, and there hasn't been another warning.
|
||||
fn warn_if_unreachable(&self, id: ast::NodeId, span: Span, kind: &str) {
|
||||
if self.diverges.get() == Diverges::Always {
|
||||
self.diverges.set(Diverges::WarnedAlways);
|
||||
|
||||
self.tcx.sess.add_lint(lint::builtin::UNREACHABLE_CODE,
|
||||
id, span,
|
||||
format!("unreachable {}", kind));
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolves type variables in `ty` if possible. Unlike the infcx
|
||||
/// version (resolve_type_vars_if_possible), this version will
|
||||
/// also select obligations if it seems useful, in an effort
|
||||
@ -1523,6 +1591,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
debug!("write_ty({}, {:?}) in fcx {}",
|
||||
node_id, ty, self.tag());
|
||||
self.tables.borrow_mut().node_types.insert(node_id, ty);
|
||||
|
||||
if ty.references_error() {
|
||||
self.has_errors.set(true);
|
||||
}
|
||||
|
||||
// FIXME(canndrew): This is_never should probably be an is_uninhabited
|
||||
if ty.is_never() || self.type_var_diverges(ty) {
|
||||
self.diverges.set(self.diverges.get() | Diverges::Always);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_substs(&self, node_id: ast::NodeId, substs: ty::ItemSubsts<'tcx>) {
|
||||
@ -2455,21 +2532,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
|
||||
// Check the arguments.
|
||||
// We do this in a pretty awful way: first we typecheck any arguments
|
||||
// that are not anonymous functions, then we typecheck the anonymous
|
||||
// functions. This is so that we have more information about the types
|
||||
// of arguments when we typecheck the functions. This isn't really the
|
||||
// right way to do this.
|
||||
let xs = [false, true];
|
||||
let mut any_diverges = false; // has any of the arguments diverged?
|
||||
let mut warned = false; // have we already warned about unreachable code?
|
||||
for check_blocks in &xs {
|
||||
let check_blocks = *check_blocks;
|
||||
debug!("check_blocks={}", check_blocks);
|
||||
// that are not closures, then we typecheck the closures. This is so
|
||||
// that we have more information about the types of arguments when we
|
||||
// typecheck the functions. This isn't really the right way to do this.
|
||||
for &check_closures in &[false, true] {
|
||||
debug!("check_closures={}", check_closures);
|
||||
|
||||
// More awful hacks: before we check argument types, try to do
|
||||
// an "opportunistic" vtable resolution of any trait bounds on
|
||||
// the call. This helps coercions.
|
||||
if check_blocks {
|
||||
if check_closures {
|
||||
self.select_obligations_where_possible();
|
||||
}
|
||||
|
||||
@ -2484,61 +2556,43 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
supplied_arg_count
|
||||
};
|
||||
for (i, arg) in args.iter().take(t).enumerate() {
|
||||
if any_diverges && !warned {
|
||||
self.tcx
|
||||
.sess
|
||||
.add_lint(lint::builtin::UNREACHABLE_CODE,
|
||||
arg.id,
|
||||
arg.span,
|
||||
"unreachable expression".to_string());
|
||||
warned = true;
|
||||
// Warn only for the first loop (the "no closures" one).
|
||||
// Closure arguments themselves can't be diverging, but
|
||||
// a previous argument can, e.g. `foo(panic!(), || {})`.
|
||||
if !check_closures {
|
||||
self.warn_if_unreachable(arg.id, arg.span, "expression");
|
||||
}
|
||||
let is_block = match arg.node {
|
||||
|
||||
let is_closure = match arg.node {
|
||||
hir::ExprClosure(..) => true,
|
||||
_ => false
|
||||
};
|
||||
|
||||
if is_block == check_blocks {
|
||||
debug!("checking the argument");
|
||||
let formal_ty = formal_tys[i];
|
||||
|
||||
// The special-cased logic below has three functions:
|
||||
// 1. Provide as good of an expected type as possible.
|
||||
let expected = expected_arg_tys.get(i).map(|&ty| {
|
||||
Expectation::rvalue_hint(self, ty)
|
||||
});
|
||||
|
||||
let checked_ty = self.check_expr_with_expectation(&arg,
|
||||
expected.unwrap_or(ExpectHasType(formal_ty)));
|
||||
// 2. Coerce to the most detailed type that could be coerced
|
||||
// to, which is `expected_ty` if `rvalue_hint` returns an
|
||||
// `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise.
|
||||
let coerce_ty = expected.and_then(|e| e.only_has_type(self));
|
||||
self.demand_coerce(&arg, checked_ty, coerce_ty.unwrap_or(formal_ty));
|
||||
|
||||
// 3. Relate the expected type and the formal one,
|
||||
// if the expected type was used for the coercion.
|
||||
coerce_ty.map(|ty| self.demand_suptype(arg.span, formal_ty, ty));
|
||||
if is_closure != check_closures {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(&arg_ty) = self.tables.borrow().node_types.get(&arg.id) {
|
||||
// FIXME(canndrew): This is_never should probably be an is_uninhabited
|
||||
any_diverges = any_diverges ||
|
||||
self.type_var_diverges(arg_ty) ||
|
||||
arg_ty.is_never();
|
||||
}
|
||||
}
|
||||
if any_diverges && !warned {
|
||||
let parent = self.tcx.map.get_parent_node(args[0].id);
|
||||
self.tcx
|
||||
.sess
|
||||
.add_lint(lint::builtin::UNREACHABLE_CODE,
|
||||
parent,
|
||||
sp,
|
||||
"unreachable call".to_string());
|
||||
warned = true;
|
||||
}
|
||||
debug!("checking the argument");
|
||||
let formal_ty = formal_tys[i];
|
||||
|
||||
// The special-cased logic below has three functions:
|
||||
// 1. Provide as good of an expected type as possible.
|
||||
let expected = expected_arg_tys.get(i).map(|&ty| {
|
||||
Expectation::rvalue_hint(self, ty)
|
||||
});
|
||||
|
||||
let checked_ty = self.check_expr_with_expectation(&arg,
|
||||
expected.unwrap_or(ExpectHasType(formal_ty)));
|
||||
// 2. Coerce to the most detailed type that could be coerced
|
||||
// to, which is `expected_ty` if `rvalue_hint` returns an
|
||||
// `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise.
|
||||
let coerce_ty = expected.and_then(|e| e.only_has_type(self));
|
||||
self.demand_coerce(&arg, checked_ty, coerce_ty.unwrap_or(formal_ty));
|
||||
|
||||
// 3. Relate the expected type and the formal one,
|
||||
// if the expected type was used for the coercion.
|
||||
coerce_ty.map(|ty| self.demand_suptype(arg.span, formal_ty, ty));
|
||||
}
|
||||
}
|
||||
|
||||
// We also need to make sure we at least write the ty of the other
|
||||
@ -2789,18 +2843,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
sp: Span,
|
||||
expected: Expectation<'tcx>) -> Ty<'tcx> {
|
||||
let cond_ty = self.check_expr_has_type(cond_expr, self.tcx.types.bool);
|
||||
let cond_diverges = self.diverges.get();
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
|
||||
let expected = expected.adjust_for_branches(self);
|
||||
let then_ty = self.check_block_with_expected(then_blk, expected);
|
||||
let then_diverges = self.diverges.get();
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
|
||||
let unit = self.tcx.mk_nil();
|
||||
let (origin, expected, found, result) =
|
||||
if let Some(else_expr) = opt_else_expr {
|
||||
let else_ty = self.check_expr_with_expectation(else_expr, expected);
|
||||
let origin = TypeOrigin::IfExpression(sp);
|
||||
let else_diverges = self.diverges.get();
|
||||
|
||||
// Only try to coerce-unify if we have a then expression
|
||||
// to assign coercions to, otherwise it's () or diverging.
|
||||
let origin = TypeOrigin::IfExpression(sp);
|
||||
let result = if let Some(ref then) = then_blk.expr {
|
||||
let res = self.try_find_coercion_lub(origin, || Some(&**then),
|
||||
then_ty, else_expr, else_ty);
|
||||
@ -2826,8 +2885,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
// We won't diverge unless both branches do (or the condition does).
|
||||
self.diverges.set(cond_diverges | then_diverges & else_diverges);
|
||||
|
||||
(origin, then_ty, else_ty, result)
|
||||
} else {
|
||||
// If the condition is false we can't diverge.
|
||||
self.diverges.set(cond_diverges);
|
||||
|
||||
let origin = TypeOrigin::IfExpressionWithNoElse(sp);
|
||||
(origin, unit, then_ty,
|
||||
self.eq_types(true, origin, unit, then_ty)
|
||||
@ -3288,10 +3354,36 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
lvalue_pref: LvaluePreference) -> Ty<'tcx> {
|
||||
debug!(">> typechecking: expr={:?} expected={:?}",
|
||||
expr, expected);
|
||||
|
||||
// Warn for expressions after diverging siblings.
|
||||
self.warn_if_unreachable(expr.id, expr.span, "expression");
|
||||
|
||||
// Hide the outer diverging and has_errors flags.
|
||||
let old_diverges = self.diverges.get();
|
||||
let old_has_errors = self.has_errors.get();
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
self.has_errors.set(false);
|
||||
|
||||
let ty = self.check_expr_kind(expr, expected, lvalue_pref);
|
||||
|
||||
// Warn for non-block expressions with diverging children.
|
||||
match expr.node {
|
||||
hir::ExprBlock(_) |
|
||||
hir::ExprLoop(..) | hir::ExprWhile(..) |
|
||||
hir::ExprIf(..) | hir::ExprMatch(..) => {}
|
||||
|
||||
_ => self.warn_if_unreachable(expr.id, expr.span, "expression")
|
||||
}
|
||||
|
||||
// Record the type, which applies it effects.
|
||||
// We need to do this after the warning above, so that
|
||||
// we don't warn for the diverging expression itself.
|
||||
self.write_ty(expr.id, ty);
|
||||
|
||||
// Combine the diverging and has_error flags.
|
||||
self.diverges.set(self.diverges.get() | old_diverges);
|
||||
self.has_errors.set(self.has_errors.get() | old_has_errors);
|
||||
|
||||
debug!("type of expr({}) {} is...", expr.id,
|
||||
pprust::expr_to_string(expr));
|
||||
debug!("... {:?}, expected is {:?}",
|
||||
@ -3516,22 +3608,29 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
expr.span, expected)
|
||||
}
|
||||
hir::ExprWhile(ref cond, ref body, _) => {
|
||||
let cond_ty = self.check_expr_has_type(&cond, tcx.types.bool);
|
||||
self.check_expr_has_type(&cond, tcx.types.bool);
|
||||
let cond_diverging = self.diverges.get();
|
||||
self.check_block_no_value(&body);
|
||||
let body_ty = self.node_ty(body.id);
|
||||
if cond_ty.references_error() || body_ty.references_error() {
|
||||
|
||||
// We may never reach the body so it diverging means nothing.
|
||||
self.diverges.set(cond_diverging);
|
||||
|
||||
if self.has_errors.get() {
|
||||
tcx.types.err
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
tcx.mk_nil()
|
||||
}
|
||||
}
|
||||
hir::ExprLoop(ref body, _) => {
|
||||
self.check_block_no_value(&body);
|
||||
if !may_break(tcx, expr.id, &body) {
|
||||
tcx.types.never
|
||||
} else {
|
||||
if may_break(tcx, expr.id, &body) {
|
||||
// No way to know whether it's diverging because
|
||||
// of a `break` or an outer `break` or `return.
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
|
||||
tcx.mk_nil()
|
||||
} else {
|
||||
tcx.types.never
|
||||
}
|
||||
}
|
||||
hir::ExprMatch(ref discrim, ref arms, match_src) => {
|
||||
@ -3864,55 +3963,66 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
}
|
||||
|
||||
pub fn check_stmt(&self, stmt: &'gcx hir::Stmt) {
|
||||
let node_id;
|
||||
let mut saw_bot = false;
|
||||
let mut saw_err = false;
|
||||
// Don't do all the complex logic below for DeclItem.
|
||||
match stmt.node {
|
||||
hir::StmtDecl(ref decl, id) => {
|
||||
node_id = id;
|
||||
match decl.node {
|
||||
hir::DeclLocal(ref l) => {
|
||||
self.check_decl_local(&l);
|
||||
let l_t = self.node_ty(l.id);
|
||||
saw_bot = saw_bot || self.type_var_diverges(l_t);
|
||||
saw_err = saw_err || l_t.references_error();
|
||||
}
|
||||
hir::DeclItem(_) => {/* ignore for now */ }
|
||||
hir::StmtDecl(ref decl, id) => {
|
||||
match decl.node {
|
||||
hir::DeclLocal(_) => {}
|
||||
hir::DeclItem(_) => {
|
||||
self.write_nil(id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
hir::StmtExpr(ref expr, id) => {
|
||||
node_id = id;
|
||||
// Check with expected type of ()
|
||||
let ty = self.check_expr_has_type(&expr, self.tcx.mk_nil());
|
||||
saw_bot = saw_bot || self.type_var_diverges(ty);
|
||||
saw_err = saw_err || ty.references_error();
|
||||
}
|
||||
hir::StmtSemi(ref expr, id) => {
|
||||
node_id = id;
|
||||
let ty = self.check_expr(&expr);
|
||||
saw_bot |= self.type_var_diverges(ty);
|
||||
saw_err |= ty.references_error();
|
||||
}
|
||||
hir::StmtExpr(..) | hir::StmtSemi(..) => {}
|
||||
}
|
||||
if saw_bot {
|
||||
self.write_ty(node_id, self.next_diverging_ty_var());
|
||||
}
|
||||
else if saw_err {
|
||||
|
||||
self.warn_if_unreachable(stmt.node.id(), stmt.span, "statement");
|
||||
|
||||
// Hide the outer diverging and has_errors flags.
|
||||
let old_diverges = self.diverges.get();
|
||||
let old_has_errors = self.has_errors.get();
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
self.has_errors.set(false);
|
||||
|
||||
let node_id = match stmt.node {
|
||||
hir::StmtDecl(ref decl, id) => {
|
||||
match decl.node {
|
||||
hir::DeclLocal(ref l) => {
|
||||
self.check_decl_local(&l);
|
||||
}
|
||||
hir::DeclItem(_) => {/* ignore for now */ }
|
||||
}
|
||||
id
|
||||
}
|
||||
hir::StmtExpr(ref expr, id) => {
|
||||
// Check with expected type of ()
|
||||
self.check_expr_has_type(&expr, self.tcx.mk_nil());
|
||||
id
|
||||
}
|
||||
hir::StmtSemi(ref expr, id) => {
|
||||
self.check_expr(&expr);
|
||||
id
|
||||
}
|
||||
};
|
||||
|
||||
if self.has_errors.get() {
|
||||
self.write_error(node_id);
|
||||
}
|
||||
else {
|
||||
} else if self.diverges.get().always() {
|
||||
self.write_ty(node_id, self.next_diverging_ty_var());
|
||||
} else {
|
||||
self.write_nil(node_id);
|
||||
}
|
||||
|
||||
// Combine the diverging and has_error flags.
|
||||
self.diverges.set(self.diverges.get() | old_diverges);
|
||||
self.has_errors.set(self.has_errors.get() | old_has_errors);
|
||||
}
|
||||
|
||||
pub fn check_block_no_value(&self, blk: &'gcx hir::Block) {
|
||||
let blkty = self.check_block_with_expected(blk, ExpectHasType(self.tcx.mk_nil()));
|
||||
if blkty.references_error() {
|
||||
self.write_error(blk.id);
|
||||
} else {
|
||||
let nilty = self.tcx.mk_nil();
|
||||
self.demand_suptype(blk.span, nilty, blkty);
|
||||
}
|
||||
let unit = self.tcx.mk_nil();
|
||||
let ty = self.check_block_with_expected(blk, ExpectHasType(unit));
|
||||
self.demand_suptype(blk.span, unit, ty);
|
||||
}
|
||||
|
||||
fn check_block_with_expected(&self,
|
||||
@ -3924,72 +4034,81 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
replace(&mut *fcx_ps, unsafety_state)
|
||||
};
|
||||
|
||||
let mut warned = false;
|
||||
let mut any_diverges = false;
|
||||
let mut any_err = false;
|
||||
for s in &blk.stmts {
|
||||
self.check_stmt(s);
|
||||
let s_id = s.node.id();
|
||||
let s_ty = self.node_ty(s_id);
|
||||
if any_diverges && !warned && match s.node {
|
||||
hir::StmtDecl(ref decl, _) => {
|
||||
match decl.node {
|
||||
hir::DeclLocal(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
hir::StmtExpr(..) | hir::StmtSemi(..) => true,
|
||||
} {
|
||||
self.tcx
|
||||
.sess
|
||||
.add_lint(lint::builtin::UNREACHABLE_CODE,
|
||||
s_id,
|
||||
s.span,
|
||||
"unreachable statement".to_string());
|
||||
warned = true;
|
||||
}
|
||||
// FIXME(canndrew): This is_never should probably be an is_uninhabited
|
||||
any_diverges = any_diverges ||
|
||||
self.type_var_diverges(s_ty) ||
|
||||
s_ty.is_never();
|
||||
any_err = any_err || s_ty.references_error();
|
||||
}
|
||||
let ty = match blk.expr {
|
||||
None => if any_err {
|
||||
self.tcx.types.err
|
||||
} else if any_diverges {
|
||||
self.next_diverging_ty_var()
|
||||
} else {
|
||||
self.tcx.mk_nil()
|
||||
},
|
||||
Some(ref e) => {
|
||||
if any_diverges && !warned {
|
||||
self.tcx
|
||||
.sess
|
||||
.add_lint(lint::builtin::UNREACHABLE_CODE,
|
||||
e.id,
|
||||
e.span,
|
||||
"unreachable expression".to_string());
|
||||
}
|
||||
let ety = match expected {
|
||||
ExpectHasType(ety) => {
|
||||
self.check_expr_coercable_to_type(&e, ety);
|
||||
ety
|
||||
}
|
||||
_ => {
|
||||
self.check_expr_with_expectation(&e, expected)
|
||||
}
|
||||
};
|
||||
|
||||
if any_err {
|
||||
self.tcx.types.err
|
||||
} else if any_diverges {
|
||||
self.next_diverging_ty_var()
|
||||
} else {
|
||||
ety
|
||||
let mut ty = match blk.expr {
|
||||
Some(ref e) => self.check_expr_with_expectation(e, expected),
|
||||
None => self.tcx.mk_nil()
|
||||
};
|
||||
|
||||
if self.diverges.get().always() {
|
||||
if let ExpectHasType(ety) = expected {
|
||||
// Avoid forcing a type (only `!` for now) in unreachable code.
|
||||
// FIXME(aburka) do we need this special case? and should it be is_uninhabited?
|
||||
if !ety.is_never() {
|
||||
if let Some(ref e) = blk.expr {
|
||||
// Coerce the tail expression to the right type.
|
||||
self.demand_coerce(e, ty, ety);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ty = self.next_diverging_ty_var();
|
||||
} else if let ExpectHasType(ety) = expected {
|
||||
if let Some(ref e) = blk.expr {
|
||||
// Coerce the tail expression to the right type.
|
||||
self.demand_coerce(e, ty, ety);
|
||||
} else {
|
||||
// We're not diverging and there's an expected type, which,
|
||||
// in case it's not `()`, could result in an error higher-up.
|
||||
// We have a chance to error here early and be more helpful.
|
||||
let origin = TypeOrigin::Misc(blk.span);
|
||||
let trace = TypeTrace::types(origin, false, ty, ety);
|
||||
match self.sub_types(false, origin, ty, ety) {
|
||||
Ok(InferOk { obligations, .. }) => {
|
||||
// FIXME(#32730) propagate obligations
|
||||
assert!(obligations.is_empty());
|
||||
},
|
||||
Err(err) => {
|
||||
let mut err = self.report_and_explain_type_error(trace, &err);
|
||||
|
||||
// Be helpful when the user wrote `{... expr;}` and
|
||||
// taking the `;` off is enough to fix the error.
|
||||
let mut extra_semi = None;
|
||||
if let Some(stmt) = blk.stmts.last() {
|
||||
if let hir::StmtSemi(ref e, _) = stmt.node {
|
||||
if self.can_sub_types(self.node_ty(e.id), ety).is_ok() {
|
||||
extra_semi = Some(stmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(last_stmt) = extra_semi {
|
||||
let original_span = original_sp(self.tcx.sess.codemap(),
|
||||
last_stmt.span, blk.span);
|
||||
let span_semi = Span {
|
||||
lo: original_span.hi - BytePos(1),
|
||||
hi: original_span.hi,
|
||||
expn_id: original_span.expn_id
|
||||
};
|
||||
err.span_help(span_semi, "consider removing this semicolon:");
|
||||
}
|
||||
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We already applied the type (and potentially errored),
|
||||
// use the expected type to avoid further errors out.
|
||||
ty = ety;
|
||||
}
|
||||
|
||||
if self.has_errors.get() || ty.references_error() {
|
||||
ty = self.tcx.types.err
|
||||
}
|
||||
|
||||
self.write_ty(blk.id, ty);
|
||||
|
||||
*self.ps.borrow_mut() = prev;
|
||||
|
@ -75,8 +75,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
match BinOpCategory::from(op) {
|
||||
BinOpCategory::Shortcircuit => {
|
||||
// && and || are a simple case.
|
||||
let lhs_diverges = self.diverges.get();
|
||||
self.demand_suptype(lhs_expr.span, tcx.mk_bool(), lhs_ty);
|
||||
self.check_expr_coercable_to_type(rhs_expr, tcx.mk_bool());
|
||||
|
||||
// Depending on the LHS' value, the RHS can never execute.
|
||||
self.diverges.set(lhs_diverges);
|
||||
|
||||
tcx.mk_bool()
|
||||
}
|
||||
_ => {
|
||||
|
@ -142,13 +142,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
pub fn regionck_fn(&self,
|
||||
fn_id: ast::NodeId,
|
||||
decl: &hir::FnDecl,
|
||||
blk: &hir::Block) {
|
||||
body: &hir::Expr) {
|
||||
debug!("regionck_fn(id={})", fn_id);
|
||||
let mut rcx = RegionCtxt::new(self, RepeatingScope(blk.id), blk.id, Subject(fn_id));
|
||||
let mut rcx = RegionCtxt::new(self, RepeatingScope(body.id), body.id, Subject(fn_id));
|
||||
|
||||
if self.err_count_since_creation() == 0 {
|
||||
// regionck assumes typeck succeeded
|
||||
rcx.visit_fn_body(fn_id, decl, blk, self.tcx.map.span(fn_id));
|
||||
rcx.visit_fn_body(fn_id, decl, body, self.tcx.map.span(fn_id));
|
||||
}
|
||||
|
||||
rcx.free_region_map.relate_free_regions_from_predicates(
|
||||
@ -268,7 +268,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
|
||||
fn visit_fn_body(&mut self,
|
||||
id: ast::NodeId, // the id of the fn itself
|
||||
fn_decl: &hir::FnDecl,
|
||||
body: &hir::Block,
|
||||
body: &hir::Expr,
|
||||
span: Span)
|
||||
{
|
||||
// When we enter a function, we can derive
|
||||
@ -305,7 +305,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
|
||||
self.relate_free_regions(&fn_sig_tys[..], body.id, span);
|
||||
self.link_fn_args(self.tcx.region_maps.node_extent(body.id),
|
||||
&fn_decl.inputs[..]);
|
||||
self.visit_block(body);
|
||||
self.visit_expr(body);
|
||||
self.visit_region_obligations(body.id);
|
||||
|
||||
let call_site_scope = self.call_site_scope.unwrap();
|
||||
@ -480,7 +480,7 @@ impl<'a, 'gcx, 'tcx, 'v> Visitor<'v> for RegionCtxt<'a, 'gcx, 'tcx> {
|
||||
// regions, until regionck, as described in #3238.
|
||||
|
||||
fn visit_fn(&mut self, _fk: intravisit::FnKind<'v>, fd: &'v hir::FnDecl,
|
||||
b: &'v hir::Block, span: Span, id: ast::NodeId) {
|
||||
b: &'v hir::Expr, span: Span, id: ast::NodeId) {
|
||||
self.visit_fn_body(id, fd, b, span)
|
||||
}
|
||||
|
||||
@ -825,7 +825,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
|
||||
|
||||
fn check_expr_fn_block(&mut self,
|
||||
expr: &hir::Expr,
|
||||
body: &hir::Block) {
|
||||
body: &hir::Expr) {
|
||||
let repeating_scope = self.set_repeating_scope(body.id);
|
||||
intravisit::walk_expr(self, expr);
|
||||
self.set_repeating_scope(repeating_scope);
|
||||
|
@ -57,18 +57,7 @@ use rustc::util::nodemap::NodeMap;
|
||||
// PUBLIC ENTRY POINTS
|
||||
|
||||
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
pub fn closure_analyze_fn(&self, body: &hir::Block) {
|
||||
let mut seed = SeedBorrowKind::new(self);
|
||||
seed.visit_block(body);
|
||||
|
||||
let mut adjust = AdjustBorrowKind::new(self, seed.temp_closure_kinds);
|
||||
adjust.visit_block(body);
|
||||
|
||||
// it's our job to process these.
|
||||
assert!(self.deferred_call_resolutions.borrow().is_empty());
|
||||
}
|
||||
|
||||
pub fn closure_analyze_const(&self, body: &hir::Expr) {
|
||||
pub fn closure_analyze(&self, body: &hir::Expr) {
|
||||
let mut seed = SeedBorrowKind::new(self);
|
||||
seed.visit_expr(body);
|
||||
|
||||
@ -110,7 +99,7 @@ impl<'a, 'gcx, 'tcx> SeedBorrowKind<'a, 'gcx, 'tcx> {
|
||||
fn check_closure(&mut self,
|
||||
expr: &hir::Expr,
|
||||
capture_clause: hir::CaptureClause,
|
||||
_body: &hir::Block)
|
||||
_body: &hir::Expr)
|
||||
{
|
||||
let closure_def_id = self.fcx.tcx.map.local_def_id(expr.id);
|
||||
if !self.fcx.tables.borrow().closure_kinds.contains_key(&closure_def_id) {
|
||||
@ -164,7 +153,7 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
|
||||
id: ast::NodeId,
|
||||
span: Span,
|
||||
decl: &hir::FnDecl,
|
||||
body: &hir::Block) {
|
||||
body: &hir::Expr) {
|
||||
/*!
|
||||
* Analysis starting point.
|
||||
*/
|
||||
@ -497,7 +486,7 @@ impl<'a, 'gcx, 'tcx, 'v> Visitor<'v> for AdjustBorrowKind<'a, 'gcx, 'tcx> {
|
||||
fn visit_fn(&mut self,
|
||||
fn_kind: intravisit::FnKind<'v>,
|
||||
decl: &'v hir::FnDecl,
|
||||
body: &'v hir::Block,
|
||||
body: &'v hir::Expr,
|
||||
span: Span,
|
||||
id: ast::NodeId)
|
||||
{
|
||||
|
@ -348,7 +348,7 @@ impl<'ccx, 'gcx> CheckTypeWellFormedVisitor<'ccx, 'gcx> {
|
||||
|
||||
fn check_item_fn(&mut self,
|
||||
item: &hir::Item,
|
||||
body: &hir::Block)
|
||||
body: &hir::Expr)
|
||||
{
|
||||
self.for_item(item).with_fcx(|fcx, this| {
|
||||
let free_substs = &fcx.parameter_environment.free_substs;
|
||||
|
@ -49,11 +49,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
|
||||
pub fn resolve_type_vars_in_fn(&self,
|
||||
decl: &hir::FnDecl,
|
||||
blk: &hir::Block,
|
||||
body: &hir::Expr,
|
||||
item_id: ast::NodeId) {
|
||||
assert_eq!(self.writeback_errors.get(), false);
|
||||
let mut wbcx = WritebackCx::new(self);
|
||||
wbcx.visit_block(blk);
|
||||
wbcx.visit_expr(body);
|
||||
for arg in &decl.inputs {
|
||||
wbcx.visit_node_id(ResolvingPattern(arg.pat.span), arg.id);
|
||||
wbcx.visit_pat(&arg.pat);
|
||||
|
@ -1017,10 +1017,10 @@ pub enum ExprKind {
|
||||
Loop(P<Block>, Option<SpannedIdent>),
|
||||
/// A `match` block.
|
||||
Match(P<Expr>, Vec<Arm>),
|
||||
/// A closure (for example, `move |a, b, c| {a + b + c}`)
|
||||
/// A closure (for example, `move |a, b, c| a + b + c`)
|
||||
///
|
||||
/// The final span is the span of the argument block `|...|`
|
||||
Closure(CaptureBy, P<FnDecl>, P<Block>, Span),
|
||||
Closure(CaptureBy, P<FnDecl>, P<Expr>, Span),
|
||||
/// A block (`{ ... }`)
|
||||
Block(P<Block>),
|
||||
|
||||
|
@ -198,17 +198,13 @@ pub trait AstBuilder {
|
||||
fn lambda_fn_decl(&self,
|
||||
span: Span,
|
||||
fn_decl: P<ast::FnDecl>,
|
||||
blk: P<ast::Block>,
|
||||
body: P<ast::Expr>,
|
||||
fn_decl_span: Span)
|
||||
-> P<ast::Expr>;
|
||||
|
||||
fn lambda(&self, span: Span, ids: Vec<ast::Ident>, blk: P<ast::Block>) -> P<ast::Expr>;
|
||||
fn lambda0(&self, span: Span, blk: P<ast::Block>) -> P<ast::Expr>;
|
||||
fn lambda1(&self, span: Span, blk: P<ast::Block>, ident: ast::Ident) -> P<ast::Expr>;
|
||||
|
||||
fn lambda_expr(&self, span: Span, ids: Vec<ast::Ident> , blk: P<ast::Expr>) -> P<ast::Expr>;
|
||||
fn lambda_expr_0(&self, span: Span, expr: P<ast::Expr>) -> P<ast::Expr>;
|
||||
fn lambda_expr_1(&self, span: Span, expr: P<ast::Expr>, ident: ast::Ident) -> P<ast::Expr>;
|
||||
fn lambda(&self, span: Span, ids: Vec<ast::Ident>, body: P<ast::Expr>) -> P<ast::Expr>;
|
||||
fn lambda0(&self, span: Span, body: P<ast::Expr>) -> P<ast::Expr>;
|
||||
fn lambda1(&self, span: Span, body: P<ast::Expr>, ident: ast::Ident) -> P<ast::Expr>;
|
||||
|
||||
fn lambda_stmts(&self, span: Span, ids: Vec<ast::Ident>,
|
||||
blk: Vec<ast::Stmt>) -> P<ast::Expr>;
|
||||
@ -940,19 +936,19 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
|
||||
fn lambda_fn_decl(&self,
|
||||
span: Span,
|
||||
fn_decl: P<ast::FnDecl>,
|
||||
blk: P<ast::Block>,
|
||||
body: P<ast::Expr>,
|
||||
fn_decl_span: Span) // span of the `|...|` part
|
||||
-> P<ast::Expr> {
|
||||
self.expr(span, ast::ExprKind::Closure(ast::CaptureBy::Ref,
|
||||
fn_decl,
|
||||
blk,
|
||||
body,
|
||||
fn_decl_span))
|
||||
}
|
||||
|
||||
fn lambda(&self,
|
||||
span: Span,
|
||||
ids: Vec<ast::Ident>,
|
||||
blk: P<ast::Block>)
|
||||
body: P<ast::Expr>)
|
||||
-> P<ast::Expr> {
|
||||
let fn_decl = self.fn_decl(
|
||||
ids.iter().map(|id| self.arg(span, *id, self.ty_infer(span))).collect(),
|
||||
@ -962,26 +958,15 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
|
||||
// part of the lambda, but it probably (maybe?) corresponds to
|
||||
// the entire lambda body. Probably we should extend the API
|
||||
// here, but that's not entirely clear.
|
||||
self.expr(span, ast::ExprKind::Closure(ast::CaptureBy::Ref, fn_decl, blk, span))
|
||||
self.expr(span, ast::ExprKind::Closure(ast::CaptureBy::Ref, fn_decl, body, span))
|
||||
}
|
||||
|
||||
fn lambda0(&self, span: Span, blk: P<ast::Block>) -> P<ast::Expr> {
|
||||
self.lambda(span, Vec::new(), blk)
|
||||
fn lambda0(&self, span: Span, body: P<ast::Expr>) -> P<ast::Expr> {
|
||||
self.lambda(span, Vec::new(), body)
|
||||
}
|
||||
|
||||
fn lambda1(&self, span: Span, blk: P<ast::Block>, ident: ast::Ident) -> P<ast::Expr> {
|
||||
self.lambda(span, vec![ident], blk)
|
||||
}
|
||||
|
||||
fn lambda_expr(&self, span: Span, ids: Vec<ast::Ident>,
|
||||
expr: P<ast::Expr>) -> P<ast::Expr> {
|
||||
self.lambda(span, ids, self.block_expr(expr))
|
||||
}
|
||||
fn lambda_expr_0(&self, span: Span, expr: P<ast::Expr>) -> P<ast::Expr> {
|
||||
self.lambda0(span, self.block_expr(expr))
|
||||
}
|
||||
fn lambda_expr_1(&self, span: Span, expr: P<ast::Expr>, ident: ast::Ident) -> P<ast::Expr> {
|
||||
self.lambda1(span, self.block_expr(expr), ident)
|
||||
fn lambda1(&self, span: Span, body: P<ast::Expr>, ident: ast::Ident) -> P<ast::Expr> {
|
||||
self.lambda(span, vec![ident], body)
|
||||
}
|
||||
|
||||
fn lambda_stmts(&self,
|
||||
@ -989,14 +974,14 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
|
||||
ids: Vec<ast::Ident>,
|
||||
stmts: Vec<ast::Stmt>)
|
||||
-> P<ast::Expr> {
|
||||
self.lambda(span, ids, self.block(span, stmts))
|
||||
self.lambda(span, ids, self.expr_block(self.block(span, stmts)))
|
||||
}
|
||||
fn lambda_stmts_0(&self, span: Span, stmts: Vec<ast::Stmt>) -> P<ast::Expr> {
|
||||
self.lambda0(span, self.block(span, stmts))
|
||||
self.lambda0(span, self.expr_block(self.block(span, stmts)))
|
||||
}
|
||||
fn lambda_stmts_1(&self, span: Span, stmts: Vec<ast::Stmt>,
|
||||
ident: ast::Ident) -> P<ast::Expr> {
|
||||
self.lambda1(span, self.block(span, stmts), ident)
|
||||
self.lambda1(span, self.expr_block(self.block(span, stmts)), ident)
|
||||
}
|
||||
|
||||
fn arg(&self, span: Span, ident: ast::Ident, ty: P<ast::Ty>) -> ast::Arg {
|
||||
|
@ -1228,12 +1228,11 @@ impl<'a> Visitor for PostExpansionVisitor<'a> {
|
||||
fn visit_fn(&mut self,
|
||||
fn_kind: FnKind,
|
||||
fn_decl: &ast::FnDecl,
|
||||
block: &ast::Block,
|
||||
span: Span,
|
||||
_node_id: NodeId) {
|
||||
// check for const fn declarations
|
||||
match fn_kind {
|
||||
FnKind::ItemFn(_, _, _, Spanned { node: ast::Constness::Const, .. }, _, _) => {
|
||||
FnKind::ItemFn(_, _, _, Spanned { node: ast::Constness::Const, .. }, _, _, _) => {
|
||||
gate_feature_post!(&self, const_fn, span, "const fn is unstable");
|
||||
}
|
||||
_ => {
|
||||
@ -1245,13 +1244,13 @@ impl<'a> Visitor for PostExpansionVisitor<'a> {
|
||||
}
|
||||
|
||||
match fn_kind {
|
||||
FnKind::ItemFn(_, _, _, _, abi, _) |
|
||||
FnKind::Method(_, &ast::MethodSig { abi, .. }, _) => {
|
||||
FnKind::ItemFn(_, _, _, _, abi, _, _) |
|
||||
FnKind::Method(_, &ast::MethodSig { abi, .. }, _, _) => {
|
||||
self.check_abi(abi, span);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
visit::walk_fn(self, fn_kind, fn_decl, block, span);
|
||||
visit::walk_fn(self, fn_kind, fn_decl, span);
|
||||
}
|
||||
|
||||
fn visit_trait_item(&mut self, ti: &ast::TraitItem) {
|
||||
|
@ -1201,7 +1201,7 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu
|
||||
ExprKind::Closure(capture_clause, decl, body, span) => {
|
||||
ExprKind::Closure(capture_clause,
|
||||
folder.fold_fn_decl(decl),
|
||||
folder.fold_block(body),
|
||||
folder.fold_expr(body),
|
||||
folder.new_span(span))
|
||||
}
|
||||
ExprKind::Block(blk) => ExprKind::Block(folder.fold_block(blk)),
|
||||
|
@ -3162,25 +3162,12 @@ impl<'a> Parser<'a> {
|
||||
let decl = self.parse_fn_block_decl()?;
|
||||
let decl_hi = self.prev_span.hi;
|
||||
let body = match decl.output {
|
||||
FunctionRetTy::Default(_) => {
|
||||
// If no explicit return type is given, parse any
|
||||
// expr and wrap it up in a dummy block:
|
||||
let body_expr = self.parse_expr()?;
|
||||
P(ast::Block {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span: body_expr.span,
|
||||
stmts: vec![Stmt {
|
||||
span: body_expr.span,
|
||||
node: StmtKind::Expr(body_expr),
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
}],
|
||||
rules: BlockCheckMode::Default,
|
||||
})
|
||||
}
|
||||
FunctionRetTy::Default(_) => self.parse_expr()?,
|
||||
_ => {
|
||||
// If an explicit return type is given, require a
|
||||
// block to appear (RFC 968).
|
||||
self.parse_block()?
|
||||
let body_lo = self.span.lo;
|
||||
self.parse_block_expr(body_lo, BlockCheckMode::Default, ThinVec::new())?
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2128,26 +2128,8 @@ impl<'a> State<'a> {
|
||||
|
||||
try!(self.print_fn_block_args(&decl));
|
||||
try!(space(&mut self.s));
|
||||
|
||||
let default_return = match decl.output {
|
||||
ast::FunctionRetTy::Default(..) => true,
|
||||
_ => false
|
||||
};
|
||||
|
||||
match body.stmts.last().map(|stmt| &stmt.node) {
|
||||
Some(&ast::StmtKind::Expr(ref i_expr)) if default_return &&
|
||||
body.stmts.len() == 1 => {
|
||||
// we extract the block, so as not to create another set of boxes
|
||||
if let ast::ExprKind::Block(ref blk) = i_expr.node {
|
||||
try!(self.print_block_unclosed_with_attrs(&blk, &i_expr.attrs));
|
||||
} else {
|
||||
// this is a bare expression
|
||||
try!(self.print_expr(&i_expr));
|
||||
try!(self.end()); // need to close a box
|
||||
}
|
||||
}
|
||||
_ => try!(self.print_block_unclosed(&body)),
|
||||
}
|
||||
try!(self.print_expr(body));
|
||||
try!(self.end()); // need to close a box
|
||||
|
||||
// a box will be closed by print_expr, but we didn't want an overall
|
||||
// wrapper so we closed the corresponding opening. so create an
|
||||
|
@ -75,9 +75,9 @@ impl Visitor for NodeCounter {
|
||||
self.count += 1;
|
||||
walk_generics(self, g)
|
||||
}
|
||||
fn visit_fn(&mut self, fk: FnKind, fd: &FnDecl, b: &Block, s: Span, _: NodeId) {
|
||||
fn visit_fn(&mut self, fk: FnKind, fd: &FnDecl, s: Span, _: NodeId) {
|
||||
self.count += 1;
|
||||
walk_fn(self, fk, fd, b, s)
|
||||
walk_fn(self, fk, fd, s)
|
||||
}
|
||||
fn visit_trait_item(&mut self, ti: &TraitItem) {
|
||||
self.count += 1;
|
||||
|
@ -31,13 +31,13 @@ use codemap::Spanned;
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum FnKind<'a> {
|
||||
/// fn foo() or extern "Abi" fn foo()
|
||||
ItemFn(Ident, &'a Generics, Unsafety, Spanned<Constness>, Abi, &'a Visibility),
|
||||
ItemFn(Ident, &'a Generics, Unsafety, Spanned<Constness>, Abi, &'a Visibility, &'a Block),
|
||||
|
||||
/// fn foo(&self)
|
||||
Method(Ident, &'a MethodSig, Option<&'a Visibility>),
|
||||
Method(Ident, &'a MethodSig, Option<&'a Visibility>, &'a Block),
|
||||
|
||||
/// |x, y| {}
|
||||
Closure,
|
||||
/// |x, y| body
|
||||
Closure(&'a Expr),
|
||||
}
|
||||
|
||||
/// Each method of the Visitor trait is a hook to be potentially
|
||||
@ -68,8 +68,8 @@ pub trait Visitor: Sized {
|
||||
fn visit_expr_post(&mut self, _ex: &Expr) { }
|
||||
fn visit_ty(&mut self, t: &Ty) { walk_ty(self, t) }
|
||||
fn visit_generics(&mut self, g: &Generics) { walk_generics(self, g) }
|
||||
fn visit_fn(&mut self, fk: FnKind, fd: &FnDecl, b: &Block, s: Span, _: NodeId) {
|
||||
walk_fn(self, fk, fd, b, s)
|
||||
fn visit_fn(&mut self, fk: FnKind, fd: &FnDecl, s: Span, _: NodeId) {
|
||||
walk_fn(self, fk, fd, s)
|
||||
}
|
||||
fn visit_trait_item(&mut self, ti: &TraitItem) { walk_trait_item(self, ti) }
|
||||
fn visit_impl_item(&mut self, ii: &ImplItem) { walk_impl_item(self, ii) }
|
||||
@ -246,9 +246,8 @@ pub fn walk_item<V: Visitor>(visitor: &mut V, item: &Item) {
|
||||
}
|
||||
ItemKind::Fn(ref declaration, unsafety, constness, abi, ref generics, ref body) => {
|
||||
visitor.visit_fn(FnKind::ItemFn(item.ident, generics, unsafety,
|
||||
constness, abi, &item.vis),
|
||||
constness, abi, &item.vis, body),
|
||||
declaration,
|
||||
body,
|
||||
item.span,
|
||||
item.id)
|
||||
}
|
||||
@ -519,24 +518,25 @@ pub fn walk_fn_decl<V: Visitor>(visitor: &mut V, function_declaration: &FnDecl)
|
||||
visitor.visit_fn_ret_ty(&function_declaration.output)
|
||||
}
|
||||
|
||||
pub fn walk_fn_kind<V: Visitor>(visitor: &mut V, function_kind: FnKind) {
|
||||
match function_kind {
|
||||
FnKind::ItemFn(_, generics, _, _, _, _) => {
|
||||
visitor.visit_generics(generics);
|
||||
}
|
||||
FnKind::Method(_, ref sig, _) => {
|
||||
visitor.visit_generics(&sig.generics);
|
||||
}
|
||||
FnKind::Closure => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn walk_fn<V>(visitor: &mut V, kind: FnKind, declaration: &FnDecl, body: &Block, _span: Span)
|
||||
pub fn walk_fn<V>(visitor: &mut V, kind: FnKind, declaration: &FnDecl, _span: Span)
|
||||
where V: Visitor,
|
||||
{
|
||||
walk_fn_kind(visitor, kind);
|
||||
walk_fn_decl(visitor, declaration);
|
||||
visitor.visit_block(body)
|
||||
match kind {
|
||||
FnKind::ItemFn(_, generics, _, _, _, _, body) => {
|
||||
visitor.visit_generics(generics);
|
||||
walk_fn_decl(visitor, declaration);
|
||||
visitor.visit_block(body);
|
||||
}
|
||||
FnKind::Method(_, ref sig, _, body) => {
|
||||
visitor.visit_generics(&sig.generics);
|
||||
walk_fn_decl(visitor, declaration);
|
||||
visitor.visit_block(body);
|
||||
}
|
||||
FnKind::Closure(body) => {
|
||||
walk_fn_decl(visitor, declaration);
|
||||
visitor.visit_expr(body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn walk_trait_item<V: Visitor>(visitor: &mut V, trait_item: &TraitItem) {
|
||||
@ -552,8 +552,8 @@ pub fn walk_trait_item<V: Visitor>(visitor: &mut V, trait_item: &TraitItem) {
|
||||
walk_fn_decl(visitor, &sig.decl);
|
||||
}
|
||||
TraitItemKind::Method(ref sig, Some(ref body)) => {
|
||||
visitor.visit_fn(FnKind::Method(trait_item.ident, sig, None), &sig.decl,
|
||||
body, trait_item.span, trait_item.id);
|
||||
visitor.visit_fn(FnKind::Method(trait_item.ident, sig, None, body),
|
||||
&sig.decl, trait_item.span, trait_item.id);
|
||||
}
|
||||
TraitItemKind::Type(ref bounds, ref default) => {
|
||||
walk_list!(visitor, visit_ty_param_bound, bounds);
|
||||
@ -575,8 +575,8 @@ pub fn walk_impl_item<V: Visitor>(visitor: &mut V, impl_item: &ImplItem) {
|
||||
visitor.visit_expr(expr);
|
||||
}
|
||||
ImplItemKind::Method(ref sig, ref body) => {
|
||||
visitor.visit_fn(FnKind::Method(impl_item.ident, sig, Some(&impl_item.vis)), &sig.decl,
|
||||
body, impl_item.span, impl_item.id);
|
||||
visitor.visit_fn(FnKind::Method(impl_item.ident, sig, Some(&impl_item.vis), body),
|
||||
&sig.decl, impl_item.span, impl_item.id);
|
||||
}
|
||||
ImplItemKind::Type(ref ty) => {
|
||||
visitor.visit_ty(ty);
|
||||
@ -711,9 +711,8 @@ pub fn walk_expr<V: Visitor>(visitor: &mut V, expression: &Expr) {
|
||||
walk_list!(visitor, visit_arm, arms);
|
||||
}
|
||||
ExprKind::Closure(_, ref function_declaration, ref body, _decl_span) => {
|
||||
visitor.visit_fn(FnKind::Closure,
|
||||
visitor.visit_fn(FnKind::Closure(body),
|
||||
function_declaration,
|
||||
body,
|
||||
expression.span,
|
||||
expression.id)
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ fn decodable_substructure(cx: &mut ExtCtxt,
|
||||
cx.ident_of("read_struct"),
|
||||
vec![cx.expr_str(trait_span, substr.type_ident.name.as_str()),
|
||||
cx.expr_usize(trait_span, nfields),
|
||||
cx.lambda_expr_1(trait_span, result, blkarg)])
|
||||
cx.lambda1(trait_span, result, blkarg)])
|
||||
}
|
||||
StaticEnum(_, ref fields) => {
|
||||
let variant = cx.ident_of("i");
|
||||
@ -165,7 +165,7 @@ fn decodable_substructure(cx: &mut ExtCtxt,
|
||||
let result =
|
||||
cx.expr_ok(trait_span,
|
||||
cx.expr_match(trait_span, cx.expr_ident(trait_span, variant), arms));
|
||||
let lambda = cx.lambda_expr(trait_span, vec![blkarg, variant], result);
|
||||
let lambda = cx.lambda(trait_span, vec![blkarg, variant], result);
|
||||
let variant_vec = cx.expr_vec(trait_span, variants);
|
||||
let variant_vec = cx.expr_addr_of(trait_span, variant_vec);
|
||||
let result = cx.expr_method_call(trait_span,
|
||||
@ -176,7 +176,7 @@ fn decodable_substructure(cx: &mut ExtCtxt,
|
||||
decoder,
|
||||
cx.ident_of("read_enum"),
|
||||
vec![cx.expr_str(trait_span, substr.type_ident.name.as_str()),
|
||||
cx.lambda_expr_1(trait_span, result, blkarg)])
|
||||
cx.lambda1(trait_span, result, blkarg)])
|
||||
}
|
||||
_ => cx.bug("expected StaticEnum or StaticStruct in derive(Decodable)"),
|
||||
};
|
||||
|
@ -197,7 +197,7 @@ fn encodable_substructure(cx: &mut ExtCtxt,
|
||||
};
|
||||
let self_ref = cx.expr_addr_of(span, self_.clone());
|
||||
let enc = cx.expr_call(span, fn_path.clone(), vec![self_ref, blkencoder.clone()]);
|
||||
let lambda = cx.lambda_expr_1(span, enc, blkarg);
|
||||
let lambda = cx.lambda1(span, enc, blkarg);
|
||||
let call = cx.expr_method_call(span,
|
||||
blkencoder.clone(),
|
||||
emit_struct_field,
|
||||
@ -246,7 +246,7 @@ fn encodable_substructure(cx: &mut ExtCtxt,
|
||||
let self_ref = cx.expr_addr_of(span, self_.clone());
|
||||
let enc =
|
||||
cx.expr_call(span, fn_path.clone(), vec![self_ref, blkencoder.clone()]);
|
||||
let lambda = cx.lambda_expr_1(span, enc, blkarg);
|
||||
let lambda = cx.lambda1(span, enc, blkarg);
|
||||
let call = cx.expr_method_call(span,
|
||||
blkencoder.clone(),
|
||||
emit_variant_arg,
|
||||
@ -273,7 +273,7 @@ fn encodable_substructure(cx: &mut ExtCtxt,
|
||||
cx.expr_usize(trait_span, idx),
|
||||
cx.expr_usize(trait_span, fields.len()),
|
||||
blk]);
|
||||
let blk = cx.lambda_expr_1(trait_span, call, blkarg);
|
||||
let blk = cx.lambda1(trait_span, call, blkarg);
|
||||
let ret = cx.expr_method_call(trait_span,
|
||||
encoder,
|
||||
cx.ident_of("emit_enum"),
|
||||
|
@ -11,10 +11,10 @@
|
||||
#![feature(start)]
|
||||
|
||||
#[start]
|
||||
fn foo(argc: isize, argv: *const *const u8) -> isize {}
|
||||
fn foo(argc: isize, argv: *const *const u8) -> isize { 0 }
|
||||
//~^ NOTE previous `start` function here
|
||||
|
||||
#[start]
|
||||
fn f(argc: isize, argv: *const *const u8) -> isize {}
|
||||
fn f(argc: isize, argv: *const *const u8) -> isize { 0 }
|
||||
//~^ ERROR E0138
|
||||
//~| NOTE multiple `start` functions
|
||||
|
@ -8,12 +8,12 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
fn f() -> String { //~ ERROR E0269
|
||||
fn f() -> String { //~ ERROR mismatched types
|
||||
0u8;
|
||||
"bla".to_string(); //~ HELP consider removing this semicolon
|
||||
}
|
||||
|
||||
fn g() -> String { //~ ERROR E0269
|
||||
fn g() -> String { //~ ERROR mismatched types
|
||||
"this won't work".to_string();
|
||||
"removeme".to_string(); //~ HELP consider removing this semicolon
|
||||
}
|
||||
|
@ -8,8 +8,8 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
fn _converge() -> ! { //~ ERROR computation may converge
|
||||
42
|
||||
fn _converge() -> ! {
|
||||
42 //~ ERROR mismatched types
|
||||
}
|
||||
|
||||
fn main() { }
|
||||
|
@ -8,7 +8,7 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
fn blah() -> i32 { //~ ERROR not all control paths return a value
|
||||
fn blah() -> i32 { //~ ERROR mismatched types
|
||||
1
|
||||
|
||||
; //~ HELP consider removing this semicolon:
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
// Regression test for #13428
|
||||
|
||||
fn foo() -> String { //~ ERROR not all control paths return a value
|
||||
fn foo() -> String { //~ ERROR mismatched types
|
||||
format!("Hello {}",
|
||||
"world")
|
||||
// Put the trailing semicolon on its own line to test that the
|
||||
@ -18,7 +18,7 @@ fn foo() -> String { //~ ERROR not all control paths return a value
|
||||
; //~ HELP consider removing this semicolon
|
||||
}
|
||||
|
||||
fn bar() -> String { //~ ERROR not all control paths return a value
|
||||
fn bar() -> String { //~ ERROR mismatched types
|
||||
"foobar".to_string()
|
||||
; //~ HELP consider removing this semicolon
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ struct Bob;
|
||||
|
||||
impl<RHS: Scalar> Add <RHS> for Bob {
|
||||
type Output = Bob;
|
||||
fn add(self, rhs : RHS) -> Bob {}
|
||||
fn add(self, rhs : RHS) -> Bob { Bob }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -15,7 +15,7 @@ mod foo {
|
||||
}
|
||||
|
||||
pub trait Baz {
|
||||
fn bar(&self) -> bool {}
|
||||
fn bar(&self) -> bool { true }
|
||||
}
|
||||
impl Baz for Foo {}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ mod a {
|
||||
|
||||
impl Default for A {
|
||||
pub fn default() -> A { //~ ERROR unnecessary visibility qualifier
|
||||
A;
|
||||
A
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,6 @@ pub trait Tr<'a> {
|
||||
}
|
||||
|
||||
pub fn f<'a, T: Tr<'a>>() -> <T as Tr<'a>>::Out {}
|
||||
//~^ ERROR not all control paths return a value
|
||||
//~^ ERROR mismatched types
|
||||
|
||||
pub fn main() {}
|
||||
|
@ -11,7 +11,7 @@
|
||||
// Regression test for issue #5239
|
||||
|
||||
fn main() {
|
||||
let x = |ref x: isize| -> isize { x += 1; };
|
||||
let x = |ref x: isize| { x += 1; };
|
||||
//~^ ERROR E0368
|
||||
//~| NOTE cannot use `+=` on type `&isize`
|
||||
}
|
||||
|
@ -8,11 +8,8 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
fn foo(b: bool) -> Result<bool,String> {
|
||||
Err("bar".to_string());
|
||||
//~^ ERROR unable to infer enough type information about `_` [E0282]
|
||||
//~| NOTE cannot infer type for `_`
|
||||
//~| NOTE type annotations or generic parameter binding
|
||||
fn foo(b: bool) -> Result<bool,String> { //~ ERROR mismatched types
|
||||
Err("bar".to_string()); //~ HELP consider removing this semicolon
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -8,10 +8,9 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// error-pattern: not all control paths return a value
|
||||
|
||||
fn god_exists(a: isize) -> bool { return god_exists(a); }
|
||||
|
||||
fn f(a: isize) -> isize { if god_exists(a) { return 5; }; }
|
||||
//~^ ERROR mismatched types
|
||||
|
||||
fn main() { f(12); }
|
||||
|
@ -13,6 +13,6 @@ use std::vec::Vec;
|
||||
fn main() {
|
||||
let a: Vec<isize> = Vec::new();
|
||||
a.iter().all(|_| -> bool {
|
||||
//~^ ERROR not all control paths return a value
|
||||
//~^ ERROR mismatched types
|
||||
});
|
||||
}
|
||||
|
@ -8,9 +8,7 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// error-pattern: not all control paths return a value
|
||||
|
||||
fn f() -> isize {
|
||||
fn f() -> isize { //~ ERROR mismatched types
|
||||
// Make sure typestate doesn't interpret this match expression as
|
||||
// the function result
|
||||
match true { true => { } _ => {} };
|
||||
|
@ -11,16 +11,16 @@
|
||||
// regression test for #8005
|
||||
|
||||
macro_rules! test { () => { fn foo() -> i32 { 1; } } }
|
||||
//~^ ERROR not all control paths return a value
|
||||
//~^ ERROR mismatched types
|
||||
//~| HELP consider removing this semicolon
|
||||
|
||||
fn no_return() -> i32 {} //~ ERROR not all control paths return a value
|
||||
fn no_return() -> i32 {} //~ ERROR mismatched types
|
||||
|
||||
fn bar(x: u32) -> u32 { //~ ERROR not all control paths return a value
|
||||
fn bar(x: u32) -> u32 { //~ ERROR mismatched types
|
||||
x * 2; //~ HELP consider removing this semicolon
|
||||
}
|
||||
|
||||
fn baz(x: u64) -> u32 { //~ ERROR not all control paths return a value
|
||||
fn baz(x: u64) -> u32 { //~ ERROR mismatched types
|
||||
x * 2;
|
||||
}
|
||||
|
||||
|
@ -10,4 +10,5 @@
|
||||
|
||||
fn main() -> char {
|
||||
//~^ ERROR: main function has wrong type
|
||||
' '
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ trait Foo<Bar, Baz, Quux>
|
||||
{}
|
||||
|
||||
fn foobar<U: Clone, T: Foo<u8, U, u32>>() -> T {
|
||||
|
||||
panic!()
|
||||
}
|
||||
|
||||
#[rustc_on_unimplemented="a collection of type `{Self}` cannot be built from an iterator over elements of type `{A}`"]
|
||||
|
@ -13,7 +13,7 @@ mod m1 {
|
||||
struct Priv;
|
||||
|
||||
impl Pub {
|
||||
pub fn f() -> Priv {} //~ ERROR private type in public interface
|
||||
pub fn f() -> Priv {Priv} //~ ERROR private type in public interface
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ mod m2 {
|
||||
struct Priv;
|
||||
|
||||
impl Pub {
|
||||
pub fn f() -> Priv {} //~ ERROR private type in public interface
|
||||
pub fn f() -> Priv {Priv} //~ ERROR private type in public interface
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#![feature(lang_items, no_core)]
|
||||
#![no_core]
|
||||
|
||||
#[lang="copy"] pub trait Copy { }
|
||||
#[lang="sized"] pub trait Sized { }
|
||||
|
||||
// error-pattern:requires `start` lang_item
|
||||
|
@ -24,7 +24,7 @@ fn diverge_first() {
|
||||
get_u8()); //~ ERROR unreachable expression
|
||||
}
|
||||
fn diverge_second() {
|
||||
call( //~ ERROR unreachable call
|
||||
call( //~ ERROR unreachable expression
|
||||
get_u8(),
|
||||
diverge());
|
||||
}
|
||||
|
@ -8,8 +8,7 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
fn equal<T>(_: &T, _: &T) -> bool where T : Eq {
|
||||
}
|
||||
fn equal<T>(a: &T, b: &T) -> bool where T : Eq { a == b }
|
||||
|
||||
struct Struct;
|
||||
|
||||
|
@ -12,5 +12,6 @@
|
||||
// unless it uses braces.
|
||||
|
||||
fn main() {
|
||||
let x = || -> i32 22; //~ ERROR expected `{`, found `22`
|
||||
let x = || -> i32 22;
|
||||
//~^ ERROR expected one of `!`, `(`, `::`, `<`, or `{`, found `22`
|
||||
}
|
||||
|
@ -18,18 +18,18 @@ extern crate std as std;
|
||||
|
||||
// #4264 fixed-length vector types
|
||||
|
||||
pub fn foo(_: [i32; (3 as usize)]) { }
|
||||
pub fn foo(_: [i32; (3 as usize)]) ({ } as ())
|
||||
|
||||
pub fn bar() {
|
||||
const FOO: usize = ((5 as usize) - (4 as usize) as usize);
|
||||
let _: [(); (FOO as usize)] = ([(() as ())] as [(); 1]);
|
||||
pub fn bar() ({
|
||||
const FOO: usize = ((5 as usize) - (4 as usize) as usize);
|
||||
let _: [(); (FOO as usize)] = ([(() as ())] as [(); 1]);
|
||||
|
||||
let _: [(); (1 as usize)] = ([(() as ())] as [(); 1]);
|
||||
let _: [(); (1 as usize)] = ([(() as ())] as [(); 1]);
|
||||
|
||||
let _ =
|
||||
(((&([(1 as i32), (2 as i32), (3 as i32)] as [i32; 3]) as &[i32; 3])
|
||||
as *const _ as *const [i32; 3]) as *const [i32; (3 as usize)] as
|
||||
*const [i32; 3]);
|
||||
let _ =
|
||||
(((&([(1 as i32), (2 as i32), (3 as i32)] as [i32; 3])
|
||||
as &[i32; 3]) as *const _ as *const [i32; 3]) as
|
||||
*const [i32; (3 as usize)] as *const [i32; 3]);
|
||||
|
||||
|
||||
|
||||
@ -38,58 +38,66 @@ pub fn bar() {
|
||||
|
||||
|
||||
|
||||
(($crate::fmt::format as
|
||||
fn(std::fmt::Arguments<'_>) -> std::string::String {std::fmt::format})(((::std::fmt::Arguments::new_v1
|
||||
as
|
||||
fn(&[&str], &[std::fmt::ArgumentV1<'_>]) -> std::fmt::Arguments<'_> {std::fmt::Arguments<'_>::new_v1})(({
|
||||
static __STATIC_FMTSTR:
|
||||
&'static [&'static str]
|
||||
=
|
||||
(&([("test"
|
||||
as
|
||||
&'static str)]
|
||||
as
|
||||
[&'static str; 1])
|
||||
as
|
||||
&'static [&'static str; 1]);
|
||||
(__STATIC_FMTSTR
|
||||
as
|
||||
&'static [&'static str])
|
||||
}
|
||||
as
|
||||
&[&str]),
|
||||
(&(match (()
|
||||
as
|
||||
())
|
||||
{
|
||||
()
|
||||
=>
|
||||
([]
|
||||
as
|
||||
[std::fmt::ArgumentV1<'_>; 0]),
|
||||
}
|
||||
as
|
||||
[std::fmt::ArgumentV1<'_>; 0])
|
||||
as
|
||||
&[std::fmt::ArgumentV1<'_>; 0]))
|
||||
as
|
||||
std::fmt::Arguments<'_>))
|
||||
as std::string::String);
|
||||
}
|
||||
|
||||
(($crate::fmt::format as
|
||||
fn(std::fmt::Arguments<'_>) -> std::string::String {std::fmt::format})(((::std::fmt::Arguments::new_v1
|
||||
as
|
||||
fn(&[&str], &[std::fmt::ArgumentV1<'_>]) -> std::fmt::Arguments<'_> {std::fmt::Arguments<'_>::new_v1})(({
|
||||
static __STATIC_FMTSTR:
|
||||
&'static [&'static str]
|
||||
=
|
||||
(&([("test"
|
||||
as
|
||||
&'static str)]
|
||||
as
|
||||
[&'static str; 1])
|
||||
as
|
||||
&'static [&'static str; 1]);
|
||||
(__STATIC_FMTSTR
|
||||
as
|
||||
&'static [&'static str])
|
||||
}
|
||||
as
|
||||
&[&str]),
|
||||
(&(match (()
|
||||
as
|
||||
())
|
||||
{
|
||||
()
|
||||
=>
|
||||
([]
|
||||
as
|
||||
[std::fmt::ArgumentV1<'_>; 0]),
|
||||
}
|
||||
as
|
||||
[std::fmt::ArgumentV1<'_>; 0])
|
||||
as
|
||||
&[std::fmt::ArgumentV1<'_>; 0]))
|
||||
as
|
||||
std::fmt::Arguments<'_>))
|
||||
as std::string::String);
|
||||
} as ())
|
||||
pub type Foo = [i32; (3 as usize)];
|
||||
pub struct Bar {
|
||||
pub x: [i32; (3 as usize)],
|
||||
}
|
||||
pub struct TupleBar([i32; (4 as usize)]);
|
||||
pub enum Baz { BazVariant([i32; (5 as usize)]), }
|
||||
pub fn id<T>(x: T) -> T { (x as T) }
|
||||
pub fn use_id() {
|
||||
let _ =
|
||||
((id::<[i32; (3 as usize)]> as
|
||||
fn([i32; 3]) -> [i32; 3] {id::<[i32; 3]>})(([(1 as i32),
|
||||
(2 as i32),
|
||||
(3 as i32)] as
|
||||
[i32; 3])) as
|
||||
[i32; 3]);
|
||||
}
|
||||
fn main() { }
|
||||
pub fn id<T>(x: T) -> T ({ (x as T) } as T)
|
||||
pub fn use_id() ({
|
||||
let _ =
|
||||
((id::<[i32; (3 as usize)]> as
|
||||
fn([i32; 3]) -> [i32; 3] {id::<[i32; 3]>})(([(1
|
||||
as
|
||||
i32),
|
||||
(2
|
||||
as
|
||||
i32),
|
||||
(3
|
||||
as
|
||||
i32)]
|
||||
as
|
||||
[i32; 3]))
|
||||
as [i32; 3]);
|
||||
} as ())
|
||||
fn main() ({ } as ())
|
||||
|
@ -198,14 +198,20 @@ fn _11() {
|
||||
};
|
||||
let _ = #[attr] || #[attr] ();
|
||||
let _ = #[attr] move || #[attr] ();
|
||||
let _ = #[attr] || {
|
||||
#![attr]
|
||||
#[attr]
|
||||
() };
|
||||
let _ = #[attr] move || {
|
||||
#![attr]
|
||||
#[attr]
|
||||
() };
|
||||
let _ =
|
||||
#[attr] ||
|
||||
{
|
||||
#![attr]
|
||||
#[attr]
|
||||
()
|
||||
};
|
||||
let _ =
|
||||
#[attr] move ||
|
||||
{
|
||||
#![attr]
|
||||
#[attr]
|
||||
()
|
||||
};
|
||||
let _ =
|
||||
#[attr] {
|
||||
#![attr]
|
||||
|
@ -2,6 +2,8 @@ digraph block {
|
||||
N0[label="entry"];
|
||||
N1[label="exit"];
|
||||
N2[label="block { }"];
|
||||
N3[label="expr { }"];
|
||||
N0 -> N2;
|
||||
N2 -> N1;
|
||||
N2 -> N3;
|
||||
N3 -> N1;
|
||||
}
|
||||
|
@ -4,8 +4,10 @@ digraph block {
|
||||
N2[label="expr 1"];
|
||||
N3[label="stmt 1;"];
|
||||
N4[label="block { 1; }"];
|
||||
N5[label="expr { 1; }"];
|
||||
N0 -> N2;
|
||||
N2 -> N3;
|
||||
N3 -> N4;
|
||||
N4 -> N1;
|
||||
N4 -> N5;
|
||||
N5 -> N1;
|
||||
}
|
||||
|
@ -4,8 +4,10 @@ digraph block {
|
||||
N2[label="local _x"];
|
||||
N3[label="stmt let _x: isize;"];
|
||||
N4[label="block { let _x: isize; }"];
|
||||
N5[label="expr { let _x: isize; }"];
|
||||
N0 -> N2;
|
||||
N2 -> N3;
|
||||
N3 -> N4;
|
||||
N4 -> N1;
|
||||
N4 -> N5;
|
||||
N5 -> N1;
|
||||
}
|
||||
|
@ -6,10 +6,12 @@ digraph block {
|
||||
N4[label="expr 3 + 4"];
|
||||
N5[label="stmt 3 + 4;"];
|
||||
N6[label="block { 3 + 4; }"];
|
||||
N7[label="expr { 3 + 4; }"];
|
||||
N0 -> N2;
|
||||
N2 -> N3;
|
||||
N3 -> N4;
|
||||
N4 -> N5;
|
||||
N5 -> N6;
|
||||
N6 -> N1;
|
||||
N6 -> N7;
|
||||
N7 -> N1;
|
||||
}
|
||||
|
@ -5,9 +5,11 @@ digraph block {
|
||||
N3[label="local _x"];
|
||||
N4[label="stmt let _x = 4;"];
|
||||
N5[label="block { let _x = 4; }"];
|
||||
N6[label="expr { let _x = 4; }"];
|
||||
N0 -> N2;
|
||||
N2 -> N3;
|
||||
N3 -> N4;
|
||||
N4 -> N5;
|
||||
N5 -> N1;
|
||||
N5 -> N6;
|
||||
N6 -> N1;
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ digraph block {
|
||||
N7[label="pat (_x, _y)"];
|
||||
N8[label="stmt let (_x, _y) = (5, 55);"];
|
||||
N9[label="block { let (_x, _y) = (5, 55); }"];
|
||||
N10[label="expr { let (_x, _y) = (5, 55); }"];
|
||||
N0 -> N2;
|
||||
N2 -> N3;
|
||||
N3 -> N4;
|
||||
@ -17,5 +18,6 @@ digraph block {
|
||||
N6 -> N7;
|
||||
N7 -> N8;
|
||||
N8 -> N9;
|
||||
N9 -> N1;
|
||||
N9 -> N10;
|
||||
N10 -> N1;
|
||||
}
|
||||
|
@ -7,11 +7,13 @@ digraph block {
|
||||
N5[label="pat S6 { val: _x }"];
|
||||
N6[label="stmt let S6 { val: _x } = S6{val: 6,};"];
|
||||
N7[label="block { let S6 { val: _x } = S6{val: 6,}; }"];
|
||||
N8[label="expr { let S6 { val: _x } = S6{val: 6,}; }"];
|
||||
N0 -> N2;
|
||||
N2 -> N3;
|
||||
N3 -> N4;
|
||||
N4 -> N5;
|
||||
N5 -> N6;
|
||||
N6 -> N7;
|
||||
N7 -> N1;
|
||||
N7 -> N8;
|
||||
N8 -> N1;
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ digraph block {
|
||||
N15[label="expr x + y"];
|
||||
N16[label="stmt match [7, 77, 777, 7777] { [x, y, ..] => x + y, };"];
|
||||
N17[label="block { match [7, 77, 777, 7777] { [x, y, ..] => x + y, }; }"];
|
||||
N18[label="expr { match [7, 77, 777, 7777] { [x, y, ..] => x + y, }; }"];
|
||||
N0 -> N2;
|
||||
N2 -> N3;
|
||||
N3 -> N4;
|
||||
@ -33,5 +34,6 @@ digraph block {
|
||||
N15 -> N7;
|
||||
N7 -> N16;
|
||||
N16 -> N17;
|
||||
N17 -> N1;
|
||||
N17 -> N18;
|
||||
N18 -> N1;
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ digraph block {
|
||||
N14[label="block { _y = 888; }"];
|
||||
N15[label="expr if x > 88 { _y = 888; }"];
|
||||
N16[label="block { let x = 8; let _y; if x > 88 { _y = 888; } }"];
|
||||
N17[label="expr { let x = 8; let _y; if x > 88 { _y = 888; } }"];
|
||||
N0 -> N2;
|
||||
N2 -> N3;
|
||||
N3 -> N4;
|
||||
@ -32,5 +33,6 @@ digraph block {
|
||||
N9 -> N15;
|
||||
N14 -> N15;
|
||||
N15 -> N16;
|
||||
N16 -> N1;
|
||||
N16 -> N17;
|
||||
N17 -> N1;
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ digraph block {
|
||||
N22[label="expr { _y = 94 + 95; }"];
|
||||
N23[label="expr if x > 92 { _y = 93; } else { _y = 94 + 95; }"];
|
||||
N24[label="block { let x = 91; let _y; if x > 92 { _y = 93; } else { _y = 94 + 95; } }"];
|
||||
N25[label="expr { let x = 91; let _y; if x > 92 { _y = 93; } else { _y = 94 + 95; } }"];
|
||||
N0 -> N2;
|
||||
N2 -> N3;
|
||||
N3 -> N4;
|
||||
@ -48,5 +49,6 @@ digraph block {
|
||||
N14 -> N23;
|
||||
N22 -> N23;
|
||||
N23 -> N24;
|
||||
N24 -> N1;
|
||||
N24 -> N25;
|
||||
N25 -> N1;
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ digraph block {
|
||||
N13[label="stmt x -= 1;"];
|
||||
N14[label="block { x -= 1; }"];
|
||||
N15[label="block { let mut x = 10; while x > 0 { x -= 1; } }"];
|
||||
N16[label="expr { let mut x = 10; while x > 0 { x -= 1; } }"];
|
||||
N0 -> N2;
|
||||
N2 -> N3;
|
||||
N3 -> N4;
|
||||
@ -30,5 +31,6 @@ digraph block {
|
||||
N13 -> N14;
|
||||
N14 -> N5;
|
||||
N9 -> N15;
|
||||
N15 -> N1;
|
||||
N15 -> N16;
|
||||
N16 -> N1;
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ digraph block {
|
||||
N13[label="expr \"unreachable\""];
|
||||
N14[label="stmt \"unreachable\";"];
|
||||
N15[label="block { let mut _x = 11; loop { _x -= 1; } \"unreachable\"; }"];
|
||||
N16[label="expr { let mut _x = 11; loop { _x -= 1; } \"unreachable\"; }"];
|
||||
N0 -> N2;
|
||||
N2 -> N3;
|
||||
N3 -> N4;
|
||||
@ -29,5 +30,6 @@ digraph block {
|
||||
N12 -> N13;
|
||||
N13 -> N14;
|
||||
N14 -> N15;
|
||||
N15 -> N1;
|
||||
N15 -> N16;
|
||||
N16 -> N1;
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ digraph block {
|
||||
N20[label="expr if x == 2 { break ; \"unreachable\"; }"];
|
||||
N21[label="block { x -= 1; if x == 2 { break ; \"unreachable\"; } }"];
|
||||
N22[label="block { let mut x = 12; loop { x -= 1; if x == 2 { break ; \"unreachable\"; } } }"];
|
||||
N23[label="expr { let mut x = 12; loop { x -= 1; if x == 2 { break ; \"unreachable\"; } } }"];
|
||||
N0 -> N2;
|
||||
N2 -> N3;
|
||||
N3 -> N4;
|
||||
@ -44,5 +45,6 @@ digraph block {
|
||||
N20 -> N21;
|
||||
N21 -> N5;
|
||||
N6 -> N22;
|
||||
N22 -> N1;
|
||||
N22 -> N23;
|
||||
N23 -> N1;
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ digraph block {
|
||||
N22[label="expr _y"];
|
||||
N23[label="expr _y = v + 1"];
|
||||
N24[label="block {\l let x = E13::E13b(13);\l let _y;\l match x { E13::E13a => _y = 1, E13::E13b(v) => _y = v + 1, }\l}\l"];
|
||||
N25[label="expr {\l let x = E13::E13b(13);\l let _y;\l match x { E13::E13a => _y = 1, E13::E13b(v) => _y = v + 1, }\l}\l"];
|
||||
N0 -> N2;
|
||||
N2 -> N3;
|
||||
N3 -> N4;
|
||||
@ -48,5 +49,6 @@ digraph block {
|
||||
N22 -> N23;
|
||||
N23 -> N10;
|
||||
N10 -> N24;
|
||||
N24 -> N1;
|
||||
N24 -> N25;
|
||||
N25 -> N1;
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ digraph block {
|
||||
N13[label="block { return; \"unreachable\"; }"];
|
||||
N14[label="expr if x > 1 { return; \"unreachable\"; }"];
|
||||
N15[label="block { let x = 14; if x > 1 { return; \"unreachable\"; } }"];
|
||||
N16[label="expr { let x = 14; if x > 1 { return; \"unreachable\"; } }"];
|
||||
N0 -> N2;
|
||||
N2 -> N3;
|
||||
N3 -> N4;
|
||||
@ -30,5 +31,6 @@ digraph block {
|
||||
N7 -> N14;
|
||||
N13 -> N14;
|
||||
N14 -> N15;
|
||||
N15 -> N1;
|
||||
N15 -> N16;
|
||||
N16 -> N1;
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ digraph block {
|
||||
N47[label="stmt x -= 5;"];
|
||||
N48[label="block {\l \'inner:\l loop {\l if x == 1 { break \'outer ; \"unreachable\"; }\l if y >= 2 { break ; \"unreachable\"; }\l y -= 3;\l }\l y -= 4;\l x -= 5;\l}\l"];
|
||||
N49[label="block {\l let mut x = 15;\l let mut y = 151;\l \'outer:\l loop {\l \'inner:\l loop {\l if x == 1 { break \'outer ; \"unreachable\"; }\l if y >= 2 { break ; \"unreachable\"; }\l y -= 3;\l }\l y -= 4;\l x -= 5;\l }\l}\l"];
|
||||
N50[label="expr {\l let mut x = 15;\l let mut y = 151;\l \'outer:\l loop {\l \'inner:\l loop {\l if x == 1 { break \'outer ; \"unreachable\"; }\l if y >= 2 { break ; \"unreachable\"; }\l y -= 3;\l }\l y -= 4;\l x -= 5;\l }\l}\l"];
|
||||
N0 -> N2;
|
||||
N2 -> N3;
|
||||
N3 -> N4;
|
||||
@ -99,5 +100,6 @@ digraph block {
|
||||
N47 -> N48;
|
||||
N48 -> N8;
|
||||
N9 -> N49;
|
||||
N49 -> N1;
|
||||
N49 -> N50;
|
||||
N50 -> N1;
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ digraph block {
|
||||
N50[label="expr \"unreachable\""];
|
||||
N51[label="stmt \"unreachable\";"];
|
||||
N52[label="block {\l let mut x = 16;\l let mut y = 16;\l \'outer:\l loop {\l \'inner:\l loop {\l if x == 1 { continue \'outer ; \"unreachable\"; }\l if y >= 1 { break ; \"unreachable\"; }\l y -= 1;\l }\l y -= 1;\l x -= 1;\l }\l \"unreachable\";\l}\l"];
|
||||
N53[label="expr {\l let mut x = 16;\l let mut y = 16;\l \'outer:\l loop {\l \'inner:\l loop {\l if x == 1 { continue \'outer ; \"unreachable\"; }\l if y >= 1 { break ; \"unreachable\"; }\l y -= 1;\l }\l y -= 1;\l x -= 1;\l }\l \"unreachable\";\l}\l"];
|
||||
N0 -> N2;
|
||||
N2 -> N3;
|
||||
N3 -> N4;
|
||||
@ -105,5 +106,6 @@ digraph block {
|
||||
N49 -> N50;
|
||||
N50 -> N51;
|
||||
N51 -> N52;
|
||||
N52 -> N1;
|
||||
N52 -> N53;
|
||||
N53 -> N1;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user