From a8efd31f2b97a043d73db2131dddfedd65485d50 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sat, 23 Nov 2019 14:15:49 +0000 Subject: [PATCH] Add raw address of expressions to the AST and HIR --- src/librustc/hir/intravisit.rs | 2 +- src/librustc/hir/lowering/expr.rs | 10 +- src/librustc/hir/mod.rs | 38 +++-- src/librustc/hir/print.rs | 35 ++--- src/librustc/middle/expr_use_visitor.rs | 2 +- src/librustc/middle/region.rs | 4 +- src/librustc_error_codes/error_codes.rs | 1 + src/librustc_error_codes/error_codes/E0745.md | 20 +++ src/librustc_mir/hair/cx/expr.rs | 131 +++++++++++------- src/librustc_parse/parser/diagnostics.rs | 2 +- src/librustc_parse/parser/expr.rs | 25 +++- src/librustc_passes/liveness.rs | 2 +- src/librustc_typeck/check/demand.rs | 6 +- src/librustc_typeck/check/expr.rs | 82 ++++++++--- src/librustc_typeck/check/op.rs | 2 +- src/librustc_typeck/check/regionck.rs | 2 +- src/libsyntax/ast.rs | 21 ++- src/libsyntax/mut_visit.rs | 2 +- src/libsyntax/print/pprust.rs | 37 ++--- src/libsyntax/visit.rs | 2 +- src/libsyntax_expand/build.rs | 2 +- src/test/ui/raw-ref-op/raw-ref-temp-deref.rs | 19 +++ 22 files changed, 308 insertions(+), 139 deletions(-) create mode 100644 src/librustc_error_codes/error_codes/E0745.md create mode 100644 src/test/ui/raw-ref-op/raw-ref-temp-deref.rs diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index 91c19e269a7..a4557a0776c 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -1024,7 +1024,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) { visitor.visit_expr(left_expression); visitor.visit_expr(right_expression) } - ExprKind::AddrOf(_, ref subexpression) | ExprKind::Unary(_, ref subexpression) => { + ExprKind::AddrOf(_, _, ref subexpression) | ExprKind::Unary(_, ref subexpression) => { visitor.visit_expr(subexpression) } ExprKind::Cast(ref subexpression, ref typ) | ExprKind::Type(ref subexpression, ref typ) => { diff --git a/src/librustc/hir/lowering/expr.rs b/src/librustc/hir/lowering/expr.rs index 929dce7aa0f..f8465baeb13 100644 --- a/src/librustc/hir/lowering/expr.rs +++ b/src/librustc/hir/lowering/expr.rs @@ -65,9 +65,9 @@ impl LoweringContext<'_> { let expr = P(self.lower_expr(expr)); hir::ExprKind::Type(expr, self.lower_ty(ty, ImplTraitContext::disallowed())) } - ExprKind::AddrOf(m, ref ohs) => { + ExprKind::AddrOf(k, m, ref ohs) => { let ohs = P(self.lower_expr(ohs)); - hir::ExprKind::AddrOf(m, ohs) + hir::ExprKind::AddrOf(k, m, ohs) } ExprKind::Let(ref pat, ref scrutinee) => self.lower_expr_let(e.span, pat, scrutinee), ExprKind::If(ref cond, ref then, ref else_opt) => { @@ -1339,7 +1339,11 @@ impl LoweringContext<'_> { } fn expr_mut_addr_of(&mut self, span: Span, e: P) -> hir::Expr { - self.expr(span, hir::ExprKind::AddrOf(hir::Mutability::Mutable, e), ThinVec::new()) + self.expr( + span, + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mutable, e), + ThinVec::new(), + ) } fn expr_unit(&mut self, sp: Span) -> hir::Expr { diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 64d22ae9435..66bb3a8d883 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -21,7 +21,8 @@ use syntax_pos::{Span, DUMMY_SP, MultiSpan}; use syntax::source_map::Spanned; use syntax::ast::{self, CrateSugar, Ident, Name, NodeId, AsmDialect}; use syntax::ast::{Attribute, Label, LitKind, StrStyle, FloatTy, IntTy, UintTy}; -pub use syntax::ast::{Mutability, Constness, Unsafety, Movability, CaptureBy, IsAuto, ImplPolarity}; +pub use syntax::ast::{Mutability, Constness, Unsafety, Movability, CaptureBy}; +pub use syntax::ast::{IsAuto, ImplPolarity, BorrowKind}; use syntax::attr::{InlineAttr, OptimizeAttr}; use syntax::symbol::{Symbol, kw}; use syntax::tokenstream::TokenStream; @@ -1493,8 +1494,20 @@ impl Expr { } } - pub fn is_place_expr(&self) -> bool { - match self.kind { + // Whether this looks like a place expr, without checking for deref + // adjustments. + // This will return `true` in some potentially surprising cases such as + // `CONSTANT.field`. + pub fn is_syntactic_place_expr(&self) -> bool { + self.is_place_expr(|_| true) + } + + // Whether this is a place expression. + // `allow_projections_from` should return `true` if indexing a field or + // index expression based on the given expression should be considered a + // place expression. + pub fn is_place_expr(&self, mut allow_projections_from: impl FnMut(&Self) -> bool) -> bool { + match self.kind { ExprKind::Path(QPath::Resolved(_, ref path)) => { match path.res { Res::Local(..) @@ -1504,14 +1517,19 @@ impl Expr { } } + // Type ascription inherits its place expression kind from its + // operand. See: + // https://github.com/rust-lang/rfcs/blob/master/text/0803-type-ascription.md#type-ascription-and-temporaries ExprKind::Type(ref e, _) => { - e.is_place_expr() + e.is_place_expr(allow_projections_from) } - ExprKind::Unary(UnDeref, _) | - ExprKind::Field(..) | - ExprKind::Index(..) => { - true + ExprKind::Unary(UnDeref, _) => true, + + ExprKind::Field(ref base, _) | + ExprKind::Index(ref base, _) => { + allow_projections_from(base) + || base.is_place_expr(allow_projections_from) } // Partially qualified paths in expressions can only legally @@ -1646,8 +1664,8 @@ pub enum ExprKind { /// Path to a definition, possibly containing lifetime or type parameters. Path(QPath), - /// A referencing operation (i.e., `&a` or `&mut a`). - AddrOf(Mutability, P), + /// A referencing operation (i.e., `&a`, `&mut a`, `&raw const a`, or `&raw mut a`). + AddrOf(BorrowKind, Mutability, P), /// A `break`, with an optional label to break. Break(Destination, Option>), /// A `continue`, with an optional label. diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index 4cbe0e80991..a069331582e 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -294,16 +294,12 @@ impl<'a> State<'a> { } hir::TyKind::Ptr(ref mt) => { self.s.word("*"); - match mt.mutbl { - hir::Mutability::Mutable => self.word_nbsp("mut"), - hir::Mutability::Immutable => self.word_nbsp("const"), - } - self.print_type(&mt.ty); + self.print_mt(mt, true); } hir::TyKind::Rptr(ref lifetime, ref mt) => { self.s.word("&"); self.print_opt_lifetime(lifetime); - self.print_mt(mt); + self.print_mt(mt, false); } hir::TyKind::Never => { self.s.word("!"); @@ -1178,11 +1174,18 @@ impl<'a> State<'a> { } fn print_expr_addr_of(&mut self, + kind: hir::BorrowKind, mutability: hir::Mutability, expr: &hir::Expr) - { + { self.s.word("&"); - self.print_mutability(mutability); + match kind { + hir::BorrowKind::Ref => self.print_mutability(mutability, false), + hir::BorrowKind::Raw => { + self.word_nbsp("raw"); + self.print_mutability(mutability, true); + } + } self.print_expr_maybe_paren(expr, parser::PREC_PREFIX) } @@ -1225,8 +1228,8 @@ impl<'a> State<'a> { hir::ExprKind::Unary(op, ref expr) => { self.print_expr_unary(op, &expr); } - hir::ExprKind::AddrOf(m, ref expr) => { - self.print_expr_addr_of(m, &expr); + hir::ExprKind::AddrOf(k, m, ref expr) => { + self.print_expr_addr_of(k, m, &expr); } hir::ExprKind::Lit(ref lit) => { self.print_literal(&lit); @@ -1629,11 +1632,11 @@ impl<'a> State<'a> { match binding_mode { hir::BindingAnnotation::Ref => { self.word_nbsp("ref"); - self.print_mutability(hir::Mutability::Immutable); + self.print_mutability(hir::Mutability::Immutable, false); } hir::BindingAnnotation::RefMut => { self.word_nbsp("ref"); - self.print_mutability(hir::Mutability::Mutable); + self.print_mutability(hir::Mutability::Mutable, false); } hir::BindingAnnotation::Unannotated => {} hir::BindingAnnotation::Mutable => { @@ -2060,15 +2063,15 @@ impl<'a> State<'a> { } } - pub fn print_mutability(&mut self, mutbl: hir::Mutability) { + pub fn print_mutability(&mut self, mutbl: hir::Mutability, print_const: bool) { match mutbl { hir::Mutability::Mutable => self.word_nbsp("mut"), - hir::Mutability::Immutable => {}, + hir::Mutability::Immutable => if print_const { self.word_nbsp("const") }, } } - pub fn print_mt(&mut self, mt: &hir::MutTy) { - self.print_mutability(mt.mutbl); + pub fn print_mt(&mut self, mt: &hir::MutTy, print_const: bool) { + self.print_mutability(mt.mutbl, print_const); self.print_type(&mt.ty) } diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 4571f551aa4..00bddf50c29 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -276,7 +276,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { self.consume_exprs(exprs); } - hir::ExprKind::AddrOf(m, ref base) => { // &base + hir::ExprKind::AddrOf(_, m, ref base) => { // &base // make sure that the thing we are pointing out stays valid // for the lifetime `scope_r` of the resulting ptr: let bk = ty::BorrowKind::from_mutbl(m); diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 9ff205228a5..aa6f2839828 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -1241,7 +1241,7 @@ fn resolve_local<'tcx>( blk_id: Option, ) { match expr.kind { - hir::ExprKind::AddrOf(_, ref subexpr) => { + hir::ExprKind::AddrOf(_, _, ref subexpr) => { record_rvalue_scope_if_borrow_expr(visitor, &subexpr, blk_id); record_rvalue_scope(visitor, &subexpr, blk_id); } @@ -1301,7 +1301,7 @@ fn resolve_local<'tcx>( visitor.scope_tree.record_rvalue_scope(expr.hir_id.local_id, blk_scope); match expr.kind { - hir::ExprKind::AddrOf(_, ref subexpr) | + hir::ExprKind::AddrOf(_, _, ref subexpr) | hir::ExprKind::Unary(hir::UnDeref, ref subexpr) | hir::ExprKind::Field(ref subexpr, _) | hir::ExprKind::Index(ref subexpr, _) => { diff --git a/src/librustc_error_codes/error_codes.rs b/src/librustc_error_codes/error_codes.rs index b11fe33880c..709ccce517a 100644 --- a/src/librustc_error_codes/error_codes.rs +++ b/src/librustc_error_codes/error_codes.rs @@ -409,6 +409,7 @@ E0741: include_str!("./error_codes/E0741.md"), E0742: include_str!("./error_codes/E0742.md"), E0743: include_str!("./error_codes/E0743.md"), E0744: include_str!("./error_codes/E0744.md"), +E0745: include_str!("./error_codes/E0745.md"), ; // E0006, // merged with E0005 // E0008, // cannot bind by-move into a pattern guard diff --git a/src/librustc_error_codes/error_codes/E0745.md b/src/librustc_error_codes/error_codes/E0745.md new file mode 100644 index 00000000000..7c478a1e0c8 --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0745.md @@ -0,0 +1,20 @@ +Cannot take address of temporary value. + +Erroneous code example: + +```compile_fail,E0745 +# #![feature(raw_ref_op)] +fn temp_address() { + let ptr = &raw const 2; // ERROR +} +``` + +To avoid the error, first bind the temporary to a named local variable. + +```ignore +# #![feature(raw_ref_op)] +fn temp_address() { + let val = 2; + let ptr = &raw const val; +} +``` diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 694a3fd04ee..afc4e461c0d 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -137,55 +137,8 @@ fn apply_adjustment<'a, 'tcx>( arg: expr.to_ref(), } } - Adjust::Borrow(AutoBorrow::RawPtr(m)) => { - // Convert this to a suitable `&foo` and - // then an unsafe coercion. - expr = Expr { - temp_lifetime, - ty: cx.tcx.mk_ref(cx.tcx.lifetimes.re_erased, - ty::TypeAndMut { - ty: expr.ty, - mutbl: m, - }), - span, - kind: ExprKind::Borrow { - borrow_kind: m.to_borrow_kind(), - arg: expr.to_ref(), - }, - }; - let cast_expr = Expr { - temp_lifetime, - ty: adjustment.target, - span, - kind: ExprKind::Cast { source: expr.to_ref() } - }; - - // To ensure that both implicit and explicit coercions are - // handled the same way, we insert an extra layer of indirection here. - // For explicit casts (e.g., 'foo as *const T'), the source of the 'Use' - // will be an ExprKind::Hair with the appropriate cast expression. Here, - // we make our Use source the generated Cast from the original coercion. - // - // In both cases, this outer 'Use' ensures that the inner 'Cast' is handled by - // as_operand, not by as_rvalue - causing the cast result to be stored in a temporary. - // Ordinary, this is identical to using the cast directly as an rvalue. However, if the - // source of the cast was previously borrowed as mutable, storing the cast in a - // temporary gives the source a chance to expire before the cast is used. For - // structs with a self-referential *mut ptr, this allows assignment to work as - // expected. - // - // For example, consider the type 'struct Foo { field: *mut Foo }', - // The method 'fn bar(&mut self) { self.field = self }' - // triggers a coercion from '&mut self' to '*mut self'. In order - // for the assignment to be valid, the implicit borrow - // of 'self' involved in the coercion needs to end before the local - // containing the '*mut T' is assigned to 'self.field' - otherwise, - // we end up trying to assign to 'self.field' while we have another mutable borrow - // active. - // - // We only need to worry about this kind of thing for coercions from refs to ptrs, - // since they get rid of a borrow implicitly. - ExprKind::Use { source: cast_expr.to_ref() } + Adjust::Borrow(AutoBorrow::RawPtr(mutbl)) => { + raw_ref_shim(cx, expr.to_ref(), adjustment.target, mutbl, span, temp_lifetime) } }; @@ -302,13 +255,26 @@ fn make_mirror_unadjusted<'a, 'tcx>( } } - hir::ExprKind::AddrOf(mutbl, ref expr) => { + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mutbl, ref arg) => { ExprKind::Borrow { borrow_kind: mutbl.to_borrow_kind(), - arg: expr.to_ref(), + arg: arg.to_ref(), } } + hir::ExprKind::AddrOf(hir::BorrowKind::Raw, mutbl, ref arg) => { + cx.tcx.sess + .struct_span_err( + expr.span, + "raw borrows are not yet implemented" + ) + .note("for more information, see https://github.com/rust-lang/rust/issues/64490") + .emit(); + + // Lower to an approximation to avoid further errors. + raw_ref_shim(cx, arg.to_ref(), expr_ty, mutbl, expr.span, temp_lifetime) + } + hir::ExprKind::Block(ref blk, _) => ExprKind::Block { body: &blk }, hir::ExprKind::Assign(ref lhs, ref rhs) => { @@ -742,7 +708,7 @@ fn make_mirror_unadjusted<'a, 'tcx>( let user_provided_types = cx.tables.user_provided_types(); let user_ty = user_provided_types.get(ty.hir_id).map(|u_ty| *u_ty); debug!("make_mirror_unadjusted: (type) user_ty={:?}", user_ty); - if source.is_place_expr() { + if source.is_syntactic_place_expr() { ExprKind::PlaceTypeAscription { source: source.to_ref(), user_ty, @@ -1123,6 +1089,67 @@ fn convert_var( } +/// Fake `&raw [mut|const] expr` using a borrow and a cast until `AddressOf` +/// exists in MIR. +fn raw_ref_shim<'tcx>( + cx: &mut Cx<'_, 'tcx>, + arg: ExprRef<'tcx>, + ty: Ty<'tcx>, + mutbl: hir::Mutability, + span: Span, + temp_lifetime: Option, +) -> ExprKind<'tcx> { + let arg_tm = if let ty::RawPtr(type_mutbl) = ty.kind { + type_mutbl + } else { + bug!("raw_ref_shim called with non-raw pointer type"); + }; + // Convert this to a suitable `&foo` and + // then an unsafe coercion. + let borrow_expr = Expr { + temp_lifetime, + ty: cx.tcx.mk_ref(cx.tcx.lifetimes.re_erased, arg_tm), + span, + kind: ExprKind::Borrow { + borrow_kind: mutbl.to_borrow_kind(), + arg, + }, + }; + let cast_expr = Expr { + temp_lifetime, + ty, + span, + kind: ExprKind::Cast { source: borrow_expr.to_ref() } + }; + + // To ensure that both implicit and explicit coercions are + // handled the same way, we insert an extra layer of indirection here. + // For explicit casts (e.g., 'foo as *const T'), the source of the 'Use' + // will be an ExprKind::Hair with the appropriate cast expression. Here, + // we make our Use source the generated Cast from the original coercion. + // + // In both cases, this outer 'Use' ensures that the inner 'Cast' is handled by + // as_operand, not by as_rvalue - causing the cast result to be stored in a temporary. + // Ordinary, this is identical to using the cast directly as an rvalue. However, if the + // source of the cast was previously borrowed as mutable, storing the cast in a + // temporary gives the source a chance to expire before the cast is used. For + // structs with a self-referential *mut ptr, this allows assignment to work as + // expected. + // + // For example, consider the type 'struct Foo { field: *mut Foo }', + // The method 'fn bar(&mut self) { self.field = self }' + // triggers a coercion from '&mut self' to '*mut self'. In order + // for the assignment to be valid, the implicit borrow + // of 'self' involved in the coercion needs to end before the local + // containing the '*mut T' is assigned to 'self.field' - otherwise, + // we end up trying to assign to 'self.field' while we have another mutable borrow + // active. + // + // We only need to worry about this kind of thing for coercions from refs to ptrs, + // since they get rid of a borrow implicitly. + ExprKind::Use { source: cast_expr.to_ref() } +} + fn bin_op(op: hir::BinOpKind) -> BinOp { match op { hir::BinOpKind::Add => BinOp::Add, diff --git a/src/librustc_parse/parser/diagnostics.rs b/src/librustc_parse/parser/diagnostics.rs index eab35a86c69..8a79a733c30 100644 --- a/src/librustc_parse/parser/diagnostics.rs +++ b/src/librustc_parse/parser/diagnostics.rs @@ -726,7 +726,7 @@ impl<'a> Parser<'a> { let sum_with_parens = pprust::to_string(|s| { s.s.word("&"); s.print_opt_lifetime(lifetime); - s.print_mutability(mut_ty.mutbl); + s.print_mutability(mut_ty.mutbl, false); s.popen(); s.print_type(&mut_ty.ty); s.print_type_bounds(" +", &bounds); diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index a56a7bf1802..81442143f53 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -442,11 +442,7 @@ impl<'a> Parser<'a> { (lo.to(span), self.mk_unary(UnOp::Deref, e)) } token::BinOp(token::And) | token::AndAnd => { - self.expect_and()?; - let m = self.parse_mutability(); - let e = self.parse_prefix_expr(None); - let (span, e) = self.interpolated_or_expr_span(e)?; - (lo.to(span), ExprKind::AddrOf(m, e)) + self.parse_address_of(lo)? } token::Ident(..) if self.token.is_keyword(kw::Box) => { self.bump(); @@ -596,6 +592,25 @@ impl<'a> Parser<'a> { } } + /// Parse `& mut? ` or `& raw [ const | mut ] ` + fn parse_address_of(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { + self.expect_and()?; + let (k, m) = if self.check_keyword(kw::Raw) + && self.look_ahead(1, Token::is_mutability) + { + let found_raw = self.eat_keyword(kw::Raw); + assert!(found_raw); + let mutability = self.parse_const_or_mut().unwrap(); + self.sess.gated_spans.gate(sym::raw_ref_op, lo.to(self.prev_span)); + (ast::BorrowKind::Raw, mutability) + } else { + (ast::BorrowKind::Ref, self.parse_mutability()) + }; + let e = self.parse_prefix_expr(None); + let (span, e) = self.interpolated_or_expr_span(e)?; + Ok((lo.to(span), ExprKind::AddrOf(k, m, e))) + } + /// Parses `a.b` or `a(13)` or `a[4]` or just `a`. fn parse_dot_or_call_expr( &mut self, diff --git a/src/librustc_passes/liveness.rs b/src/librustc_passes/liveness.rs index 8d7a0388122..81a39edf215 100644 --- a/src/librustc_passes/liveness.rs +++ b/src/librustc_passes/liveness.rs @@ -1174,7 +1174,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } hir::ExprKind::Box(ref e) | - hir::ExprKind::AddrOf(_, ref e) | + hir::ExprKind::AddrOf(_, _, ref e) | hir::ExprKind::Cast(ref e, _) | hir::ExprKind::Type(ref e, _) | hir::ExprKind::DropTemps(ref e) | diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 5d9b3a8fba4..b0a026b8ccc 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -484,7 +484,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } }, - (hir::ExprKind::AddrOf(_, ref expr), _, &ty::Ref(_, checked, _)) if { + ( + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref expr), + _, + &ty::Ref(_, checked, _) + ) if { self.infcx.can_sub(self.param_env, checked, &expected).is_ok() && !is_macro } => { // We have `&T`, check if what was expected was `T`. If so, diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index 163412f6a16..6c24f3184ca 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -238,8 +238,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ExprKind::Unary(unop, ref oprnd) => { self.check_expr_unary(unop, oprnd, expected, needs, expr) } - ExprKind::AddrOf(mutbl, ref oprnd) => { - self.check_expr_addr_of(mutbl, oprnd, expected, expr) + ExprKind::AddrOf(kind, mutbl, ref oprnd) => { + self.check_expr_addr_of(kind, mutbl, oprnd, expected, expr) } ExprKind::Path(ref qpath) => { self.check_expr_path(qpath, expr) @@ -424,6 +424,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_expr_addr_of( &self, + kind: hir::BorrowKind, mutbl: hir::Mutability, oprnd: &'tcx hir::Expr, expected: Expectation<'tcx>, @@ -432,7 +433,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hint = expected.only_has_type(self).map_or(NoExpectation, |ty| { match ty.kind { ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { - if oprnd.is_place_expr() { + if oprnd.is_syntactic_place_expr() { // Places may legitimately have unsized types. // For example, dereferences of a fat pointer and // the last field of a struct can be unsized. @@ -448,24 +449,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = self.check_expr_with_expectation_and_needs(&oprnd, hint, needs); let tm = ty::TypeAndMut { ty: ty, mutbl: mutbl }; - if tm.ty.references_error() { - self.tcx.types.err - } else { - // Note: at this point, we cannot say what the best lifetime - // is to use for resulting pointer. We want to use the - // shortest lifetime possible so as to avoid spurious borrowck - // errors. Moreover, the longest lifetime will depend on the - // precise details of the value whose address is being taken - // (and how long it is valid), which we don't know yet until type - // inference is complete. + match kind { + _ if tm.ty.references_error() => self.tcx.types.err, + hir::BorrowKind::Raw => { + self.check_named_place_expr(oprnd); + self.tcx.mk_ptr(tm) + } + hir::BorrowKind::Ref => { + // Note: at this point, we cannot say what the best lifetime + // is to use for resulting pointer. We want to use the + // shortest lifetime possible so as to avoid spurious borrowck + // errors. Moreover, the longest lifetime will depend on the + // precise details of the value whose address is being taken + // (and how long it is valid), which we don't know yet until + // type inference is complete. + // + // Therefore, here we simply generate a region variable. The + // region inferencer will then select a suitable value. + // Finally, borrowck will infer the value of the region again, + // this time with enough precision to check that the value + // whose address was taken can actually be made to live as long + // as it needs to live. + let region = self.next_region_var(infer::AddrOfRegion(expr.span)); + self.tcx.mk_ref(region, tm) + } + } + } + + /// Does this expression refer to a place that either: + /// * Is based on a local or static. + /// * Contains a dereference + /// Note that the adjustments for the children of `expr` should already + /// have been resolved. + fn check_named_place_expr(&self, oprnd: &'tcx hir::Expr) { + let is_named = oprnd.is_place_expr(|base| { + // Allow raw borrows if there are any deref adjustments. // - // Therefore, here we simply generate a region variable. The - // region inferencer will then select the ultimate value. - // Finally, borrowck is charged with guaranteeing that the - // value whose address was taken can actually be made to live - // as long as it needs to live. - let region = self.next_region_var(infer::AddrOfRegion(expr.span)); - self.tcx.mk_ref(region, tm) + // const VAL: (i32,) = (0,); + // const REF: &(i32,) = &(0,); + // + // &raw const VAL.0; // ERROR + // &raw const REF.0; // OK, same as &raw const (*REF).0; + // + // This is maybe too permissive, since it allows + // `let u = &raw const Box::new((1,)).0`, which creates an + // immediately dangling raw pointer. + self.tables.borrow().adjustments().get(base.hir_id).map_or(false, |x| { + x.iter().any(|adj| if let Adjust::Deref(_) = adj.kind { + true + } else { + false + }) + }) + }); + if !is_named { + struct_span_err!(self.tcx.sess, oprnd.span, E0745, "cannot take address of a temporary") + .span_label(oprnd.span, "temporary value") + .emit(); } } @@ -740,7 +780,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.help(msg); } err.emit(); - } else if !lhs.is_place_expr() { + } else if !lhs.is_syntactic_place_expr() { struct_span_err!(self.tcx.sess, expr.span, E0070, "invalid left-hand side expression") .span_label(expr.span, "left-hand of expression not valid") diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index 28db09fe92b..321faa4a322 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -33,7 +33,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return_ty }; - if !lhs_expr.is_place_expr() { + if !lhs_expr.is_syntactic_place_expr() { struct_span_err!( self.tcx.sess, lhs_expr.span, E0067, "invalid left-hand side expression") diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 2f9091282b7..f4fdc2882e7 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -645,7 +645,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionCtxt<'a, 'tcx> { intravisit::walk_expr(self, expr); } - hir::ExprKind::AddrOf(m, ref base) => { + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, m, ref base) => { self.link_addr_of(expr, m, &base); // Require that when you write a `&expr` expression, the diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 14243076941..dc26929100a 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -754,6 +754,21 @@ impl Mutability { } } +/// The kind of borrow in an `AddrOf` expression, +/// e.g., `&place` or `&raw const place`. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[derive(RustcEncodable, RustcDecodable, HashStable_Generic)] +pub enum BorrowKind { + /// A raw borrow, `&raw const $expr` or `&raw mut $expr`. + /// The resulting type is either `*const T` or `*mut T` + /// where `T = typeof($expr)`. + Ref, + /// A normal borrow, `&$expr` or `&mut $expr`. + /// The resulting type is either `&'a T` or `&'a mut T` + /// where `T = typeof($expr)` and `'a` is some lifetime. + Raw, +} + #[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Copy)] pub enum BinOpKind { /// The `+` operator (addition) @@ -1071,7 +1086,7 @@ impl Expr { ExprKind::Paren(expr) => expr.to_ty().map(TyKind::Paren)?, - ExprKind::AddrOf(mutbl, expr) => expr + ExprKind::AddrOf(BorrowKind::Ref, mutbl, expr) => expr .to_ty() .map(|ty| TyKind::Rptr(None, MutTy { ty, mutbl: *mutbl }))?, @@ -1262,8 +1277,8 @@ pub enum ExprKind { /// Optionally "qualified" (e.g., ` as SomeTrait>::SomeType`). Path(Option, Path), - /// A referencing operation (`&a` or `&mut a`). - AddrOf(Mutability, P), + /// A referencing operation (`&a`, `&mut a`, `&raw const a` or `&raw mut a`). + AddrOf(BorrowKind, Mutability, P), /// A `break`, with an optional label to break, and an optional expression. Break(Option