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:
Eduard-Mihai Burtescu 2016-11-10 03:46:28 +02:00 committed by GitHub
commit 368281a110
109 changed files with 785 additions and 836 deletions

View File

@ -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() -> ! {

View File

@ -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,

View File

@ -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 {

View File

@ -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; }`)

View File

@ -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 {

View File

@ -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)
})
}

View File

@ -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,

View File

@ -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);
}

View File

@ -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

View File

@ -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.

View File

@ -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,

View File

@ -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) { }

View File

@ -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)
}
}

View File

@ -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) |

View File

@ -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, ..) =>

View File

@ -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));

View File

@ -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")
}

View File

@ -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, &param_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");
}
}
}

View File

@ -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(_) => {}

View File

@ -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) {

View File

@ -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

View File

@ -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);

View File

@ -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 {

View File

@ -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]) {

View File

@ -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>

View File

@ -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,

View File

@ -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 {

View File

@ -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();

View File

@ -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,

View File

@ -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);

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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> {

View File

@ -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

View File

@ -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);

View File

@ -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) {

View File

@ -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),

View File

@ -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

View File

@ -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,

View File

@ -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, _) => {

View File

@ -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,

View File

@ -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);

View File

@ -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;

View File

@ -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()
}
_ => {

View File

@ -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);

View File

@ -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)
{

View File

@ -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;

View File

@ -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);

View File

@ -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>),

View File

@ -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 {

View File

@ -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) {

View File

@ -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)),

View File

@ -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())?
}
};

View File

@ -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

View File

@ -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;

View File

@ -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)
}

View File

@ -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)"),
};

View File

@ -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"),

View File

@ -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

View File

@ -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
}

View File

@ -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() { }

View File

@ -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:

View File

@ -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
}

View File

@ -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() {

View File

@ -15,7 +15,7 @@ mod foo {
}
pub trait Baz {
fn bar(&self) -> bool {}
fn bar(&self) -> bool { true }
}
impl Baz for Foo {}
}

View File

@ -13,7 +13,7 @@ mod a {
impl Default for A {
pub fn default() -> A { //~ ERROR unnecessary visibility qualifier
A;
A
}
}
}

View File

@ -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() {}

View File

@ -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`
}

View File

@ -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() {

View File

@ -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); }

View File

@ -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
});
}

View File

@ -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 => { } _ => {} };

View File

@ -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;
}

View File

@ -10,4 +10,5 @@
fn main() -> char {
//~^ ERROR: main function has wrong type
' '
}

View File

@ -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}`"]

View File

@ -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
}
}

View File

@ -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

View File

@ -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());
}

View File

@ -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;

View File

@ -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`
}

View File

@ -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 ())

View File

@ -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]

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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