mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-01 15:01:51 +00:00
Make expr_is_lval more robust
Previously it also tried to find out the best way to translate the expression, which could ICE during type-checking. Fixes #23173 Fixes #24322 Fixes #25757
This commit is contained in:
parent
a38e7585fc
commit
59be753544
@ -20,7 +20,6 @@ pub use self::Variance::*;
|
||||
pub use self::AutoAdjustment::*;
|
||||
pub use self::Representability::*;
|
||||
pub use self::AutoRef::*;
|
||||
pub use self::ExprKind::*;
|
||||
pub use self::DtorKind::*;
|
||||
pub use self::ExplicitSelfCategory::*;
|
||||
pub use self::FnOutput::*;
|
||||
@ -87,7 +86,7 @@ use syntax::abi;
|
||||
use syntax::ast::{CrateNum, DefId, ItemImpl, ItemTrait, LOCAL_CRATE};
|
||||
use syntax::ast::{MutImmutable, MutMutable, Name, NamedField, NodeId};
|
||||
use syntax::ast::{StmtExpr, StmtSemi, StructField, UnnamedField, Visibility};
|
||||
use syntax::ast_util::{self, is_local, lit_is_str, local_def};
|
||||
use syntax::ast_util::{self, is_local, local_def};
|
||||
use syntax::attr::{self, AttrMetaMethods, SignedInt, UnsignedInt};
|
||||
use syntax::codemap::Span;
|
||||
use syntax::parse::token::{self, InternedString, special_idents};
|
||||
@ -5106,96 +5105,27 @@ pub fn resolve_expr(tcx: &ctxt, expr: &ast::Expr) -> def::Def {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expr_is_lval(tcx: &ctxt, e: &ast::Expr) -> bool {
|
||||
match expr_kind(tcx, e) {
|
||||
LvalueExpr => true,
|
||||
RvalueDpsExpr | RvalueDatumExpr | RvalueStmtExpr => false
|
||||
}
|
||||
}
|
||||
|
||||
/// We categorize expressions into three kinds. The distinction between
|
||||
/// lvalue/rvalue is fundamental to the language. The distinction between the
|
||||
/// two kinds of rvalues is an artifact of trans which reflects how we will
|
||||
/// generate code for that kind of expression. See trans/expr.rs for more
|
||||
/// information.
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum ExprKind {
|
||||
LvalueExpr,
|
||||
RvalueDpsExpr,
|
||||
RvalueDatumExpr,
|
||||
RvalueStmtExpr
|
||||
}
|
||||
|
||||
pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind {
|
||||
if tcx.method_map.borrow().contains_key(&MethodCall::expr(expr.id)) {
|
||||
// Overloaded operations are generally calls, and hence they are
|
||||
// generated via DPS, but there are a few exceptions:
|
||||
return match expr.node {
|
||||
// `a += b` has a unit result.
|
||||
ast::ExprAssignOp(..) => RvalueStmtExpr,
|
||||
|
||||
// the deref method invoked for `*a` always yields an `&T`
|
||||
ast::ExprUnary(ast::UnDeref, _) => LvalueExpr,
|
||||
|
||||
// the index method invoked for `a[i]` always yields an `&T`
|
||||
ast::ExprIndex(..) => LvalueExpr,
|
||||
|
||||
// in the general case, result could be any type, use DPS
|
||||
_ => RvalueDpsExpr
|
||||
};
|
||||
}
|
||||
|
||||
match expr.node {
|
||||
pub fn expr_is_lval(tcx: &ctxt, expr: &ast::Expr) -> bool {
|
||||
match expr.node {
|
||||
ast::ExprPath(..) => {
|
||||
match resolve_expr(tcx, expr) {
|
||||
def::DefVariant(tid, vid, _) => {
|
||||
let variant_info = enum_variant_with_id(tcx, tid, vid);
|
||||
if !variant_info.args.is_empty() {
|
||||
// N-ary variant.
|
||||
RvalueDatumExpr
|
||||
} else {
|
||||
// Nullary variant.
|
||||
RvalueDpsExpr
|
||||
}
|
||||
// We can't use resolve_expr here, as this needs to run on broken
|
||||
// programs. We don't need to through - associated items are all
|
||||
// rvalues.
|
||||
match tcx.def_map.borrow().get(&expr.id) {
|
||||
Some(&def::PathResolution {
|
||||
base_def: def::DefStatic(..), ..
|
||||
}) | Some(&def::PathResolution {
|
||||
base_def: def::DefUpvar(..), ..
|
||||
}) | Some(&def::PathResolution {
|
||||
base_def: def::DefLocal(..), ..
|
||||
}) => {
|
||||
true
|
||||
}
|
||||
|
||||
def::DefStruct(_) => {
|
||||
match tcx.node_types.borrow().get(&expr.id) {
|
||||
Some(ty) => match ty.sty {
|
||||
TyBareFn(..) => RvalueDatumExpr,
|
||||
_ => RvalueDpsExpr
|
||||
},
|
||||
// See ExprCast below for why types might be missing.
|
||||
None => RvalueDatumExpr
|
||||
}
|
||||
}
|
||||
Some(..) => false,
|
||||
|
||||
// Special case: A unit like struct's constructor must be called without () at the
|
||||
// end (like `UnitStruct`) which means this is an ExprPath to a DefFn. But in case
|
||||
// of unit structs this is should not be interpreted as function pointer but as
|
||||
// call to the constructor.
|
||||
def::DefFn(_, true) => RvalueDpsExpr,
|
||||
|
||||
// Fn pointers are just scalar values.
|
||||
def::DefFn(..) | def::DefMethod(..) => RvalueDatumExpr,
|
||||
|
||||
// Note: there is actually a good case to be made that
|
||||
// DefArg's, particularly those of immediate type, ought to
|
||||
// considered rvalues.
|
||||
def::DefStatic(..) |
|
||||
def::DefUpvar(..) |
|
||||
def::DefLocal(..) => LvalueExpr,
|
||||
|
||||
def::DefConst(..) |
|
||||
def::DefAssociatedConst(..) => RvalueDatumExpr,
|
||||
|
||||
def => {
|
||||
tcx.sess.span_bug(
|
||||
expr.span,
|
||||
&format!("uncategorized def for expr {}: {:?}",
|
||||
expr.id,
|
||||
def));
|
||||
}
|
||||
None => tcx.sess.span_bug(expr.span, &format!(
|
||||
"no def for path {}", expr.id))
|
||||
}
|
||||
}
|
||||
|
||||
@ -5203,7 +5133,7 @@ pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind {
|
||||
ast::ExprField(..) |
|
||||
ast::ExprTupField(..) |
|
||||
ast::ExprIndex(..) => {
|
||||
LvalueExpr
|
||||
true
|
||||
}
|
||||
|
||||
ast::ExprCall(..) |
|
||||
@ -5216,25 +5146,7 @@ pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind {
|
||||
ast::ExprClosure(..) |
|
||||
ast::ExprBlock(..) |
|
||||
ast::ExprRepeat(..) |
|
||||
ast::ExprVec(..) => {
|
||||
RvalueDpsExpr
|
||||
}
|
||||
|
||||
ast::ExprIfLet(..) => {
|
||||
tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
|
||||
}
|
||||
ast::ExprWhileLet(..) => {
|
||||
tcx.sess.span_bug(expr.span, "non-desugared ExprWhileLet");
|
||||
}
|
||||
|
||||
ast::ExprForLoop(..) => {
|
||||
tcx.sess.span_bug(expr.span, "non-desugared ExprForLoop");
|
||||
}
|
||||
|
||||
ast::ExprLit(ref lit) if lit_is_str(&**lit) => {
|
||||
RvalueDpsExpr
|
||||
}
|
||||
|
||||
ast::ExprVec(..) |
|
||||
ast::ExprBreak(..) |
|
||||
ast::ExprAgain(..) |
|
||||
ast::ExprRet(..) |
|
||||
@ -5242,34 +5154,21 @@ pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind {
|
||||
ast::ExprLoop(..) |
|
||||
ast::ExprAssign(..) |
|
||||
ast::ExprInlineAsm(..) |
|
||||
ast::ExprAssignOp(..) => {
|
||||
RvalueStmtExpr
|
||||
}
|
||||
|
||||
ast::ExprLit(_) | // Note: LitStr is carved out above
|
||||
ast::ExprAssignOp(..) |
|
||||
ast::ExprLit(_) |
|
||||
ast::ExprUnary(..) |
|
||||
ast::ExprBox(None, _) |
|
||||
ast::ExprBox(..) |
|
||||
ast::ExprAddrOf(..) |
|
||||
ast::ExprBinary(..) |
|
||||
ast::ExprCast(..) => {
|
||||
RvalueDatumExpr
|
||||
false
|
||||
}
|
||||
|
||||
ast::ExprBox(Some(ref place), _) => {
|
||||
// Special case `Box<T>` for now:
|
||||
let def_id = match tcx.def_map.borrow().get(&place.id) {
|
||||
Some(def) => def.def_id(),
|
||||
None => panic!("no def for place"),
|
||||
};
|
||||
if tcx.lang_items.exchange_heap() == Some(def_id) {
|
||||
RvalueDatumExpr
|
||||
} else {
|
||||
RvalueDpsExpr
|
||||
}
|
||||
}
|
||||
|
||||
ast::ExprParen(ref e) => expr_kind(tcx, &**e),
|
||||
ast::ExprParen(ref e) => expr_is_lval(tcx, e),
|
||||
|
||||
ast::ExprIfLet(..) |
|
||||
ast::ExprWhileLet(..) |
|
||||
ast::ExprForLoop(..) |
|
||||
ast::ExprMac(..) => {
|
||||
tcx.sess.span_bug(
|
||||
expr.span,
|
||||
|
@ -188,15 +188,15 @@ pub fn trans_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||
false);
|
||||
bcx.fcx.push_ast_cleanup_scope(cleanup_debug_loc);
|
||||
|
||||
let kind = ty::expr_kind(bcx.tcx(), expr);
|
||||
let kind = expr_kind(bcx.tcx(), expr);
|
||||
bcx = match kind {
|
||||
ty::LvalueExpr | ty::RvalueDatumExpr => {
|
||||
ExprKind::Lvalue | ExprKind::RvalueDatum => {
|
||||
trans_unadjusted(bcx, expr).store_to_dest(dest, expr.id)
|
||||
}
|
||||
ty::RvalueDpsExpr => {
|
||||
ExprKind::RvalueDps => {
|
||||
trans_rvalue_dps_unadjusted(bcx, expr, dest)
|
||||
}
|
||||
ty::RvalueStmtExpr => {
|
||||
ExprKind::RvalueStmt => {
|
||||
trans_rvalue_stmt_unadjusted(bcx, expr)
|
||||
}
|
||||
};
|
||||
@ -582,8 +582,8 @@ fn trans_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||
|
||||
debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);
|
||||
|
||||
return match ty::expr_kind(bcx.tcx(), expr) {
|
||||
ty::LvalueExpr | ty::RvalueDatumExpr => {
|
||||
return match expr_kind(bcx.tcx(), expr) {
|
||||
ExprKind::Lvalue | ExprKind::RvalueDatum => {
|
||||
let datum = unpack_datum!(bcx, {
|
||||
trans_datum_unadjusted(bcx, expr)
|
||||
});
|
||||
@ -591,12 +591,12 @@ fn trans_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||
DatumBlock {bcx: bcx, datum: datum}
|
||||
}
|
||||
|
||||
ty::RvalueStmtExpr => {
|
||||
ExprKind::RvalueStmt => {
|
||||
bcx = trans_rvalue_stmt_unadjusted(bcx, expr);
|
||||
nil(bcx, expr_ty(bcx, expr))
|
||||
}
|
||||
|
||||
ty::RvalueDpsExpr => {
|
||||
ExprKind::RvalueDps => {
|
||||
let ty = expr_ty(bcx, expr);
|
||||
if type_is_zero_size(bcx.ccx(), ty) {
|
||||
bcx = trans_rvalue_dps_unadjusted(bcx, expr, Ignore);
|
||||
@ -1531,11 +1531,13 @@ pub fn trans_adt<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
|
||||
// Second, trans the base to the dest.
|
||||
assert_eq!(discr, 0);
|
||||
|
||||
match ty::expr_kind(bcx.tcx(), &*base.expr) {
|
||||
ty::RvalueDpsExpr | ty::RvalueDatumExpr if !bcx.fcx.type_needs_drop(ty) => {
|
||||
match expr_kind(bcx.tcx(), &*base.expr) {
|
||||
ExprKind::RvalueDps | ExprKind::RvalueDatum if !bcx.fcx.type_needs_drop(ty) => {
|
||||
bcx = trans_into(bcx, &*base.expr, SaveIn(addr));
|
||||
},
|
||||
ty::RvalueStmtExpr => bcx.tcx().sess.bug("unexpected expr kind for struct base expr"),
|
||||
ExprKind::RvalueStmt => {
|
||||
bcx.tcx().sess.bug("unexpected expr kind for struct base expr")
|
||||
}
|
||||
_ => {
|
||||
let base_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, &*base.expr, "base"));
|
||||
for &(i, t) in &base.fields {
|
||||
@ -2592,3 +2594,155 @@ fn with_overflow_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, oop: OverflowOp, info
|
||||
(bcx, res)
|
||||
}
|
||||
}
|
||||
|
||||
/// We categorize expressions into three kinds. The distinction between
|
||||
/// lvalue/rvalue is fundamental to the language. The distinction between the
|
||||
/// two kinds of rvalues is an artifact of trans which reflects how we will
|
||||
/// generate code for that kind of expression. See trans/expr.rs for more
|
||||
/// information.
|
||||
#[derive(Copy, Clone)]
|
||||
enum ExprKind {
|
||||
Lvalue,
|
||||
RvalueDps,
|
||||
RvalueDatum,
|
||||
RvalueStmt
|
||||
}
|
||||
|
||||
fn expr_kind(tcx: &ty::ctxt, expr: &ast::Expr) -> ExprKind {
|
||||
if tcx.method_map.borrow().contains_key(&MethodCall::expr(expr.id)) {
|
||||
// Overloaded operations are generally calls, and hence they are
|
||||
// generated via DPS, but there are a few exceptions:
|
||||
return match expr.node {
|
||||
// `a += b` has a unit result.
|
||||
ast::ExprAssignOp(..) => ExprKind::RvalueStmt,
|
||||
|
||||
// the deref method invoked for `*a` always yields an `&T`
|
||||
ast::ExprUnary(ast::UnDeref, _) => ExprKind::Lvalue,
|
||||
|
||||
// the index method invoked for `a[i]` always yields an `&T`
|
||||
ast::ExprIndex(..) => ExprKind::Lvalue,
|
||||
|
||||
// in the general case, result could be any type, use DPS
|
||||
_ => ExprKind::RvalueDps
|
||||
};
|
||||
}
|
||||
|
||||
match expr.node {
|
||||
ast::ExprPath(..) => {
|
||||
match ty::resolve_expr(tcx, expr) {
|
||||
def::DefStruct(_) | def::DefVariant(..) => {
|
||||
if let ty::TyBareFn(..) = ty::node_id_to_type(tcx, expr.id).sty {
|
||||
// ctor function
|
||||
ExprKind::RvalueDatum
|
||||
} else {
|
||||
ExprKind::RvalueDps
|
||||
}
|
||||
}
|
||||
|
||||
// Special case: A unit like struct's constructor must be called without () at the
|
||||
// end (like `UnitStruct`) which means this is an ExprPath to a DefFn. But in case
|
||||
// of unit structs this is should not be interpreted as function pointer but as
|
||||
// call to the constructor.
|
||||
def::DefFn(_, true) => ExprKind::RvalueDps,
|
||||
|
||||
// Fn pointers are just scalar values.
|
||||
def::DefFn(..) | def::DefMethod(..) => ExprKind::RvalueDatum,
|
||||
|
||||
// Note: there is actually a good case to be made that
|
||||
// DefArg's, particularly those of immediate type, ought to
|
||||
// considered rvalues.
|
||||
def::DefStatic(..) |
|
||||
def::DefUpvar(..) |
|
||||
def::DefLocal(..) => ExprKind::Lvalue,
|
||||
|
||||
def::DefConst(..) |
|
||||
def::DefAssociatedConst(..) => ExprKind::RvalueDatum,
|
||||
|
||||
def => {
|
||||
tcx.sess.span_bug(
|
||||
expr.span,
|
||||
&format!("uncategorized def for expr {}: {:?}",
|
||||
expr.id,
|
||||
def));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ast::ExprUnary(ast::UnDeref, _) |
|
||||
ast::ExprField(..) |
|
||||
ast::ExprTupField(..) |
|
||||
ast::ExprIndex(..) => {
|
||||
ExprKind::Lvalue
|
||||
}
|
||||
|
||||
ast::ExprCall(..) |
|
||||
ast::ExprMethodCall(..) |
|
||||
ast::ExprStruct(..) |
|
||||
ast::ExprRange(..) |
|
||||
ast::ExprTup(..) |
|
||||
ast::ExprIf(..) |
|
||||
ast::ExprMatch(..) |
|
||||
ast::ExprClosure(..) |
|
||||
ast::ExprBlock(..) |
|
||||
ast::ExprRepeat(..) |
|
||||
ast::ExprVec(..) => {
|
||||
ExprKind::RvalueDps
|
||||
}
|
||||
|
||||
ast::ExprIfLet(..) => {
|
||||
tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
|
||||
}
|
||||
ast::ExprWhileLet(..) => {
|
||||
tcx.sess.span_bug(expr.span, "non-desugared ExprWhileLet");
|
||||
}
|
||||
|
||||
ast::ExprForLoop(..) => {
|
||||
tcx.sess.span_bug(expr.span, "non-desugared ExprForLoop");
|
||||
}
|
||||
|
||||
ast::ExprLit(ref lit) if ast_util::lit_is_str(&**lit) => {
|
||||
ExprKind::RvalueDps
|
||||
}
|
||||
|
||||
ast::ExprBreak(..) |
|
||||
ast::ExprAgain(..) |
|
||||
ast::ExprRet(..) |
|
||||
ast::ExprWhile(..) |
|
||||
ast::ExprLoop(..) |
|
||||
ast::ExprAssign(..) |
|
||||
ast::ExprInlineAsm(..) |
|
||||
ast::ExprAssignOp(..) => {
|
||||
ExprKind::RvalueStmt
|
||||
}
|
||||
|
||||
ast::ExprLit(_) | // Note: LitStr is carved out above
|
||||
ast::ExprUnary(..) |
|
||||
ast::ExprBox(None, _) |
|
||||
ast::ExprAddrOf(..) |
|
||||
ast::ExprBinary(..) |
|
||||
ast::ExprCast(..) => {
|
||||
ExprKind::RvalueDatum
|
||||
}
|
||||
|
||||
ast::ExprBox(Some(ref place), _) => {
|
||||
// Special case `Box<T>` for now:
|
||||
let def_id = match tcx.def_map.borrow().get(&place.id) {
|
||||
Some(def) => def.def_id(),
|
||||
None => panic!("no def for place"),
|
||||
};
|
||||
if tcx.lang_items.exchange_heap() == Some(def_id) {
|
||||
ExprKind::RvalueDatum
|
||||
} else {
|
||||
ExprKind::RvalueDps
|
||||
}
|
||||
}
|
||||
|
||||
ast::ExprParen(ref e) => expr_kind(tcx, &**e),
|
||||
|
||||
ast::ExprMac(..) => {
|
||||
tcx.sess.span_bug(
|
||||
expr.span,
|
||||
"macro expression remains after expansion");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
17
src/test/compile-fail/issue-23173.rs
Normal file
17
src/test/compile-fail/issue-23173.rs
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
enum Token { LeftParen, RightParen, Plus, Minus, /* etc */ }
|
||||
|
||||
fn use_token(token: &Token) { unimplemented!() }
|
||||
|
||||
fn main() {
|
||||
use_token(&Token::Homura); //~ ERROR no associated item named
|
||||
}
|
19
src/test/compile-fail/issue-24322.rs
Normal file
19
src/test/compile-fail/issue-24322.rs
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
struct B;
|
||||
|
||||
impl B {
|
||||
fn func(&self) -> u32 { 42 }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x: &fn(&B) -> u32 = &B::func; //~ ERROR mismatched types
|
||||
}
|
27
src/test/run-pass/issue-25757.rs
Normal file
27
src/test/run-pass/issue-25757.rs
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
struct Foo {
|
||||
a: u32
|
||||
}
|
||||
|
||||
impl Foo {
|
||||
fn x(&mut self) {
|
||||
self.a = 5;
|
||||
}
|
||||
}
|
||||
|
||||
const FUNC: &'static Fn(&mut Foo) -> () = &Foo::x;
|
||||
|
||||
fn main() {
|
||||
let mut foo = Foo { a: 137 };
|
||||
FUNC(&mut foo); //~ ERROR bad
|
||||
assert_eq!(foo.a, 5);
|
||||
}
|
Loading…
Reference in New Issue
Block a user