expr.rs 108.5 KB
Newer Older
1
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 3 4 5 6 7 8 9 10
// 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.

S
Steve Klabnik 已提交
11 12
//! # Translation of Expressions
//!
S
Steve Klabnik 已提交
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
//! The expr module handles translation of expressions. The most general
//! translation routine is `trans()`, which will translate an expression
//! into a datum. `trans_into()` is also available, which will translate
//! an expression and write the result directly into memory, sometimes
//! avoiding the need for a temporary stack slot. Finally,
//! `trans_to_lvalue()` is available if you'd like to ensure that the
//! result has cleanup scheduled.
//!
//! Internally, each of these functions dispatches to various other
//! expression functions depending on the kind of expression. We divide
//! up expressions into:
//!
//! - **Datum expressions:** Those that most naturally yield values.
//!   Examples would be `22`, `box x`, or `a + b` (when not overloaded).
//! - **DPS expressions:** Those that most naturally write into a location
//!   in memory. Examples would be `foo()` or `Point { x: 3, y: 4 }`.
//! - **Statement expressions:** That that do not generate a meaningful
//!   result. Examples would be `while { ... }` or `return 44`.
//!
S
Steve Klabnik 已提交
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
//! Public entry points:
//!
//! - `trans_into(bcx, expr, dest) -> bcx`: evaluates an expression,
//!   storing the result into `dest`. This is the preferred form, if you
//!   can manage it.
//!
//! - `trans(bcx, expr) -> DatumBlock`: evaluates an expression, yielding
//!   `Datum` with the result. You can then store the datum, inspect
//!   the value, etc. This may introduce temporaries if the datum is a
//!   structural type.
//!
//! - `trans_to_lvalue(bcx, expr, "...") -> DatumBlock`: evaluates an
//!   expression and ensures that the result has a cleanup associated with it,
//!   creating a temporary stack slot if necessary.
//!
//! - `trans_local_var -> Datum`: looks up a local variable or upvar.
48

49
#![allow(non_camel_case_types)]
50

S
Steven Fackler 已提交
51 52 53
pub use self::Dest::*;
use self::lazy_binop_ty::*;

54
use back::abi;
55
use llvm::{self, ValueRef, TypeKind};
56
use middle::check_const;
57
use middle::def;
N
Nick Cameron 已提交
58
use middle::lang_items::CoerceUnsizedTraitLangItem;
N
Nick Cameron 已提交
59
use middle::subst::{Substs, VecPerParamSpace};
N
Nick Cameron 已提交
60
use middle::traits;
61
use trans::{_match, adt, asm, base, callee, closure, consts, controlflow};
62 63
use trans::base::*;
use trans::build::*;
64
use trans::cleanup::{self, CleanupMethods, DropHintMethods};
65 66
use trans::common::*;
use trans::datum::*;
67
use trans::debuginfo::{self, DebugLoc, ToDebugLoc};
68
use trans::declare;
69 70 71 72 73
use trans::glue;
use trans::machine;
use trans::meth;
use trans::tvec;
use trans::type_of;
74 75
use middle::ty::adjustment::{AdjustDerefRef, AdjustReifyFnPointer};
use middle::ty::adjustment::{AdjustUnsafeFnPointer, CustomCoerceUnsized};
76
use middle::ty::{self, Ty};
77
use middle::ty::MethodCall;
78
use middle::ty::cast::{CastKind, CastTy};
79
use util::common::indenter;
80 81
use trans::machine::{llsize_of, llsize_of_alloc};
use trans::type_::Type;
J
James Miller 已提交
82

83 84 85
use rustc_front;
use rustc_front::hir;

86
use syntax::{ast, ast_util, codemap};
87
use syntax::parse::token::InternedString;
88
use syntax::ptr::P;
N
Nick Cameron 已提交
89
use syntax::parse::token;
90
use std::mem;
91

92 93 94 95 96
// Destinations

// These are passed around by the code generating functions to track the
// destination of a computation's value.

N
Niko Matsakis 已提交
97
#[derive(Copy, Clone, PartialEq)]
98
pub enum Dest {
99 100 101 102
    SaveIn(ValueRef),
    Ignore,
}

103
impl Dest {
104
    pub fn to_string(&self, ccx: &CrateContext) -> String {
B
Ben Striegel 已提交
105
        match *self {
106
            SaveIn(v) => format!("SaveIn({})", ccx.tn().val_to_string(v)),
107
            Ignore => "Ignore".to_string()
108 109 110 111
        }
    }
}

S
Steve Klabnik 已提交
112 113
/// This function is equivalent to `trans(bcx, expr).store_to_dest(dest)` but it may generate
/// better optimized LLVM code.
114
pub fn trans_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
115
                              expr: &hir::Expr,
116 117
                              dest: Dest)
                              -> Block<'blk, 'tcx> {
118 119
    let mut bcx = bcx;

120 121
    debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);

122
    if adjustment_required(bcx, expr) {
123 124 125
        // use trans, which may be less efficient but
        // which will perform the adjustments:
        let datum = unpack_datum!(bcx, trans(bcx, expr));
126 127 128
        return datum.store_to_dest(bcx, dest, expr.id);
    }

129
    let qualif = *bcx.tcx().const_qualif_map.borrow().get(&expr.id).unwrap();
T
Fallout  
Tamir Duberstein 已提交
130 131 132 133 134
    if !qualif.intersects(
        check_const::ConstQualif::NOT_CONST |
        check_const::ConstQualif::NEEDS_DROP
    ) {
        if !qualif.intersects(check_const::ConstQualif::PREFER_IN_PLACE) {
135
            if let SaveIn(lldest) = dest {
O
Oliver Schneider 已提交
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
                match consts::get_const_expr_as_global(bcx.ccx(), expr, qualif,
                                                       bcx.fcx.param_substs,
                                                       consts::TrueConst::No) {
                    Ok(global) => {
                        // Cast pointer to destination, because constants
                        // have different types.
                        let lldest = PointerCast(bcx, lldest, val_ty(global));
                        memcpy_ty(bcx, lldest, global, expr_ty_adjusted(bcx, expr));
                        return bcx;
                    },
                    Err(consts::ConstEvalFailure::Runtime(_)) => {
                        // in case const evaluation errors, translate normally
                        // debug assertions catch the same errors
                        // see RFC 1229
                    },
                    Err(consts::ConstEvalFailure::Compiletime(_)) => {
                        return bcx;
                    },
                }
155
            }
156 157 158 159
            // Even if we don't have a value to emit, and the expression
            // doesn't have any side-effects, we still have to translate the
            // body of any closures.
            // FIXME: Find a better way of handling this case.
160 161 162 163 164
        } else {
            // The only way we're going to see a `const` at this point is if
            // it prefers in-place instantiation, likely because it contains
            // `[x; N]` somewhere within.
            match expr.node {
165
                hir::ExprPath(..) => {
166 167
                    match bcx.def(expr.id) {
                        def::DefConst(did) => {
168
                            let const_expr = consts::get_const_expr(bcx.ccx(), did, expr);
169 170 171 172 173 174 175
                            // Temporarily get cleanup scopes out of the way,
                            // as they require sub-expressions to be contained
                            // inside the current AST scope.
                            // These should record no cleanups anyways, `const`
                            // can't have destructors.
                            let scopes = mem::replace(&mut *bcx.fcx.scopes.borrow_mut(),
                                                      vec![]);
176 177 178 179 180 181 182
                            // Lock emitted debug locations to the location of
                            // the constant reference expression.
                            debuginfo::with_source_location_override(bcx.fcx,
                                                                     expr.debug_loc(),
                                                                     || {
                                bcx = trans_into(bcx, const_expr, dest)
                            });
183 184 185 186 187 188 189 190 191 192 193
                            let scopes = mem::replace(&mut *bcx.fcx.scopes.borrow_mut(),
                                                      scopes);
                            assert!(scopes.is_empty());
                            return bcx;
                        }
                        _ => {}
                    }
                }
                _ => {}
            }
        }
194 195
    }

196
    debug!("trans_into() expr={:?}", expr);
197

198 199
    let cleanup_debug_loc = debuginfo::get_cleanup_debug_loc_for_ast_node(bcx.ccx(),
                                                                          expr.id,
200 201 202
                                                                          expr.span,
                                                                          false);
    bcx.fcx.push_ast_cleanup_scope(cleanup_debug_loc);
203

A
Ariel Ben-Yehuda 已提交
204
    let kind = expr_kind(bcx.tcx(), expr);
205
    bcx = match kind {
A
Ariel Ben-Yehuda 已提交
206
        ExprKind::Lvalue | ExprKind::RvalueDatum => {
207 208
            trans_unadjusted(bcx, expr).store_to_dest(dest, expr.id)
        }
A
Ariel Ben-Yehuda 已提交
209
        ExprKind::RvalueDps => {
210 211
            trans_rvalue_dps_unadjusted(bcx, expr, dest)
        }
A
Ariel Ben-Yehuda 已提交
212
        ExprKind::RvalueStmt => {
213 214 215 216 217 218 219
            trans_rvalue_stmt_unadjusted(bcx, expr)
        }
    };

    bcx.fcx.pop_and_trans_ast_cleanup_scope(bcx, expr.id)
}

S
Steve Klabnik 已提交
220 221 222
/// Translates an expression, returning a datum (and new block) encapsulating the result. When
/// possible, it is preferred to use `trans_into`, as that may avoid creating a temporary on the
/// stack.
223
pub fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
224
                         expr: &hir::Expr)
225
                         -> DatumBlock<'blk, 'tcx, Expr> {
226
    debug!("trans(expr={:?})", expr);
N
Niko Matsakis 已提交
227

228
    let mut bcx = bcx;
229
    let fcx = bcx.fcx;
230
    let qualif = *bcx.tcx().const_qualif_map.borrow().get(&expr.id).unwrap();
T
Fallout  
Tamir Duberstein 已提交
231 232 233 234 235
    let adjusted_global = !qualif.intersects(check_const::ConstQualif::NON_STATIC_BORROWS);
    let global = if !qualif.intersects(
        check_const::ConstQualif::NOT_CONST |
        check_const::ConstQualif::NEEDS_DROP
    ) {
O
Oliver Schneider 已提交
236 237 238 239 240 241
        match consts::get_const_expr_as_global(bcx.ccx(), expr, qualif,
                                                            bcx.fcx.param_substs,
                                                            consts::TrueConst::No) {
            Ok(global) => {
                if qualif.intersects(check_const::ConstQualif::HAS_STATIC_BORROWS) {
                    // Is borrowed as 'static, must return lvalue.
242

O
Oliver Schneider 已提交
243 244 245 246 247 248 249
                    // Cast pointer to global, because constants have different types.
                    let const_ty = expr_ty_adjusted(bcx, expr);
                    let llty = type_of::type_of(bcx.ccx(), const_ty);
                    let global = PointerCast(bcx, global, llty.ptr_to());
                    let datum = Datum::new(global, const_ty, Lvalue::new("expr::trans"));
                    return DatumBlock::new(bcx, datum.to_expr_datum());
                }
250

O
Oliver Schneider 已提交
251 252 253 254 255 256
                // Otherwise, keep around and perform adjustments, if needed.
                let const_ty = if adjusted_global {
                    expr_ty_adjusted(bcx, expr)
                } else {
                    expr_ty(bcx, expr)
                };
257

O
Oliver Schneider 已提交
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
                // This could use a better heuristic.
                Some(if type_is_immediate(bcx.ccx(), const_ty) {
                    // Cast pointer to global, because constants have different types.
                    let llty = type_of::type_of(bcx.ccx(), const_ty);
                    let global = PointerCast(bcx, global, llty.ptr_to());
                    // Maybe just get the value directly, instead of loading it?
                    immediate_rvalue(load_ty(bcx, global, const_ty), const_ty)
                } else {
                    let scratch = alloc_ty(bcx, const_ty, "const");
                    call_lifetime_start(bcx, scratch);
                    let lldest = if !const_ty.is_structural() {
                        // Cast pointer to slot, because constants have different types.
                        PointerCast(bcx, scratch, val_ty(global))
                    } else {
                        // In this case, memcpy_ty calls llvm.memcpy after casting both
                        // source and destination to i8*, so we don't need any casts.
                        scratch
                    };
                    memcpy_ty(bcx, lldest, global, const_ty);
                    Datum::new(scratch, const_ty, Rvalue::new(ByRef))
                })
            },
            Err(consts::ConstEvalFailure::Runtime(_)) => {
                // in case const evaluation errors, translate normally
                // debug assertions catch the same errors
                // see RFC 1229
                None
            },
            Err(consts::ConstEvalFailure::Compiletime(_)) => {
                // generate a dummy llvm value
                let const_ty = expr_ty(bcx, expr);
                let llty = type_of::type_of(bcx.ccx(), const_ty);
                let dummy = C_undef(llty.ptr_to());
                Some(Datum::new(dummy, const_ty, Rvalue::new(ByRef)))
            },
        }
294 295 296
    } else {
        None
    };
297

298 299
    let cleanup_debug_loc = debuginfo::get_cleanup_debug_loc_for_ast_node(bcx.ccx(),
                                                                          expr.id,
300 301 302
                                                                          expr.span,
                                                                          false);
    fcx.push_ast_cleanup_scope(cleanup_debug_loc);
303 304 305 306 307 308 309 310 311
    let datum = match global {
        Some(rvalue) => rvalue.to_expr_datum(),
        None => unpack_datum!(bcx, trans_unadjusted(bcx, expr))
    };
    let datum = if adjusted_global {
        datum // trans::consts already performed adjustments.
    } else {
        unpack_datum!(bcx, apply_adjustments(bcx, expr, datum))
    };
312
    bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, expr.id);
K
Kevin Butler 已提交
313
    return DatumBlock::new(bcx, datum);
314 315
}

316
pub fn get_meta(bcx: Block, fat_ptr: ValueRef) -> ValueRef {
317
    StructGEP(bcx, fat_ptr, abi::FAT_PTR_EXTRA)
N
Nick Cameron 已提交
318 319
}

320
pub fn get_dataptr(bcx: Block, fat_ptr: ValueRef) -> ValueRef {
321
    StructGEP(bcx, fat_ptr, abi::FAT_PTR_ADDR)
N
Nick Cameron 已提交
322 323
}

324 325
pub fn copy_fat_ptr(bcx: Block, src_ptr: ValueRef, dst_ptr: ValueRef) {
    Store(bcx, Load(bcx, get_dataptr(bcx, src_ptr)), get_dataptr(bcx, dst_ptr));
326
    Store(bcx, Load(bcx, get_meta(bcx, src_ptr)), get_meta(bcx, dst_ptr));
327 328
}

329 330 331 332 333 334 335 336 337 338
/// Retrieve the information we are losing (making dynamic) in an unsizing
/// adjustment.
///
/// The `old_info` argument is a bit funny. It is intended for use
/// in an upcast, where the new vtable for an object will be drived
/// from the old one.
pub fn unsized_info<'ccx, 'tcx>(ccx: &CrateContext<'ccx, 'tcx>,
                                source: Ty<'tcx>,
                                target: Ty<'tcx>,
                                old_info: Option<ValueRef>,
N
Nick Cameron 已提交
339
                                param_substs: &'tcx Substs<'tcx>)
340
                                -> ValueRef {
341
    let (source, target) = ccx.tcx().struct_lockstep_tails(source, target);
342
    match (&source.sty, &target.sty) {
343
        (&ty::TyArray(_, len), &ty::TySlice(_)) => C_uint(ccx, len),
344
        (&ty::TyTrait(_), &ty::TyTrait(_)) => {
345 346 347 348 349
            // For now, upcasts are limited to changes in marker
            // traits, and hence never actually require an actual
            // change to the vtable.
            old_info.expect("unsized_info: missing old info for trait upcast")
        }
350
        (_, &ty::TyTrait(box ty::TraitTy { ref principal, .. })) => {
351
            // Note that we preserve binding levels here:
352
            let substs = principal.0.substs.with_self_ty(source).erase_regions();
353
            let substs = ccx.tcx().mk_substs(substs);
354 355
            let trait_ref = ty::Binder(ty::TraitRef { def_id: principal.def_id(),
                                                      substs: substs });
356
            consts::ptrcast(meth::get_vtable(ccx, trait_ref, param_substs),
357 358
                            Type::vtable_ptr(ccx))
        }
359 360 361
        _ => ccx.sess().bug(&format!("unsized_info: invalid unsizing {:?} -> {:?}",
                                     source,
                                     target))
362 363 364
    }
}

365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
fn adjustment_required<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                   expr: &hir::Expr) -> bool {
    let adjustment = match bcx.tcx().tables.borrow().adjustments.get(&expr.id).cloned() {
        None => { return false; }
        Some(adj) => adj
    };

    // Don't skip a conversion from Box<T> to &T, etc.
    if bcx.tcx().is_overloaded_autoderef(expr.id, 0) {
        return true;
    }

    match adjustment {
        AdjustReifyFnPointer => {
            // FIXME(#19925) once fn item types are
            // zero-sized, we'll need to return true here
            false
        }
        AdjustUnsafeFnPointer => {
            // purely a type-level thing
            false
        }
        AdjustDerefRef(ref adj) => {
            // We are a bit paranoid about adjustments and thus might have a re-
            // borrow here which merely derefs and then refs again (it might have
            // a different region or mutability, but we don't care here).
            !(adj.autoderefs == 1 && adj.autoref.is_some() && adj.unsize.is_none())
        }
    }
}

S
Steve Klabnik 已提交
396 397
/// Helper for trans that apply adjustments from `expr` to `datum`, which should be the unadjusted
/// translation of `expr`.
398
fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
399
                                 expr: &hir::Expr,
400
                                 datum: Datum<'tcx, Expr>)
401 402
                                 -> DatumBlock<'blk, 'tcx, Expr>
{
403 404
    let mut bcx = bcx;
    let mut datum = datum;
405
    let adjustment = match bcx.tcx().tables.borrow().adjustments.get(&expr.id).cloned() {
406
        None => {
K
Kevin Butler 已提交
407
            return DatumBlock::new(bcx, datum);
408
        }
409
        Some(adj) => { adj }
410
    };
411 412
    debug!("unadjusted datum for expr {:?}: {} adjustment={:?}",
           expr,
413
           datum.to_string(bcx.ccx()),
414
           adjustment);
415
    match adjustment {
416
        AdjustReifyFnPointer => {
417 418
            // FIXME(#19925) once fn item types are
            // zero-sized, we'll need to do something here
419
        }
420 421 422
        AdjustUnsafeFnPointer => {
            // purely a type-level thing
        }
423
        AdjustDerefRef(ref adj) => {
424
            let skip_reborrows = if adj.autoderefs == 1 && adj.autoref.is_some() {
425 426
                // We are a bit paranoid about adjustments and thus might have a re-
                // borrow here which merely derefs and then refs again (it might have
427 428 429
                // a different region or mutability, but we don't care here).
                match datum.ty.sty {
                    // Don't skip a conversion from Box<T> to &T, etc.
430
                    ty::TyRef(..) => {
431
                        if bcx.tcx().is_overloaded_autoderef(expr.id, 0) {
432 433 434 435
                            // Don't skip an overloaded deref.
                            0
                        } else {
                            1
436 437
                        }
                    }
438
                    _ => 0
439
                }
440 441
            } else {
                0
N
Nick Cameron 已提交
442 443
            };

444
            if adj.autoderefs > skip_reborrows {
445 446
                // Schedule cleanup.
                let lval = unpack_datum!(bcx, datum.to_lvalue_datum(bcx, "auto_deref", expr.id));
447 448 449
                datum = unpack_datum!(bcx, deref_multiple(bcx, expr,
                                                          lval.to_expr_datum(),
                                                          adj.autoderefs - skip_reborrows));
450 451
            }

452
            // (You might think there is a more elegant way to do this than a
453 454
            // skip_reborrows bool, but then you remember that the borrow checker exists).
            if skip_reborrows == 0 && adj.autoref.is_some() {
455
                datum = unpack_datum!(bcx, auto_ref(bcx, datum, expr));
456 457 458
            }

            if let Some(target) = adj.unsize {
N
Nick Cameron 已提交
459 460 461 462 463 464 465 466 467 468
                // We do not arrange cleanup ourselves; if we already are an
                // L-value, then cleanup will have already been scheduled (and
                // the `datum.to_rvalue_datum` call below will emit code to zero
                // the drop flag when moving out of the L-value). If we are an
                // R-value, then we do not need to schedule cleanup.
                let source_datum = unpack_datum!(bcx,
                    datum.to_rvalue_datum(bcx, "__coerce_source"));

                let target = bcx.monomorphize(&target);

469
                let scratch = alloc_ty(bcx, target, "__coerce_target");
470
                call_lifetime_start(bcx, scratch);
N
Nick Cameron 已提交
471 472 473 474 475
                let target_datum = Datum::new(scratch, target,
                                              Rvalue::new(ByRef));
                bcx = coerce_unsized(bcx, expr.span, source_datum, target_datum);
                datum = Datum::new(scratch, target,
                                   RvalueExpr(Rvalue::new(ByRef)));
N
Nick Cameron 已提交
476
            }
477
        }
478
    }
479
    debug!("after adjustments, datum={}", datum.to_string(bcx.ccx()));
N
Nick Cameron 已提交
480 481
    DatumBlock::new(bcx, datum)
}
482

N
Nick Cameron 已提交
483 484 485 486 487 488 489 490 491 492 493
fn coerce_unsized<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                              span: codemap::Span,
                              source: Datum<'tcx, Rvalue>,
                              target: Datum<'tcx, Rvalue>)
                              -> Block<'blk, 'tcx> {
    let mut bcx = bcx;
    debug!("coerce_unsized({} -> {})",
           source.to_string(bcx.ccx()),
           target.to_string(bcx.ccx()));

    match (&source.ty.sty, &target.ty.sty) {
494
        (&ty::TyBox(a), &ty::TyBox(b)) |
J
Jared Roesch 已提交
495 496 497 498 499 500
        (&ty::TyRef(_, ty::TypeAndMut { ty: a, .. }),
         &ty::TyRef(_, ty::TypeAndMut { ty: b, .. })) |
        (&ty::TyRef(_, ty::TypeAndMut { ty: a, .. }),
         &ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) |
        (&ty::TyRawPtr(ty::TypeAndMut { ty: a, .. }),
         &ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) => {
N
Nick Cameron 已提交
501 502 503 504 505 506 507 508 509 510
            let (inner_source, inner_target) = (a, b);

            let (base, old_info) = if !type_is_sized(bcx.tcx(), inner_source) {
                // Normally, the source is a thin pointer and we are
                // adding extra info to make a fat pointer. The exception
                // is when we are upcasting an existing object fat pointer
                // to use a different vtable. In that case, we want to
                // load out the original data pointer so we can repackage
                // it.
                (Load(bcx, get_dataptr(bcx, source.val)),
511
                Some(Load(bcx, get_meta(bcx, source.val))))
N
Nick Cameron 已提交
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
            } else {
                let val = if source.kind.is_by_ref() {
                    load_ty(bcx, source.val, source.ty)
                } else {
                    source.val
                };
                (val, None)
            };

            let info = unsized_info(bcx.ccx(), inner_source, inner_target,
                                    old_info, bcx.fcx.param_substs);

            // Compute the base pointer. This doesn't change the pointer value,
            // but merely its type.
            let ptr_ty = type_of::in_memory_type_of(bcx.ccx(), inner_target).ptr_to();
            let base = PointerCast(bcx, base, ptr_ty);

            Store(bcx, base, get_dataptr(bcx, target.val));
530
            Store(bcx, info, get_meta(bcx, target.val));
531
        }
N
Nick Cameron 已提交
532

N
Nick Cameron 已提交
533
        // This can be extended to enums and tuples in the future.
534 535
        // (&ty::TyEnum(def_id_a, _), &ty::TyEnum(def_id_b, _)) |
        (&ty::TyStruct(def_id_a, _), &ty::TyStruct(def_id_b, _)) => {
N
Nick Cameron 已提交
536
            assert_eq!(def_id_a, def_id_b);
537

N
Nick Cameron 已提交
538 539 540
            // The target is already by-ref because it's to be written to.
            let source = unpack_datum!(bcx, source.to_ref_datum(bcx));
            assert!(target.kind.is_by_ref());
541

N
Nick Cameron 已提交
542 543 544
            let trait_substs = Substs::erased(VecPerParamSpace::new(vec![target.ty],
                                                                    vec![source.ty],
                                                                    Vec::new()));
N
Nick Cameron 已提交
545
            let trait_ref = ty::Binder(ty::TraitRef {
N
Nick Cameron 已提交
546 547 548
                def_id: langcall(bcx, Some(span), "coercion",
                                 CoerceUnsizedTraitLangItem),
                substs: bcx.tcx().mk_substs(trait_substs)
N
Nick Cameron 已提交
549
            });
N
Nick Cameron 已提交
550

N
Nick Cameron 已提交
551 552
            let kind = match fulfill_obligation(bcx.ccx(), span, trait_ref) {
                traits::VtableImpl(traits::VtableImplData { impl_def_id, .. }) => {
553
                    bcx.tcx().custom_coerce_unsized_kind(impl_def_id)
N
Nick Cameron 已提交
554 555
                }
                vtable => {
556 557
                    bcx.sess().span_bug(span, &format!("invalid CoerceUnsized vtable: {:?}",
                                                       vtable));
N
Nick Cameron 已提交
558 559
                }
            };
N
Nick Cameron 已提交
560

N
Nick Cameron 已提交
561
            let repr_source = adt::represent_type(bcx.ccx(), source.ty);
N
Nick Cameron 已提交
562 563 564 565 566 567
            let src_fields = match &*repr_source {
                &adt::Repr::Univariant(ref s, _) => &s.fields,
                _ => bcx.sess().span_bug(span,
                                         &format!("Non univariant struct? (repr_source: {:?})",
                                                  repr_source)),
            };
N
Nick Cameron 已提交
568
            let repr_target = adt::represent_type(bcx.ccx(), target.ty);
N
Nick Cameron 已提交
569 570 571 572 573 574
            let target_fields = match &*repr_target {
                &adt::Repr::Univariant(ref s, _) => &s.fields,
                _ => bcx.sess().span_bug(span,
                                         &format!("Non univariant struct? (repr_target: {:?})",
                                                  repr_target)),
            };
N
Nick Cameron 已提交
575 576

            let coerce_index = match kind {
577
                CustomCoerceUnsized::Struct(i) => i
N
Nick Cameron 已提交
578
            };
N
Nick Cameron 已提交
579
            assert!(coerce_index < src_fields.len() && src_fields.len() == target_fields.len());
N
Nick Cameron 已提交
580

581
            let iter = src_fields.iter().zip(target_fields).enumerate();
N
Nick Cameron 已提交
582
            for (i, (src_ty, target_ty)) in iter {
N
Nick Cameron 已提交
583 584 585 586 587 588
                let ll_source = adt::trans_field_ptr(bcx, &repr_source, source.val, 0, i);
                let ll_target = adt::trans_field_ptr(bcx, &repr_target, target.val, 0, i);

                // If this is the field we need to coerce, recurse on it.
                if i == coerce_index {
                    coerce_unsized(bcx, span,
N
Nick Cameron 已提交
589
                                   Datum::new(ll_source, src_ty,
N
Nick Cameron 已提交
590
                                              Rvalue::new(ByRef)),
N
Nick Cameron 已提交
591
                                   Datum::new(ll_target, target_ty,
N
Nick Cameron 已提交
592 593 594
                                              Rvalue::new(ByRef)));
                } else {
                    // Otherwise, simply copy the data from the source.
595
                    assert!(src_ty.is_phantom_data() || src_ty == target_ty);
N
Nick Cameron 已提交
596
                    memcpy_ty(bcx, ll_target, ll_source, src_ty);
N
Nick Cameron 已提交
597 598 599
                }
            }
        }
600 601 602
        _ => bcx.sess().bug(&format!("coerce_unsized: invalid coercion {:?} -> {:?}",
                                     source.ty,
                                     target.ty))
603
    }
N
Nick Cameron 已提交
604
    bcx
605 606
}

S
Steve Klabnik 已提交
607 608 609 610 611 612 613
/// Translates an expression in "lvalue" mode -- meaning that it returns a reference to the memory
/// that the expr represents.
///
/// If this expression is an rvalue, this implies introducing a temporary.  In other words,
/// something like `x().f` is translated into roughly the equivalent of
///
///   { tmp = x(); tmp.f }
614
pub fn trans_to_lvalue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
615
                                   expr: &hir::Expr,
616 617
                                   name: &str)
                                   -> DatumBlock<'blk, 'tcx, Lvalue> {
618 619 620
    let mut bcx = bcx;
    let datum = unpack_datum!(bcx, trans(bcx, expr));
    return datum.to_lvalue_datum(bcx, name, expr.id);
621 622
}

S
Steve Klabnik 已提交
623 624
/// A version of `trans` that ignores adjustments. You almost certainly do not want to call this
/// directly.
625
fn trans_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
626
                                expr: &hir::Expr)
627
                                -> DatumBlock<'blk, 'tcx, Expr> {
628 629
    let mut bcx = bcx;

630
    debug!("trans_unadjusted(expr={:?})", expr);
631 632
    let _indenter = indenter();

633
    debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);
634

A
Ariel Ben-Yehuda 已提交
635 636
    return match expr_kind(bcx.tcx(), expr) {
        ExprKind::Lvalue | ExprKind::RvalueDatum => {
637
            let datum = unpack_datum!(bcx, {
638
                trans_datum_unadjusted(bcx, expr)
639
            });
640 641

            DatumBlock {bcx: bcx, datum: datum}
642 643
        }

A
Ariel Ben-Yehuda 已提交
644
        ExprKind::RvalueStmt => {
645
            bcx = trans_rvalue_stmt_unadjusted(bcx, expr);
646
            nil(bcx, expr_ty(bcx, expr))
647 648
        }

A
Ariel Ben-Yehuda 已提交
649
        ExprKind::RvalueDps => {
650
            let ty = expr_ty(bcx, expr);
651
            if type_is_zero_size(bcx.ccx(), ty) {
652
                bcx = trans_rvalue_dps_unadjusted(bcx, expr, Ignore);
653
                nil(bcx, ty)
654
            } else {
655
                let scratch = rvalue_scratch_datum(bcx, ty, "");
656 657
                bcx = trans_rvalue_dps_unadjusted(
                    bcx, expr, SaveIn(scratch.val));
658 659 660 661 662 663 664 665 666 667

                // Note: this is not obviously a good idea.  It causes
                // immediate values to be loaded immediately after a
                // return from a call or other similar expression,
                // which in turn leads to alloca's having shorter
                // lifetimes and hence larger stack frames.  However,
                // in turn it can lead to more register pressure.
                // Still, in practice it seems to increase
                // performance, since we have fewer problems with
                // morestack churn.
668 669
                let scratch = unpack_datum!(
                    bcx, scratch.to_appropriate_datum(bcx));
670

K
Kevin Butler 已提交
671
                DatumBlock::new(bcx, scratch.to_expr_datum())
672 673
            }
        }
674
    };
675

676
    fn nil<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ty: Ty<'tcx>)
677
                       -> DatumBlock<'blk, 'tcx, Expr> {
678 679
        let llval = C_undef(type_of::type_of(bcx.ccx(), ty));
        let datum = immediate_rvalue(llval, ty);
K
Kevin Butler 已提交
680
        DatumBlock::new(bcx, datum.to_expr_datum())
681 682 683
    }
}

684
fn trans_datum_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
685
                                      expr: &hir::Expr)
686
                                      -> DatumBlock<'blk, 'tcx, Expr> {
687 688 689
    let mut bcx = bcx;
    let fcx = bcx.fcx;
    let _icx = push_ctxt("trans_datum_unadjusted");
690 691

    match expr.node {
692
        hir::ExprPath(..) => {
693 694
            trans_def(bcx, expr, bcx.def(expr.id))
        }
695 696
        hir::ExprField(ref base, name) => {
            trans_rec_field(bcx, &**base, name.node)
697
        }
698
        hir::ExprTupField(ref base, idx) => {
699 700
            trans_rec_tup_field(bcx, &**base, idx.node)
        }
701
        hir::ExprIndex(ref base, ref idx) => {
702
            trans_index(bcx, expr, &**base, &**idx, MethodCall::expr(expr.id))
N
Nick Cameron 已提交
703
        }
704
        hir::ExprBox(ref contents) => {
E
Eduard Burtescu 已提交
705
            // Special case for `Box<T>`
706
            let box_ty = expr_ty(bcx, expr);
707
            let contents_ty = expr_ty(bcx, &**contents);
708
            match box_ty.sty {
709
                ty::TyBox(..) => {
710
                    trans_uniq_expr(bcx, expr, box_ty, &**contents, contents_ty)
711 712
                }
                _ => bcx.sess().span_bug(expr.span,
E
Eduard Burtescu 已提交
713
                                         "expected unique box")
714
            }
N
Nick Cameron 已提交
715

716
        }
717 718
        hir::ExprLit(ref lit) => trans_immediate_lit(bcx, expr, &**lit),
        hir::ExprBinary(op, ref lhs, ref rhs) => {
719
            trans_binary(bcx, expr, op, &**lhs, &**rhs)
720
        }
721
        hir::ExprUnary(op, ref x) => {
722
            trans_unary(bcx, expr, op, &**x)
723
        }
724
        hir::ExprAddrOf(_, ref x) => {
N
Nick Cameron 已提交
725
            match x.node {
726
                hir::ExprRepeat(..) | hir::ExprVec(..) => {
N
Nick Cameron 已提交
727
                    // Special case for slices.
728
                    let cleanup_debug_loc =
729 730 731 732
                        debuginfo::get_cleanup_debug_loc_for_ast_node(bcx.ccx(),
                                                                      x.id,
                                                                      x.span,
                                                                      false);
733
                    fcx.push_ast_cleanup_scope(cleanup_debug_loc);
N
Nick Cameron 已提交
734 735 736 737 738 739 740 741 742
                    let datum = unpack_datum!(
                        bcx, tvec::trans_slice_vec(bcx, expr, &**x));
                    bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, x.id);
                    DatumBlock::new(bcx, datum)
                }
                _ => {
                    trans_addr_of(bcx, expr, &**x)
                }
            }
743
        }
744
        hir::ExprCast(ref val, _) => {
745
            // Datum output mode means this is a scalar cast:
746
            trans_imm_cast(bcx, &**val, expr.id)
747
        }
748 749 750
        _ => {
            bcx.tcx().sess.span_bug(
                expr.span,
J
Jorge Aparicio 已提交
751
                &format!("trans_rvalue_datum_unadjusted reached \
752
                         fall-through case: {:?}",
753
                        expr.node));
754 755 756 757
        }
    }
}

758
fn trans_field<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
759
                              base: &hir::Expr,
760 761
                              get_idx: F)
                              -> DatumBlock<'blk, 'tcx, Expr> where
762
    F: FnOnce(&'blk ty::ctxt<'tcx>, &VariantInfo<'tcx>) -> usize,
763
{
764 765 766 767
    let mut bcx = bcx;
    let _icx = push_ctxt("trans_rec_field");

    let base_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, base, "field"));
768
    let bare_ty = base_datum.ty;
N
Nick Cameron 已提交
769
    let repr = adt::represent_type(bcx.ccx(), bare_ty);
770
    let vinfo = VariantInfo::from_ty(bcx.tcx(), bare_ty, None);
N
Nick Cameron 已提交
771

772 773 774 775 776
    let ix = get_idx(bcx.tcx(), &vinfo);
    let d = base_datum.get_element(
        bcx,
        vinfo.fields[ix].1,
        |srcval| adt::trans_field_ptr(bcx, &*repr, srcval, vinfo.discr, ix));
777

778 779 780 781 782
    if type_is_sized(bcx.tcx(), d.ty) {
        DatumBlock { datum: d.to_expr_datum(), bcx: bcx }
    } else {
        let scratch = rvalue_scratch_datum(bcx, d.ty, "");
        Store(bcx, d.val, get_dataptr(bcx, scratch.val));
783 784
        let info = Load(bcx, get_meta(bcx, base_datum.val));
        Store(bcx, info, get_meta(bcx, scratch.val));
785 786 787 788 789

        // Always generate an lvalue datum, because this pointer doesn't own
        // the data and cleanup is scheduled elsewhere.
        DatumBlock::new(bcx, Datum::new(scratch.val, scratch.ty, LvalueExpr(d.kind)))
    }
790 791 792 793
}

/// Translates `base.field`.
fn trans_rec_field<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
794
                               base: &hir::Expr,
795
                               field: ast::Name)
796
                               -> DatumBlock<'blk, 'tcx, Expr> {
797
    trans_field(bcx, base, |_, vinfo| vinfo.field_index(field))
798 799 800 801
}

/// Translates `base.<idx>`.
fn trans_rec_tup_field<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
802
                                   base: &hir::Expr,
803
                                   idx: usize)
804 805
                                   -> DatumBlock<'blk, 'tcx, Expr> {
    trans_field(bcx, base, |_, _| idx)
806 807
}

808
fn trans_index<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
809 810 811
                           index_expr: &hir::Expr,
                           base: &hir::Expr,
                           idx: &hir::Expr,
812 813
                           method_call: MethodCall)
                           -> DatumBlock<'blk, 'tcx, Expr> {
814 815 816 817 818 819
    //! Translates `base[idx]`.

    let _icx = push_ctxt("trans_index");
    let ccx = bcx.ccx();
    let mut bcx = bcx;

820 821
    let index_expr_debug_loc = index_expr.debug_loc();

822
    // Check for overloaded index.
823
    let method_ty = ccx.tcx()
824
                       .tables
825
                       .borrow()
826
                       .method_map
827
                       .get(&method_call)
828 829 830
                       .map(|method| method.ty);
    let elt_datum = match method_ty {
        Some(method_ty) => {
831 832
            let method_ty = monomorphize_type(bcx, method_ty);

833
            let base_datum = unpack_datum!(bcx, trans(bcx, base));
834

835 836
            // Translate index expression.
            let ix_datum = unpack_datum!(bcx, trans(bcx, idx));
837

838
            let ref_ty = // invoked methods have LB regions instantiated:
839
                bcx.tcx().no_late_bound_regions(&method_ty.fn_ret()).unwrap().unwrap();
840
            let elt_ty = match ref_ty.builtin_deref(true, ty::NoPreference) {
841 842 843 844 845 846 847
                None => {
                    bcx.tcx().sess.span_bug(index_expr.span,
                                            "index method didn't return a \
                                             dereferenceable type?!")
                }
                Some(elt_tm) => elt_tm.ty,
            };
848 849 850 851 852 853 854 855 856 857 858

            // Overloaded. Evaluate `trans_overloaded_op`, which will
            // invoke the user's index() method, which basically yields
            // a `&T` pointer.  We can then proceed down the normal
            // path (below) to dereference that `&T`.
            let scratch = rvalue_scratch_datum(bcx, ref_ty, "overloaded_index_elt");
            unpack_result!(bcx,
                           trans_overloaded_op(bcx,
                                               index_expr,
                                               method_call,
                                               base_datum,
859
                                               Some((ix_datum, idx.id)),
860
                                               Some(SaveIn(scratch.val)),
861
                                               false));
862
            let datum = scratch.to_expr_datum();
863
            let lval = Lvalue::new("expr::trans_index overload");
864
            if type_is_sized(bcx.tcx(), elt_ty) {
865
                Datum::new(datum.to_llscalarish(bcx), elt_ty, LvalueExpr(lval))
866
            } else {
867
                Datum::new(datum.val, elt_ty, LvalueExpr(lval))
868
            }
869 870 871 872 873 874 875 876 877 878 879 880 881
        }
        None => {
            let base_datum = unpack_datum!(bcx, trans_to_lvalue(bcx,
                                                                base,
                                                                "index"));

            // Translate index expression and cast to a suitable LLVM integer.
            // Rust is less strict than LLVM in this regard.
            let ix_datum = unpack_datum!(bcx, trans(bcx, idx));
            let ix_val = ix_datum.to_llscalarish(bcx);
            let ix_size = machine::llbitsize_of_real(bcx.ccx(),
                                                     val_ty(ix_val));
            let int_size = machine::llbitsize_of_real(bcx.ccx(),
882
                                                      ccx.int_type());
883 884
            let ix_val = {
                if ix_size < int_size {
885
                    if expr_ty(bcx, idx).is_signed() {
886 887
                        SExt(bcx, ix_val, ccx.int_type())
                    } else { ZExt(bcx, ix_val, ccx.int_type()) }
888
                } else if ix_size > int_size {
889
                    Trunc(bcx, ix_val, ccx.int_type())
890 891 892 893
                } else {
                    ix_val
                }
            };
894

895
            let unit_ty = base_datum.ty.sequence_element_type(bcx.tcx());
896 897 898

            let (base, len) = base_datum.get_vec_base_and_len(bcx);

899 900
            debug!("trans_index: base {}", bcx.val_to_string(base));
            debug!("trans_index: len {}", bcx.val_to_string(len));
901

902 903 904 905 906
            let bounds_check = ICmp(bcx,
                                    llvm::IntUGE,
                                    ix_val,
                                    len,
                                    index_expr_debug_loc);
907 908 909
            let expect = ccx.get_intrinsic(&("llvm.expect.i1"));
            let expected = Call(bcx,
                                expect,
N
Nick Cameron 已提交
910
                                &[bounds_check, C_bool(ccx, false)],
911
                                None,
912
                                index_expr_debug_loc);
913 914
            bcx = with_cond(bcx, expected, |bcx| {
                controlflow::trans_fail_bounds_check(bcx,
915
                                                     expr_info(index_expr),
916 917 918
                                                     ix_val,
                                                     len)
            });
N
Nick Cameron 已提交
919
            let elt = InBoundsGEP(bcx, base, &[ix_val]);
920
            let elt = PointerCast(bcx, elt, type_of::type_of(ccx, unit_ty).ptr_to());
921 922
            let lval = Lvalue::new("expr::trans_index fallback");
            Datum::new(elt, unit_ty, LvalueExpr(lval))
923 924
        }
    };
925

926
    DatumBlock::new(bcx, elt_datum)
927 928
}

929
fn trans_def<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
930
                         ref_expr: &hir::Expr,
931 932
                         def: def::Def)
                         -> DatumBlock<'blk, 'tcx, Expr> {
933 934 935 936
    //! Translates a reference to a path.

    let _icx = push_ctxt("trans_def_lvalue");
    match def {
937
        def::DefFn(..) | def::DefMethod(..) |
938
        def::DefStruct(_) | def::DefVariant(..) => {
939 940 941
            let datum = trans_def_fn_unadjusted(bcx.ccx(), ref_expr, def,
                                                bcx.fcx.param_substs);
            DatumBlock::new(bcx, datum.to_expr_datum())
942
        }
943
        def::DefStatic(did, _) => {
944
            let const_ty = expr_ty(bcx, ref_expr);
945
            let val = get_static_val(bcx.ccx(), did, const_ty);
946 947
            let lval = Lvalue::new("expr::trans_def");
            DatumBlock::new(bcx, Datum::new(val, const_ty, LvalueExpr(lval)))
948
        }
949 950 951
        def::DefConst(_) => {
            bcx.sess().span_bug(ref_expr.span,
                "constant expression should not reach expr::trans_def")
952
        }
953
        _ => {
K
Kevin Butler 已提交
954
            DatumBlock::new(bcx, trans_local_var(bcx, def).to_expr_datum())
955 956 957 958
        }
    }
}

959
fn trans_rvalue_stmt_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
960
                                            expr: &hir::Expr)
961
                                            -> Block<'blk, 'tcx> {
962
    let mut bcx = bcx;
963
    let _icx = push_ctxt("trans_rvalue_stmt");
964

965
    if bcx.unreachable.get() {
966 967 968
        return bcx;
    }

969 970
    debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);

971
    match expr.node {
972
        hir::ExprBreak(label_opt) => {
973
            controlflow::trans_break(bcx, expr, label_opt.map(|l| l.node.name))
974
        }
975
        hir::ExprAgain(label_opt) => {
976
            controlflow::trans_cont(bcx, expr, label_opt.map(|l| l.node.name))
977
        }
978
        hir::ExprRet(ref ex) => {
J
James Miller 已提交
979 980 981 982 983 984 985 986 987
            // Check to see if the return expression itself is reachable.
            // This can occur when the inner expression contains a return
            let reachable = if let Some(ref cfg) = bcx.fcx.cfg {
                cfg.node_is_reachable(expr.id)
            } else {
                true
            };

            if reachable {
988
                controlflow::trans_ret(bcx, expr, ex.as_ref().map(|e| &**e))
J
James Miller 已提交
989 990 991 992 993 994
            } else {
                // If it's not reachable, just translate the inner expression
                // directly. This avoids having to manage a return slot when
                // it won't actually be used anyway.
                if let &Some(ref x) = ex {
                    bcx = trans_into(bcx, &**x, Ignore);
J
James Miller 已提交
995
                }
996 997 998 999
                // Mark the end of the block as unreachable. Once we get to
                // a return expression, there's no more we should be doing
                // after this.
                Unreachable(bcx);
J
James Miller 已提交
1000 1001
                bcx
            }
1002
        }
1003
        hir::ExprWhile(ref cond, ref body, _) => {
1004
            controlflow::trans_while(bcx, expr, &**cond, &**body)
1005
        }
1006
        hir::ExprLoop(ref body, _) => {
1007
            controlflow::trans_loop(bcx, expr, &**body)
1008
        }
1009
        hir::ExprAssign(ref dst, ref src) => {
1010
            let src_datum = unpack_datum!(bcx, trans(bcx, &**src));
1011
            let dst_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, &**dst, "assign"));
1012

1013
            if bcx.fcx.type_needs_drop(dst_datum.ty) {
1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029
                // If there are destructors involved, make sure we
                // are copying from an rvalue, since that cannot possible
                // alias an lvalue. We are concerned about code like:
                //
                //   a = a
                //
                // but also
                //
                //   a = a.b
                //
                // where e.g. a : Option<Foo> and a.b :
                // Option<Foo>. In that case, freeing `a` before the
                // assignment may also free `a.b`!
                //
                // We could avoid this intermediary with some analysis
                // to determine whether `dst` may possibly own `src`.
1030
                debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);
1031 1032
                let src_datum = unpack_datum!(
                    bcx, src_datum.to_rvalue_datum(bcx, "ExprAssign"));
1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048
                let opt_hint_datum = dst_datum.kind.drop_flag_info.hint_datum(bcx);
                let opt_hint_val = opt_hint_datum.map(|d|d.to_value());

                // 1. Drop the data at the destination, passing the
                //    drop-hint in case the lvalue has already been
                //    dropped or moved.
                bcx = glue::drop_ty_core(bcx,
                                         dst_datum.val,
                                         dst_datum.ty,
                                         expr.debug_loc(),
                                         false,
                                         opt_hint_val);

                // 2. We are overwriting the destination; ensure that
                //    its drop-hint (if any) says "initialized."
                if let Some(hint_val) = opt_hint_val {
1049
                    let hint_llval = hint_val.value();
1050
                    let drop_needed = C_u8(bcx.fcx.ccx, adt::DTOR_NEEDED_HINT);
1051 1052
                    Store(bcx, drop_needed, hint_llval);
                }
1053 1054
                src_datum.store_to(bcx, dst_datum.val)
            } else {
1055
                src_datum.store_to(bcx, dst_datum.val)
1056
            }
1057
        }
1058
        hir::ExprAssignOp(op, ref dst, ref src) => {
1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072
            let has_method_map = bcx.tcx()
                                    .tables
                                    .borrow()
                                    .method_map
                                    .contains_key(&MethodCall::expr(expr.id));

            if has_method_map {
                let dst = unpack_datum!(bcx, trans(bcx, &**dst));
                let src_datum = unpack_datum!(bcx, trans(bcx, &**src));
                trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id), dst,
                                    Some((src_datum, src.id)), None, false).bcx
            } else {
                trans_assign_op(bcx, expr, op, &**dst, &**src)
            }
1073
        }
1074
        hir::ExprInlineAsm(ref a) => {
1075
            asm::trans_inline_asm(bcx, a)
1076
        }
1077 1078 1079
        _ => {
            bcx.tcx().sess.span_bug(
                expr.span,
J
Jorge Aparicio 已提交
1080
                &format!("trans_rvalue_stmt_unadjusted reached \
1081
                         fall-through case: {:?}",
1082
                        expr.node));
1083
        }
1084
    }
1085 1086
}

1087
fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
1088
                                           expr: &hir::Expr,
1089 1090
                                           dest: Dest)
                                           -> Block<'blk, 'tcx> {
1091
    let _icx = push_ctxt("trans_rvalue_dps_unadjusted");
1092
    let mut bcx = bcx;
1093 1094
    let tcx = bcx.tcx();

1095 1096
    debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);

1097
    match expr.node {
1098
        hir::ExprPath(..) => {
1099
            trans_def_dps_unadjusted(bcx, expr, bcx.def(expr.id), dest)
1100
        }
1101
        hir::ExprIf(ref cond, ref thn, ref els) => {
1102
            controlflow::trans_if(bcx, expr.id, &**cond, &**thn, els.as_ref().map(|e| &**e), dest)
1103
        }
1104
        hir::ExprMatch(ref discr, ref arms, _) => {
1105
            _match::trans_match(bcx, expr, &**discr, &arms[..], dest)
1106
        }
1107
        hir::ExprBlock(ref blk) => {
1108
            controlflow::trans_block(bcx, &**blk, dest)
1109
        }
1110
        hir::ExprStruct(_, ref fields, ref base) => {
1111
            trans_struct(bcx,
1112
                         &fields[..],
1113
                         base.as_ref().map(|e| &**e),
1114 1115
                         expr.span,
                         expr.id,
N
Nick Cameron 已提交
1116
                         node_id_type(bcx, expr.id),
1117
                         dest)
1118
        }
1119
        hir::ExprRange(ref start, ref end) => {
N
Nick Cameron 已提交
1120 1121
            // FIXME it is just not right that we are synthesising ast nodes in
            // trans. Shudder.
1122 1123
            fn make_field(field_name: &str, expr: P<hir::Expr>) -> hir::Field {
                hir::Field {
1124
                    name: codemap::dummy_spanned(token::intern(field_name)),
N
Nick Cameron 已提交
1125 1126 1127 1128 1129 1130
                    expr: expr,
                    span: codemap::DUMMY_SP,
                }
            }

            // A range just desugars into a struct.
1131 1132 1133 1134 1135
            // Note that the type of the start and end may not be the same, but
            // they should only differ in their lifetime, which should not matter
            // in trans.
            let (did, fields, ty_params) = match (start, end) {
                (&Some(ref start), &Some(ref end)) => {
N
Nick Cameron 已提交
1136
                    // Desugar to Range
N
Nick Cameron 已提交
1137 1138
                    let fields = vec![make_field("start", start.clone()),
                                      make_field("end", end.clone())];
1139
                    (tcx.lang_items.range_struct(), fields, vec![node_id_type(bcx, start.id)])
N
Nick Cameron 已提交
1140
                }
1141
                (&Some(ref start), &None) => {
N
Nick Cameron 已提交
1142
                    // Desugar to RangeFrom
N
Nick Cameron 已提交
1143
                    let fields = vec![make_field("start", start.clone())];
1144 1145 1146 1147
                    (tcx.lang_items.range_from_struct(), fields, vec![node_id_type(bcx, start.id)])
                }
                (&None, &Some(ref end)) => {
                    // Desugar to RangeTo
N
Nick Cameron 已提交
1148
                    let fields = vec![make_field("end", end.clone())];
1149 1150 1151
                    (tcx.lang_items.range_to_struct(), fields, vec![node_id_type(bcx, end.id)])
                }
                _ => {
N
Nick Cameron 已提交
1152 1153
                    // Desugar to RangeFull
                    (tcx.lang_items.range_full_struct(), vec![], vec![])
N
Nick Cameron 已提交
1154 1155 1156 1157
                }
            };

            if let Some(did) = did {
1158
                let substs = Substs::new_type(ty_params, vec![]);
N
Nick Cameron 已提交
1159
                trans_struct(bcx,
1160
                             &fields,
N
Nick Cameron 已提交
1161 1162 1163
                             None,
                             expr.span,
                             expr.id,
1164 1165
                             tcx.mk_struct(tcx.lookup_adt_def(did),
                                           tcx.mk_substs(substs)),
N
Nick Cameron 已提交
1166 1167 1168 1169 1170 1171
                             dest)
            } else {
                tcx.sess.span_bug(expr.span,
                                  "No lang item for ranges (how did we get this far?)")
            }
        }
1172 1173
        hir::ExprTup(ref args) => {
            let numbered_fields: Vec<(usize, &hir::Expr)> =
1174
                args.iter().enumerate().map(|(i, arg)| (i, &**arg)).collect();
1175 1176 1177
            trans_adt(bcx,
                      expr_ty(bcx, expr),
                      0,
1178
                      &numbered_fields[..],
1179 1180
                      None,
                      dest,
1181
                      expr.debug_loc())
1182
        }
1183
        hir::ExprLit(ref lit) => {
1184
            match lit.node {
1185
                ast::LitStr(ref s, _) => {
1186
                    tvec::trans_lit_str(bcx, expr, (*s).clone(), dest)
1187 1188 1189 1190 1191 1192 1193 1194 1195
                }
                _ => {
                    bcx.tcx()
                       .sess
                       .span_bug(expr.span,
                                 "trans_rvalue_dps_unadjusted shouldn't be \
                                  translating this type of literal")
                }
            }
1196
        }
1197
        hir::ExprVec(..) | hir::ExprRepeat(..) => {
N
Nick Cameron 已提交
1198
            tvec::trans_fixed_vstore(bcx, expr, dest)
1199
        }
1200
        hir::ExprClosure(_, ref decl, ref body) => {
1201 1202 1203 1204
            let dest = match dest {
                SaveIn(lldest) => closure::Dest::SaveIn(bcx, lldest),
                Ignore => closure::Dest::Ignore(bcx.ccx())
            };
1205 1206 1207 1208 1209 1210 1211 1212 1213 1214

            // NB. To get the id of the closure, we don't use
            // `local_def_id(id)`, but rather we extract the closure
            // def-id from the expr's type. This is because this may
            // be an inlined expression from another crate, and we
            // want to get the ORIGINAL closure def-id, since that is
            // the key we need to find the closure-kind and
            // closure-type etc.
            let (def_id, substs) = match expr_ty(bcx, expr).sty {
                ty::TyClosure(def_id, ref substs) => (def_id, substs),
1215 1216 1217 1218 1219
                ref t =>
                    bcx.tcx().sess.span_bug(
                        expr.span,
                        &format!("closure expr without closure type: {:?}", t)),
            };
1220 1221

            closure::trans_closure_expr(dest, decl, body, expr.id, def_id, substs).unwrap_or(bcx)
1222
        }
1223
        hir::ExprCall(ref f, ref args) => {
1224 1225 1226
            if bcx.tcx().is_method_call(expr.id) {
                trans_overloaded_call(bcx,
                                      expr,
1227
                                      &**f,
1228
                                      &args[..],
1229 1230 1231 1232
                                      Some(dest))
            } else {
                callee::trans_call(bcx,
                                   expr,
1233
                                   &**f,
1234
                                   callee::ArgExprs(&args[..]),
1235 1236
                                   dest)
            }
1237
        }
1238
        hir::ExprMethodCall(_, _, ref args) => {
1239 1240
            callee::trans_method_call(bcx,
                                      expr,
1241
                                      &*args[0],
1242
                                      callee::ArgExprs(&args[..]),
1243
                                      dest)
1244
        }
1245
        hir::ExprBinary(op, ref lhs, ref rhs) => {
1246
            // if not overloaded, would be RvalueDatumExpr
1247 1248
            let lhs = unpack_datum!(bcx, trans(bcx, &**lhs));
            let rhs_datum = unpack_datum!(bcx, trans(bcx, &**rhs));
1249
            trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id), lhs,
1250
                                Some((rhs_datum, rhs.id)), Some(dest),
1251
                                !rustc_front::util::is_by_value_binop(op.node)).bcx
1252
        }
1253
        hir::ExprUnary(op, ref subexpr) => {
1254
            // if not overloaded, would be RvalueDatumExpr
1255
            let arg = unpack_datum!(bcx, trans(bcx, &**subexpr));
1256
            trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id),
1257
                                arg, None, Some(dest), !rustc_front::util::is_by_value_unop(op)).bcx
1258
        }
1259
        hir::ExprIndex(ref base, ref idx) => {
1260
            // if not overloaded, would be RvalueDatumExpr
1261 1262
            let base = unpack_datum!(bcx, trans(bcx, &**base));
            let idx_datum = unpack_datum!(bcx, trans(bcx, &**idx));
1263
            trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id), base,
1264
                                Some((idx_datum, idx.id)), Some(dest), true).bcx
1265
        }
1266
        hir::ExprCast(..) => {
N
Nick Cameron 已提交
1267 1268
            // Trait casts used to come this way, now they should be coercions.
            bcx.tcx().sess.span_bug(expr.span, "DPS expr_cast (residual trait cast?)")
1269
        }
1270 1271 1272 1273 1274
        hir::ExprAssignOp(op, _, _) => {
            bcx.tcx().sess.span_bug(
                expr.span,
                &format!("augmented assignment `{}=` should always be a rvalue_stmt",
                         rustc_front::util::binop_to_string(op.node)))
1275 1276 1277 1278
        }
        _ => {
            bcx.tcx().sess.span_bug(
                expr.span,
J
Jorge Aparicio 已提交
1279
                &format!("trans_rvalue_dps_unadjusted reached fall-through \
1280
                         case: {:?}",
1281
                        expr.node));
1282 1283 1284 1285
        }
    }
}

1286
fn trans_def_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
1287
                                        ref_expr: &hir::Expr,
1288 1289 1290
                                        def: def::Def,
                                        dest: Dest)
                                        -> Block<'blk, 'tcx> {
1291
    let _icx = push_ctxt("trans_def_dps_unadjusted");
1292 1293 1294 1295 1296 1297 1298

    let lldest = match dest {
        SaveIn(lldest) => lldest,
        Ignore => { return bcx; }
    };

    match def {
1299
        def::DefVariant(tid, vid, _) => {
1300 1301
            let variant = bcx.tcx().lookup_adt_def(tid).variant_with_id(vid);
            if let ty::VariantKind::Tuple = variant.kind() {
1302
                // N-ary variant.
1303 1304 1305
                let llfn = callee::trans_fn_ref(bcx.ccx(), vid,
                                                ExprId(ref_expr.id),
                                                bcx.fcx.param_substs).val;
1306
                Store(bcx, llfn, lldest);
1307
                return bcx;
1308
            } else {
1309 1310
                // Nullary variant.
                let ty = expr_ty(bcx, ref_expr);
1311
                let repr = adt::represent_type(bcx.ccx(), ty);
1312
                adt::trans_set_discr(bcx, &*repr, lldest, variant.disr_val);
1313
                return bcx;
1314 1315
            }
        }
1316
        def::DefStruct(_) => {
1317
            let ty = expr_ty(bcx, ref_expr);
1318
            match ty.sty {
1319
                ty::TyStruct(def, _) if def.has_dtor() => {
1320
                    let repr = adt::represent_type(bcx.ccx(), ty);
1321
                    adt::trans_set_discr(bcx, &*repr, lldest, 0);
1322
                }
1323
                _ => {}
1324
            }
1325
            bcx
1326
        }
1327
        _ => {
J
Jorge Aparicio 已提交
1328
            bcx.tcx().sess.span_bug(ref_expr.span, &format!(
1329
                "Non-DPS def {:?} referened by {}",
1330
                def, bcx.node_id_to_string(ref_expr.id)));
1331 1332 1333 1334
        }
    }
}

1335
pub fn trans_def_fn_unadjusted<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
1336
                                         ref_expr: &hir::Expr,
1337
                                         def: def::Def,
N
Nick Cameron 已提交
1338
                                         param_substs: &'tcx Substs<'tcx>)
1339
                                         -> Datum<'tcx, Rvalue> {
1340
    let _icx = push_ctxt("trans_def_datum_unadjusted");
1341

1342
    match def {
1343
        def::DefFn(did, _) |
1344
        def::DefStruct(did) | def::DefVariant(_, did, _) => {
1345
            callee::trans_fn_ref(ccx, did, ExprId(ref_expr.id), param_substs)
1346
        }
1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359
        def::DefMethod(method_did) => {
            match ccx.tcx().impl_or_trait_item(method_did).container() {
                ty::ImplContainer(_) => {
                    callee::trans_fn_ref(ccx, method_did,
                                         ExprId(ref_expr.id),
                                         param_substs)
                }
                ty::TraitContainer(trait_did) => {
                    meth::trans_static_method_callee(ccx, method_did,
                                                     trait_did, ref_expr.id,
                                                     param_substs)
                }
            }
1360 1361
        }
        _ => {
J
Jorge Aparicio 已提交
1362
            ccx.tcx().sess.span_bug(ref_expr.span, &format!(
1363
                    "trans_def_fn_unadjusted invoked on: {:?} for {:?}",
1364
                    def,
1365
                    ref_expr));
1366
        }
1367
    }
1368 1369
}

S
Steve Klabnik 已提交
1370
/// Translates a reference to a local variable or argument. This always results in an lvalue datum.
1371 1372
pub fn trans_local_var<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                   def: def::Def)
1373
                                   -> Datum<'tcx, Lvalue> {
1374
    let _icx = push_ctxt("trans_local_var");
1375

1376
    match def {
1377
        def::DefUpvar(_, nid, _, _) => {
1378
            // Can't move upvars, so this is never a ZeroMemLastUse.
1379
            let local_ty = node_id_type(bcx, nid);
1380 1381
            let lval = Lvalue::new_with_hint("expr::trans_local_var (upvar)",
                                             bcx, nid, HintKind::ZeroAndMaintain);
1382
            match bcx.fcx.llupvars.borrow().get(&nid) {
1383
                Some(&val) => Datum::new(val, local_ty, lval),
1384
                None => {
J
Jorge Aparicio 已提交
1385
                    bcx.sess().bug(&format!(
L
Luqman Aden 已提交
1386
                        "trans_local_var: no llval for upvar {} found",
1387
                        nid));
1388 1389 1390
                }
            }
        }
1391
        def::DefLocal(_, nid) => {
1392
            let datum = match bcx.fcx.lllocals.borrow().get(&nid) {
1393 1394
                Some(&v) => v,
                None => {
J
Jorge Aparicio 已提交
1395
                    bcx.sess().bug(&format!(
L
Luqman Aden 已提交
1396
                        "trans_local_var: no datum for local/arg {} found",
1397
                        nid));
1398 1399
                }
            };
L
Luqman Aden 已提交
1400
            debug!("take_local(nid={}, v={}, ty={})",
1401
                   nid, bcx.val_to_string(datum.val), datum.ty);
1402
            datum
1403 1404
        }
        _ => {
J
Jorge Aparicio 已提交
1405
            bcx.sess().unimpl(&format!(
1406
                "unsupported def type in trans_local_var: {:?}",
1407
                def));
1408 1409 1410 1411
        }
    }
}

1412
fn trans_struct<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
1413 1414
                            fields: &[hir::Field],
                            base: Option<&hir::Expr>,
1415
                            expr_span: codemap::Span,
1416
                            expr_id: ast::NodeId,
N
Nick Cameron 已提交
1417
                            ty: Ty<'tcx>,
1418
                            dest: Dest) -> Block<'blk, 'tcx> {
1419
    let _icx = push_ctxt("trans_rec");
1420 1421

    let tcx = bcx.tcx();
1422 1423 1424 1425 1426
    let vinfo = VariantInfo::of_node(tcx, ty, expr_id);

    let mut need_base = vec![true; vinfo.fields.len()];

    let numbered_fields = fields.iter().map(|field| {
1427
        let pos = vinfo.field_index(field.name.node);
1428 1429 1430 1431 1432 1433 1434 1435 1436 1437
        need_base[pos] = false;
        (pos, &*field.expr)
    }).collect::<Vec<_>>();

    let optbase = match base {
        Some(base_expr) => {
            let mut leftovers = Vec::new();
            for (i, b) in need_base.iter().enumerate() {
                if *b {
                    leftovers.push((i, vinfo.fields[i].1));
1438
                }
1439
            }
1440 1441 1442 1443 1444 1445
            Some(StructBaseInfo {expr: base_expr,
                                 fields: leftovers })
        }
        None => {
            if need_base.iter().any(|b| *b) {
                tcx.sess.span_bug(expr_span, "missing fields and no base expr")
1446
            }
1447 1448 1449
            None
        }
    };
1450

1451 1452 1453 1454 1455 1456 1457
    trans_adt(bcx,
              ty,
              vinfo.discr,
              &numbered_fields,
              optbase,
              dest,
              DebugLoc::At(expr_id, expr_span))
1458 1459
}

S
Steve Klabnik 已提交
1460 1461 1462 1463 1464 1465
/// Information that `trans_adt` needs in order to fill in the fields
/// of a struct copied from a base struct (e.g., from an expression
/// like `Foo { a: b, ..base }`.
///
/// Note that `fields` may be empty; the base expression must always be
/// evaluated for side-effects.
1466
pub struct StructBaseInfo<'a, 'tcx> {
1467
    /// The base expression; will be evaluated after all explicit fields.
1468
    expr: &'a hir::Expr,
1469
    /// The indices of fields to copy paired with their types.
1470
    fields: Vec<(usize, Ty<'tcx>)>
1471
}
1472

S
Steve Klabnik 已提交
1473 1474 1475 1476 1477 1478 1479 1480
/// Constructs an ADT instance:
///
/// - `fields` should be a list of field indices paired with the
/// expression to store into that field.  The initializers will be
/// evaluated in the order specified by `fields`.
///
/// - `optbase` contains information on the base struct (if any) from
/// which remaining fields are copied; see comments on `StructBaseInfo`.
1481 1482 1483
pub fn trans_adt<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                                 ty: Ty<'tcx>,
                                 discr: ty::Disr,
1484
                                 fields: &[(usize, &hir::Expr)],
1485 1486
                                 optbase: Option<StructBaseInfo<'a, 'tcx>>,
                                 dest: Dest,
1487
                                 debug_location: DebugLoc)
1488
                                 -> Block<'blk, 'tcx> {
1489
    let _icx = push_ctxt("trans_adt");
1490
    let fcx = bcx.fcx;
1491 1492
    let repr = adt::represent_type(bcx.ccx(), ty);

1493
    debug_location.apply(bcx.fcx);
1494

1495 1496
    // If we don't care about the result, just make a
    // temporary stack slot
1497
    let addr = match dest {
1498
        SaveIn(pos) => pos,
1499 1500 1501 1502 1503
        Ignore => {
            let llresult = alloc_ty(bcx, ty, "temp");
            call_lifetime_start(bcx, llresult);
            llresult
        }
1504
    };
1505 1506

    // This scope holds intermediates that must be cleaned should
S
Steve Klabnik 已提交
1507
    // panic occur before the ADT as a whole is ready.
1508 1509
    let custom_cleanup_scope = fcx.push_custom_cleanup_scope();

A
Ariel Ben-Yehuda 已提交
1510
    if ty.is_simd() {
1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548
        // Issue 23112: The original logic appeared vulnerable to same
        // order-of-eval bug. But, SIMD values are tuple-structs;
        // i.e. functional record update (FRU) syntax is unavailable.
        //
        // To be safe, double-check that we did not get here via FRU.
        assert!(optbase.is_none());

        // This is the constructor of a SIMD type, such types are
        // always primitive machine types and so do not have a
        // destructor or require any clean-up.
        let llty = type_of::type_of(bcx.ccx(), ty);

        // keep a vector as a register, and running through the field
        // `insertelement`ing them directly into that register
        // (i.e. avoid GEPi and `store`s to an alloca) .
        let mut vec_val = C_undef(llty);

        for &(i, ref e) in fields {
            let block_datum = trans(bcx, &**e);
            bcx = block_datum.bcx;
            let position = C_uint(bcx.ccx(), i);
            let value = block_datum.datum.to_llscalarish(bcx);
            vec_val = InsertElement(bcx, vec_val, value, position);
        }
        Store(bcx, vec_val, addr);
    } else if let Some(base) = optbase {
        // Issue 23112: If there is a base, then order-of-eval
        // requires field expressions eval'ed before base expression.

        // First, trans field expressions to temporary scratch values.
        let scratch_vals: Vec<_> = fields.iter().map(|&(i, ref e)| {
            let datum = unpack_datum!(bcx, trans(bcx, &**e));
            (i, datum)
        }).collect();

        debug_location.apply(bcx.fcx);

        // Second, trans the base to the dest.
1549 1550
        assert_eq!(discr, 0);

A
Ariel Ben-Yehuda 已提交
1551 1552
        match expr_kind(bcx.tcx(), &*base.expr) {
            ExprKind::RvalueDps | ExprKind::RvalueDatum if !bcx.fcx.type_needs_drop(ty) => {
1553 1554
                bcx = trans_into(bcx, &*base.expr, SaveIn(addr));
            },
A
Ariel Ben-Yehuda 已提交
1555 1556 1557
            ExprKind::RvalueStmt => {
                bcx.tcx().sess.bug("unexpected expr kind for struct base expr")
            }
1558
            _ => {
1559
                let base_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, &*base.expr, "base"));
1560
                for &(i, t) in &base.fields {
1561
                    let datum = base_datum.get_element(
N
Nick Cameron 已提交
1562
                            bcx, t, |srcval| adt::trans_field_ptr(bcx, &*repr, srcval, discr, i));
1563
                    assert!(type_is_sized(bcx.tcx(), datum.ty));
1564
                    let dest = adt::trans_field_ptr(bcx, &*repr, addr, discr, i);
1565 1566
                    bcx = datum.store_to(bcx, dest);
                }
1567
            }
1568
        }
1569

1570
        // Finally, move scratch field values into actual field locations
1571
        for (i, datum) in scratch_vals {
1572 1573
            let dest = adt::trans_field_ptr(bcx, &*repr, addr, discr, i);
            bcx = datum.store_to(bcx, dest);
1574 1575
        }
    } else {
1576
        // No base means we can write all fields directly in place.
1577
        for &(i, ref e) in fields {
1578 1579 1580 1581 1582
            let dest = adt::trans_field_ptr(bcx, &*repr, addr, discr, i);
            let e_ty = expr_ty_adjusted(bcx, &**e);
            bcx = trans_into(bcx, &**e, SaveIn(dest));
            let scope = cleanup::CustomScope(custom_cleanup_scope);
            fcx.schedule_lifetime_end(scope, dest);
1583 1584
            // FIXME: nonzeroing move should generalize to fields
            fcx.schedule_drop_mem(scope, dest, e_ty, None);
1585
        }
1586
    }
1587

1588
    adt::trans_set_discr(bcx, &*repr, addr, discr);
1589

1590 1591
    fcx.pop_custom_cleanup_scope(custom_cleanup_scope);

1592 1593 1594 1595
    // If we don't care about the result drop the temporary we made
    match dest {
        SaveIn(_) => bcx,
        Ignore => {
1596
            bcx = glue::drop_ty(bcx, addr, ty, debug_location);
1597 1598 1599 1600
            base::call_lifetime_end(bcx, addr);
            bcx
        }
    }
1601 1602
}

1603

1604
fn trans_immediate_lit<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
1605
                                   expr: &hir::Expr,
1606
                                   lit: &ast::Lit)
1607
                                   -> DatumBlock<'blk, 'tcx, Expr> {
1608
    // must not be a string constant, that is a RvalueDpsExpr
1609
    let _icx = push_ctxt("trans_immediate_lit");
1610
    let ty = expr_ty(bcx, expr);
1611 1612
    let v = consts::const_lit(bcx.ccx(), expr, lit);
    immediate_rvalue_bcx(bcx, v, ty).to_expr_datumblock()
1613 1614
}

1615
fn trans_unary<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
1616 1617 1618
                           expr: &hir::Expr,
                           op: hir::UnOp,
                           sub_expr: &hir::Expr)
1619
                           -> DatumBlock<'blk, 'tcx, Expr> {
1620
    let ccx = bcx.ccx();
1621
    let mut bcx = bcx;
1622
    let _icx = push_ctxt("trans_unary_datum");
1623

1624
    let method_call = MethodCall::expr(expr.id);
1625 1626 1627 1628

    // The only overloaded operator that is translated to a datum
    // is an overloaded deref, since it is always yields a `&T`.
    // Otherwise, we should be in the RvalueDpsExpr path.
1629
    assert!(op == hir::UnDeref || !ccx.tcx().is_method_call(expr.id));
1630

1631
    let un_ty = expr_ty(bcx, expr);
1632

1633 1634
    let debug_loc = expr.debug_loc();

1635
    match op {
1636
        hir::UnNot => {
1637
            let datum = unpack_datum!(bcx, trans(bcx, sub_expr));
1638
            let llresult = Not(bcx, datum.to_llscalarish(bcx), debug_loc);
1639
            immediate_rvalue_bcx(bcx, llresult, un_ty).to_expr_datumblock()
1640
        }
1641
        hir::UnNeg => {
1642 1643
            let datum = unpack_datum!(bcx, trans(bcx, sub_expr));
            let val = datum.to_llscalarish(bcx);
1644
            let (bcx, llneg) = {
1645
                if un_ty.is_fp() {
1646 1647
                    let result = FNeg(bcx, val, debug_loc);
                    (bcx, result)
1648
                } else {
1649
                    let is_signed = un_ty.is_signed();
1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663
                    let result = Neg(bcx, val, debug_loc);
                    let bcx = if bcx.ccx().check_overflow() && is_signed {
                        let (llty, min) = base::llty_and_min_for_signed_ty(bcx, un_ty);
                        let is_min = ICmp(bcx, llvm::IntEQ, val,
                                          C_integral(llty, min, true), debug_loc);
                        with_cond(bcx, is_min, |bcx| {
                            let msg = InternedString::new(
                                "attempted to negate with overflow");
                            controlflow::trans_fail(bcx, expr_info(expr), msg)
                        })
                    } else {
                        bcx
                    };
                    (bcx, result)
1664 1665
                }
            };
1666
            immediate_rvalue_bcx(bcx, llneg, un_ty).to_expr_datumblock()
1667
        }
1668
        hir::UnDeref => {
1669
            let datum = unpack_datum!(bcx, trans(bcx, sub_expr));
1670
            deref_once(bcx, expr, datum, method_call)
1671
        }
1672
    }
1673
}
1674

1675
fn trans_uniq_expr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
1676
                               box_expr: &hir::Expr,
1677
                               box_ty: Ty<'tcx>,
1678
                               contents: &hir::Expr,
1679
                               contents_ty: Ty<'tcx>)
1680
                               -> DatumBlock<'blk, 'tcx, Expr> {
E
Eduard Burtescu 已提交
1681
    let _icx = push_ctxt("trans_uniq_expr");
1682
    let fcx = bcx.fcx;
1683
    assert!(type_is_sized(bcx.tcx(), contents_ty));
E
Eduard Burtescu 已提交
1684 1685
    let llty = type_of::type_of(bcx.ccx(), contents_ty);
    let size = llsize_of(bcx.ccx(), llty);
1686
    let align = C_uint(bcx.ccx(), type_of::align_of(bcx.ccx(), contents_ty));
N
Nick Cameron 已提交
1687
    let llty_ptr = llty.ptr_to();
1688 1689 1690 1691 1692 1693
    let Result { bcx, val } = malloc_raw_dyn(bcx,
                                             llty_ptr,
                                             box_ty,
                                             size,
                                             align,
                                             box_expr.debug_loc());
1694 1695 1696
    // Unique boxes do not allocate for zero-size types. The standard library
    // may assume that `free` is never called on the pointer returned for
    // `Box<ZeroSizeType>`.
E
Eduard Burtescu 已提交
1697 1698
    let bcx = if llsize_of_alloc(bcx.ccx(), llty) == 0 {
        trans_into(bcx, contents, SaveIn(val))
1699
    } else {
1700 1701
        let custom_cleanup_scope = fcx.push_custom_cleanup_scope();
        fcx.schedule_free_value(cleanup::CustomScope(custom_cleanup_scope),
1702
                                val, cleanup::HeapExchange, contents_ty);
E
Eduard Burtescu 已提交
1703
        let bcx = trans_into(bcx, contents, SaveIn(val));
1704
        fcx.pop_custom_cleanup_scope(custom_cleanup_scope);
E
Eduard Burtescu 已提交
1705 1706 1707 1708 1709
        bcx
    };
    immediate_rvalue_bcx(bcx, val, box_ty).to_expr_datumblock()
}

1710
fn trans_addr_of<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
1711 1712
                             expr: &hir::Expr,
                             subexpr: &hir::Expr)
1713
                             -> DatumBlock<'blk, 'tcx, Expr> {
1714
    let _icx = push_ctxt("trans_addr_of");
1715
    let mut bcx = bcx;
1716
    let sub_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, subexpr, "addr_of"));
1717
    let ty = expr_ty(bcx, expr);
1718
    if !type_is_sized(bcx.tcx(), sub_datum.ty) {
1719 1720 1721
        // Always generate an lvalue datum, because this pointer doesn't own
        // the data and cleanup is scheduled elsewhere.
        DatumBlock::new(bcx, Datum::new(sub_datum.val, ty, LvalueExpr(sub_datum.kind)))
1722 1723 1724
    } else {
        // Sized value, ref to a thin pointer
        immediate_rvalue_bcx(bcx, sub_datum.val, ty).to_expr_datumblock()
N
Nick Cameron 已提交
1725
    }
1726 1727
}

A
Ariel Ben-Yehuda 已提交
1728 1729 1730 1731 1732 1733 1734 1735 1736
fn trans_scalar_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                  binop_expr: &hir::Expr,
                                  binop_ty: Ty<'tcx>,
                                  op: hir::BinOp,
                                  lhs: Datum<'tcx, Rvalue>,
                                  rhs: Datum<'tcx, Rvalue>)
                                  -> DatumBlock<'blk, 'tcx, Expr>
{
    let _icx = push_ctxt("trans_scalar_binop");
1737

1738
    let tcx = bcx.tcx();
A
Ariel Ben-Yehuda 已提交
1739
    let lhs_t = lhs.ty;
1740 1741 1742
    assert!(!lhs_t.is_simd());
    let is_float = lhs_t.is_fp();
    let is_signed = lhs_t.is_signed();
1743
    let info = expr_info(binop_expr);
1744

1745 1746
    let binop_debug_loc = binop_expr.debug_loc();

1747
    let mut bcx = bcx;
A
Ariel Ben-Yehuda 已提交
1748 1749
    let lhs = lhs.to_llscalarish(bcx);
    let rhs = rhs.to_llscalarish(bcx);
1750
    let val = match op.node {
1751
      hir::BiAdd => {
1752 1753 1754
        if is_float {
            FAdd(bcx, lhs, rhs, binop_debug_loc)
        } else {
1755 1756 1757 1758
            let (newbcx, res) = with_overflow_check(
                bcx, OverflowOp::Add, info, lhs_t, lhs, rhs, binop_debug_loc);
            bcx = newbcx;
            res
1759
        }
1760
      }
1761
      hir::BiSub => {
1762 1763 1764
        if is_float {
            FSub(bcx, lhs, rhs, binop_debug_loc)
        } else {
1765 1766 1767 1768
            let (newbcx, res) = with_overflow_check(
                bcx, OverflowOp::Sub, info, lhs_t, lhs, rhs, binop_debug_loc);
            bcx = newbcx;
            res
1769
        }
1770
      }
1771
      hir::BiMul => {
1772 1773 1774
        if is_float {
            FMul(bcx, lhs, rhs, binop_debug_loc)
        } else {
1775 1776 1777 1778
            let (newbcx, res) = with_overflow_check(
                bcx, OverflowOp::Mul, info, lhs_t, lhs, rhs, binop_debug_loc);
            bcx = newbcx;
            res
1779
        }
1780
      }
1781
      hir::BiDiv => {
1782
        if is_float {
1783
            FDiv(bcx, lhs, rhs, binop_debug_loc)
1784 1785
        } else {
            // Only zero-check integers; fp /0 is NaN
1786 1787 1788 1789 1790
            bcx = base::fail_if_zero_or_overflows(bcx,
                                                  expr_info(binop_expr),
                                                  op,
                                                  lhs,
                                                  rhs,
A
Ariel Ben-Yehuda 已提交
1791
                                                  lhs_t);
1792
            if is_signed {
1793
                SDiv(bcx, lhs, rhs, binop_debug_loc)
1794
            } else {
1795
                UDiv(bcx, lhs, rhs, binop_debug_loc)
1796 1797 1798
            }
        }
      }
1799
      hir::BiRem => {
1800
        if is_float {
1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837
            // LLVM currently always lowers the `frem` instructions appropriate
            // library calls typically found in libm. Notably f64 gets wired up
            // to `fmod` and f32 gets wired up to `fmodf`. Inconveniently for
            // us, 32-bit MSVC does not actually have a `fmodf` symbol, it's
            // instead just an inline function in a header that goes up to a
            // f64, uses `fmod`, and then comes back down to a f32.
            //
            // Although LLVM knows that `fmodf` doesn't exist on MSVC, it will
            // still unconditionally lower frem instructions over 32-bit floats
            // to a call to `fmodf`. To work around this we special case MSVC
            // 32-bit float rem instructions and instead do the call out to
            // `fmod` ourselves.
            //
            // Note that this is currently duplicated with src/libcore/ops.rs
            // which does the same thing, and it would be nice to perhaps unify
            // these two implementations on day! Also note that we call `fmod`
            // for both 32 and 64-bit floats because if we emit any FRem
            // instruction at all then LLVM is capable of optimizing it into a
            // 32-bit FRem (which we're trying to avoid).
            let use_fmod = tcx.sess.target.target.options.is_like_msvc &&
                           tcx.sess.target.target.arch == "x86";
            if use_fmod {
                let f64t = Type::f64(bcx.ccx());
                let fty = Type::func(&[f64t, f64t], &f64t);
                let llfn = declare::declare_cfn(bcx.ccx(), "fmod", fty,
                                                tcx.types.f64);
                if lhs_t == tcx.types.f32 {
                    let lhs = FPExt(bcx, lhs, f64t);
                    let rhs = FPExt(bcx, rhs, f64t);
                    let res = Call(bcx, llfn, &[lhs, rhs], None, binop_debug_loc);
                    FPTrunc(bcx, res, Type::f32(bcx.ccx()))
                } else {
                    Call(bcx, llfn, &[lhs, rhs], None, binop_debug_loc)
                }
            } else {
                FRem(bcx, lhs, rhs, binop_debug_loc)
            }
1838 1839
        } else {
            // Only zero-check integers; fp %0 is NaN
1840 1841
            bcx = base::fail_if_zero_or_overflows(bcx,
                                                  expr_info(binop_expr),
A
Ariel Ben-Yehuda 已提交
1842
                                                  op, lhs, rhs, lhs_t);
1843
            if is_signed {
1844
                SRem(bcx, lhs, rhs, binop_debug_loc)
1845
            } else {
1846
                URem(bcx, lhs, rhs, binop_debug_loc)
1847 1848 1849
            }
        }
      }
1850 1851 1852 1853
      hir::BiBitOr => Or(bcx, lhs, rhs, binop_debug_loc),
      hir::BiBitAnd => And(bcx, lhs, rhs, binop_debug_loc),
      hir::BiBitXor => Xor(bcx, lhs, rhs, binop_debug_loc),
      hir::BiShl => {
1854 1855 1856 1857 1858
          let (newbcx, res) = with_overflow_check(
              bcx, OverflowOp::Shl, info, lhs_t, lhs, rhs, binop_debug_loc);
          bcx = newbcx;
          res
      }
1859
      hir::BiShr => {
1860 1861 1862 1863
          let (newbcx, res) = with_overflow_check(
              bcx, OverflowOp::Shr, info, lhs_t, lhs, rhs, binop_debug_loc);
          bcx = newbcx;
          res
1864
      }
1865
      hir::BiEq | hir::BiNe | hir::BiLt | hir::BiGe | hir::BiLe | hir::BiGt => {
1866
          base::compare_scalar_types(bcx, lhs, rhs, lhs_t, op.node, binop_debug_loc)
1867
      }
1868
      _ => {
J
Jeong YunWon 已提交
1869
        bcx.tcx().sess.span_bug(binop_expr.span, "unexpected binop");
1870 1871 1872
      }
    };

1873
    immediate_rvalue_bcx(bcx, val, binop_ty).to_expr_datumblock()
1874 1875 1876
}

// refinement types would obviate the need for this
1877 1878 1879 1880
enum lazy_binop_ty {
    lazy_and,
    lazy_or,
}
1881

1882
fn trans_lazy_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
1883
                                binop_expr: &hir::Expr,
1884
                                op: lazy_binop_ty,
1885 1886
                                a: &hir::Expr,
                                b: &hir::Expr)
1887
                                -> DatumBlock<'blk, 'tcx, Expr> {
1888
    let _icx = push_ctxt("trans_lazy_binop");
1889
    let binop_ty = expr_ty(bcx, binop_expr);
1890
    let fcx = bcx.fcx;
1891

1892 1893
    let DatumBlock {bcx: past_lhs, datum: lhs} = trans(bcx, a);
    let lhs = lhs.to_llscalarish(past_lhs);
1894

1895
    if past_lhs.unreachable.get() {
1896
        return immediate_rvalue_bcx(past_lhs, lhs, binop_ty).to_expr_datumblock();
1897 1898
    }

1899 1900
    let join = fcx.new_id_block("join", binop_expr.id);
    let before_rhs = fcx.new_id_block("before_rhs", b.id);
1901 1902

    match op {
1903 1904
      lazy_and => CondBr(past_lhs, lhs, before_rhs.llbb, join.llbb, DebugLoc::None),
      lazy_or => CondBr(past_lhs, lhs, join.llbb, before_rhs.llbb, DebugLoc::None)
1905
    }
1906

1907 1908
    let DatumBlock {bcx: past_rhs, datum: rhs} = trans(before_rhs, b);
    let rhs = rhs.to_llscalarish(past_rhs);
1909

1910
    if past_rhs.unreachable.get() {
1911
        return immediate_rvalue_bcx(join, lhs, binop_ty).to_expr_datumblock();
1912 1913
    }

1914
    Br(past_rhs, join.llbb, DebugLoc::None);
N
Nick Cameron 已提交
1915 1916
    let phi = Phi(join, Type::i1(bcx.ccx()), &[lhs, rhs],
                  &[past_lhs.llbb, past_rhs.llbb]);
1917

1918
    return immediate_rvalue_bcx(join, phi, binop_ty).to_expr_datumblock();
1919 1920
}

1921
fn trans_binary<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
1922 1923 1924 1925
                            expr: &hir::Expr,
                            op: hir::BinOp,
                            lhs: &hir::Expr,
                            rhs: &hir::Expr)
1926
                            -> DatumBlock<'blk, 'tcx, Expr> {
1927
    let _icx = push_ctxt("trans_binary");
1928
    let ccx = bcx.ccx();
1929

1930
    // if overloaded, would be RvalueDpsExpr
1931
    assert!(!ccx.tcx().is_method_call(expr.id));
1932

1933
    match op.node {
1934
        hir::BiAnd => {
1935
            trans_lazy_binop(bcx, expr, lazy_and, lhs, rhs)
1936
        }
1937
        hir::BiOr => {
1938
            trans_lazy_binop(bcx, expr, lazy_or, lhs, rhs)
1939 1940 1941
        }
        _ => {
            let mut bcx = bcx;
1942
            let binop_ty = expr_ty(bcx, expr);
1943

A
Ariel Ben-Yehuda 已提交
1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955
            let lhs = unpack_datum!(bcx, trans(bcx, lhs));
            let lhs = unpack_datum!(bcx, lhs.to_rvalue_datum(bcx, "binop_lhs"));
            debug!("trans_binary (expr {}): lhs={}",
                   expr.id, lhs.to_string(ccx));
            let rhs = unpack_datum!(bcx, trans(bcx, rhs));
            let rhs = unpack_datum!(bcx, rhs.to_rvalue_datum(bcx, "binop_rhs"));
            debug!("trans_binary (expr {}): rhs={}",
                   expr.id, rhs.to_string(ccx));

            if type_is_fat_ptr(ccx.tcx(), lhs.ty) {
                assert!(type_is_fat_ptr(ccx.tcx(), rhs.ty),
                        "built-in binary operators on fat pointers are homogeneous");
1956 1957 1958 1959 1960 1961 1962 1963 1964
                assert_eq!(binop_ty, bcx.tcx().types.bool);
                let val = base::compare_scalar_types(
                    bcx,
                    lhs.val,
                    rhs.val,
                    lhs.ty,
                    op.node,
                    expr.debug_loc());
                immediate_rvalue_bcx(bcx, val, binop_ty).to_expr_datumblock()
A
Ariel Ben-Yehuda 已提交
1965 1966 1967 1968 1969
            } else {
                assert!(!type_is_fat_ptr(ccx.tcx(), rhs.ty),
                        "built-in binary operators on fat pointers are homogeneous");
                trans_scalar_binop(bcx, expr, binop_ty, op, lhs, rhs)
            }
1970 1971 1972 1973
        }
    }
}

1974
fn trans_overloaded_op<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
1975
                                   expr: &hir::Expr,
1976
                                   method_call: MethodCall,
1977
                                   lhs: Datum<'tcx, Expr>,
1978
                                   rhs: Option<(Datum<'tcx, Expr>, ast::NodeId)>,
1979 1980
                                   dest: Option<Dest>,
                                   autoref: bool)
1981
                                   -> Result<'blk, 'tcx> {
1982
    callee::trans_call_inner(bcx,
1983
                             expr.debug_loc(),
1984
                             |bcx, arg_cleanup_scope| {
1985
                                meth::trans_method_callee(bcx,
1986 1987
                                                          method_call,
                                                          None,
1988
                                                          arg_cleanup_scope)
1989
                             },
1990
                             callee::ArgOverloadedOp(lhs, rhs, autoref),
1991
                             dest)
1992 1993
}

1994
fn trans_overloaded_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
1995 1996 1997
                                         expr: &hir::Expr,
                                         callee: &'a hir::Expr,
                                         args: &'a [P<hir::Expr>],
1998 1999
                                         dest: Option<Dest>)
                                         -> Block<'blk, 'tcx> {
2000
    debug!("trans_overloaded_call {}", expr.id);
2001
    let method_call = MethodCall::expr(expr.id);
2002
    let mut all_args = vec!(callee);
2003
    all_args.extend(args.iter().map(|e| &**e));
2004 2005
    unpack_result!(bcx,
                   callee::trans_call_inner(bcx,
2006
                                            expr.debug_loc(),
2007 2008 2009 2010 2011 2012 2013
                                            |bcx, arg_cleanup_scope| {
                                                meth::trans_method_callee(
                                                    bcx,
                                                    method_call,
                                                    None,
                                                    arg_cleanup_scope)
                                            },
2014
                                            callee::ArgOverloadedCall(all_args),
2015 2016 2017 2018
                                            dest));
    bcx
}

2019
pub fn cast_is_noop<'tcx>(tcx: &ty::ctxt<'tcx>,
2020
                          expr: &hir::Expr,
2021 2022 2023 2024 2025
                          t_in: Ty<'tcx>,
                          t_out: Ty<'tcx>)
                          -> bool {
    if let Some(&CastKind::CoercionCast) = tcx.cast_kinds.borrow().get(&expr.id) {
        return true;
2026 2027
    }

2028 2029
    match (t_in.builtin_deref(true, ty::NoPreference),
           t_out.builtin_deref(true, ty::NoPreference)) {
2030
        (Some(ty::TypeAndMut{ ty: t_in, .. }), Some(ty::TypeAndMut{ ty: t_out, .. })) => {
2031 2032
            t_in == t_out
        }
2033 2034 2035 2036 2037 2038
        _ => {
            // This condition isn't redundant with the check for CoercionCast:
            // different types can be substituted into the same type, and
            // == equality can be overconservative if there are regions.
            t_in == t_out
        }
2039 2040 2041
    }
}

2042
fn trans_imm_cast<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
2043
                              expr: &hir::Expr,
2044
                              id: ast::NodeId)
2045 2046
                              -> DatumBlock<'blk, 'tcx, Expr>
{
2047 2048
    use middle::ty::cast::CastTy::*;
    use middle::ty::cast::IntTy::*;
2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086

    fn int_cast(bcx: Block,
                lldsttype: Type,
                llsrctype: Type,
                llsrc: ValueRef,
                signed: bool)
                -> ValueRef
    {
        let _icx = push_ctxt("int_cast");
        let srcsz = llsrctype.int_width();
        let dstsz = lldsttype.int_width();
        return if dstsz == srcsz {
            BitCast(bcx, llsrc, lldsttype)
        } else if srcsz > dstsz {
            TruncOrBitCast(bcx, llsrc, lldsttype)
        } else if signed {
            SExtOrBitCast(bcx, llsrc, lldsttype)
        } else {
            ZExtOrBitCast(bcx, llsrc, lldsttype)
        }
    }

    fn float_cast(bcx: Block,
                  lldsttype: Type,
                  llsrctype: Type,
                  llsrc: ValueRef)
                  -> ValueRef
    {
        let _icx = push_ctxt("float_cast");
        let srcsz = llsrctype.float_width();
        let dstsz = lldsttype.float_width();
        return if dstsz > srcsz {
            FPExt(bcx, llsrc, lldsttype)
        } else if srcsz > dstsz {
            FPTrunc(bcx, llsrc, lldsttype)
        } else { llsrc };
    }

A
Ariel Ben-Yehuda 已提交
2087 2088 2089
    let _icx = push_ctxt("trans_cast");
    let mut bcx = bcx;
    let ccx = bcx.ccx();
2090

N
Nick Cameron 已提交
2091
    let t_in = expr_ty_adjusted(bcx, expr);
2092
    let t_out = node_id_type(bcx, id);
2093

2094
    debug!("trans_cast({:?} as {:?})", t_in, t_out);
2095 2096
    let mut ll_t_in = type_of::arg_type_of(ccx, t_in);
    let ll_t_out = type_of::arg_type_of(ccx, t_out);
2097 2098
    // Convert the value to be cast into a ValueRef, either by-ref or
    // by-value as appropriate given its type:
2099 2100
    let mut datum = unpack_datum!(bcx, trans(bcx, expr));

N
Nick Cameron 已提交
2101
    let datum_ty = monomorphize_type(bcx, datum.ty);
2102 2103

    if cast_is_noop(bcx.tcx(), expr, datum_ty, t_out) {
2104 2105 2106 2107
        datum.ty = t_out;
        return DatumBlock::new(bcx, datum);
    }

2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118
    if type_is_fat_ptr(bcx.tcx(), t_in) {
        assert!(datum.kind.is_by_ref());
        if type_is_fat_ptr(bcx.tcx(), t_out) {
            return DatumBlock::new(bcx, Datum::new(
                PointerCast(bcx, datum.val, ll_t_out.ptr_to()),
                t_out,
                Rvalue::new(ByRef)
            )).to_expr_datumblock();
        } else {
            // Return the address
            return immediate_rvalue_bcx(bcx,
A
Ariel Ben-Yehuda 已提交
2119 2120 2121
                                        PointerCast(bcx,
                                                    Load(bcx, get_dataptr(bcx, datum.val)),
                                                    ll_t_out),
2122
                                        t_out).to_expr_datumblock();
2123
        }
2124 2125
    }

2126 2127
    let r_t_in = CastTy::from_ty(t_in).expect("bad input type for cast");
    let r_t_out = CastTy::from_ty(t_out).expect("bad output type for cast");
2128 2129 2130 2131 2132 2133 2134 2135 2136 2137

    let (llexpr, signed) = if let Int(CEnum) = r_t_in {
        let repr = adt::represent_type(ccx, t_in);
        let datum = unpack_datum!(
            bcx, datum.to_lvalue_datum(bcx, "trans_imm_cast", expr.id));
        let llexpr_ptr = datum.to_llref();
        let discr = adt::trans_get_discr(bcx, &*repr, llexpr_ptr, Some(Type::i64(ccx)));
        ll_t_in = val_ty(discr);
        (discr, adt::is_discr_signed(&*repr))
    } else {
2138
        (datum.to_llscalarish(bcx), t_in.is_signed())
2139 2140 2141
    };

    let newval = match (r_t_in, r_t_out) {
A
Ariel Ben-Yehuda 已提交
2142 2143 2144 2145
        (Ptr(_), Ptr(_)) | (FnPtr, Ptr(_)) | (RPtr(_), Ptr(_)) => {
            PointerCast(bcx, llexpr, ll_t_out)
        }
        (Ptr(_), Int(_)) | (FnPtr, Int(_)) => PtrToInt(bcx, llexpr, ll_t_out),
2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156
        (Int(_), Ptr(_)) => IntToPtr(bcx, llexpr, ll_t_out),

        (Int(_), Int(_)) => int_cast(bcx, ll_t_out, ll_t_in, llexpr, signed),
        (Float, Float) => float_cast(bcx, ll_t_out, ll_t_in, llexpr),
        (Int(_), Float) if signed => SIToFP(bcx, llexpr, ll_t_out),
        (Int(_), Float) => UIToFP(bcx, llexpr, ll_t_out),
        (Float, Int(I)) => FPToSI(bcx, llexpr, ll_t_out),
        (Float, Int(_)) => FPToUI(bcx, llexpr, ll_t_out),

        _ => ccx.sess().span_bug(expr.span,
                                  &format!("translating unsupported cast: \
2157 2158 2159
                                            {:?} -> {:?}",
                                           t_in,
                                           t_out)
2160
                                 )
2161 2162
    };
    return immediate_rvalue_bcx(bcx, newval, t_out).to_expr_datumblock();
2163 2164
}

2165
fn trans_assign_op<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
2166 2167 2168 2169
                               expr: &hir::Expr,
                               op: hir::BinOp,
                               dst: &hir::Expr,
                               src: &hir::Expr)
2170
                               -> Block<'blk, 'tcx> {
2171
    let _icx = push_ctxt("trans_assign_op");
2172 2173
    let mut bcx = bcx;

2174
    debug!("trans_assign_op(expr={:?})", expr);
2175

2176
    // User-defined operator methods cannot be used with `+=` etc right now
2177
    assert!(!bcx.tcx().is_method_call(expr.id));
2178

2179
    // Evaluate LHS (destination), which should be an lvalue
A
Ariel Ben-Yehuda 已提交
2180 2181 2182 2183
    let dst = unpack_datum!(bcx, trans_to_lvalue(bcx, dst, "assign_op"));
    assert!(!bcx.fcx.type_needs_drop(dst.ty));
    let lhs = load_ty(bcx, dst.val, dst.ty);
    let lhs = immediate_rvalue(lhs, dst.ty);
2184

A
Ariel Ben-Yehuda 已提交
2185 2186 2187
    // Evaluate RHS - FIXME(#28160) this sucks
    let rhs = unpack_datum!(bcx, trans(bcx, &*src));
    let rhs = unpack_datum!(bcx, rhs.to_rvalue_datum(bcx, "assign_op_rhs"));
2188 2189

    // Perform computation and store the result
2190
    let result_datum = unpack_datum!(
A
Ariel Ben-Yehuda 已提交
2191 2192
        bcx, trans_scalar_binop(bcx, expr, dst.ty, op, lhs, rhs));
    return result_datum.store_to(bcx, dst.val);
2193 2194
}

2195
fn auto_ref<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
2196
                        datum: Datum<'tcx, Expr>,
2197
                        expr: &hir::Expr)
2198
                        -> DatumBlock<'blk, 'tcx, Expr> {
2199 2200 2201 2202 2203 2204 2205 2206 2207
    let mut bcx = bcx;

    // Ensure cleanup of `datum` if not already scheduled and obtain
    // a "by ref" pointer.
    let lv_datum = unpack_datum!(bcx, datum.to_lvalue_datum(bcx, "autoref", expr.id));

    // Compute final type. Note that we are loose with the region and
    // mutability, since those things don't matter in trans.
    let referent_ty = lv_datum.ty;
2208
    let ptr_ty = bcx.tcx().mk_imm_ref(bcx.tcx().mk_region(ty::ReStatic), referent_ty);
2209 2210 2211 2212 2213 2214 2215

    // Get the pointer.
    let llref = lv_datum.to_llref();

    // Construct the resulting datum, using what was the "by ref"
    // ValueRef of type `referent_ty` to be the "by value" ValueRef
    // of type `&referent_ty`.
2216 2217 2218
    // Pointers to DST types are non-immediate, and therefore still use ByRef.
    let kind  = if type_is_sized(bcx.tcx(), referent_ty) { ByValue } else { ByRef };
    DatumBlock::new(bcx, Datum::new(llref, ptr_ty, RvalueExpr(Rvalue::new(kind))))
2219 2220
}

2221
fn deref_multiple<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
2222
                              expr: &hir::Expr,
2223
                              datum: Datum<'tcx, Expr>,
2224
                              times: usize)
2225
                              -> DatumBlock<'blk, 'tcx, Expr> {
2226 2227
    let mut bcx = bcx;
    let mut datum = datum;
2228
    for i in 0..times {
2229
        let method_call = MethodCall::autoderef(expr.id, i as u32);
2230
        datum = unpack_datum!(bcx, deref_once(bcx, expr, datum, method_call));
2231 2232 2233 2234
    }
    DatumBlock { bcx: bcx, datum: datum }
}

2235
fn deref_once<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
2236
                          expr: &hir::Expr,
2237
                          datum: Datum<'tcx, Expr>,
2238 2239
                          method_call: MethodCall)
                          -> DatumBlock<'blk, 'tcx, Expr> {
2240 2241
    let ccx = bcx.ccx();

2242 2243
    debug!("deref_once(expr={:?}, datum={}, method_call={:?})",
           expr,
2244
           datum.to_string(ccx),
2245
           method_call);
2246 2247 2248

    let mut bcx = bcx;

2249
    // Check for overloaded deref.
2250 2251 2252 2253
    let method_ty = ccx.tcx()
                       .tables
                       .borrow()
                       .method_map
2254
                       .get(&method_call).map(|method| method.ty);
2255

2256 2257
    let datum = match method_ty {
        Some(method_ty) => {
2258 2259
            let method_ty = monomorphize_type(bcx, method_ty);

2260 2261
            // Overloaded. Evaluate `trans_overloaded_op`, which will
            // invoke the user's deref() method, which basically
2262
            // converts from the `Smaht<T>` pointer that we have into
2263 2264
            // a `&T` pointer.  We can then proceed down the normal
            // path (below) to dereference that `&T`.
2265 2266 2267
            let datum = if method_call.autoderef == 0 {
                datum
            } else {
2268
                // Always perform an AutoPtr when applying an overloaded auto-deref
2269
                unpack_datum!(bcx, auto_ref(bcx, datum, expr))
2270
            };
2271

2272
            let ref_ty = // invoked methods have their LB regions instantiated
2273
                ccx.tcx().no_late_bound_regions(&method_ty.fn_ret()).unwrap().unwrap();
2274 2275 2276
            let scratch = rvalue_scratch_datum(bcx, ref_ty, "overloaded_deref");

            unpack_result!(bcx, trans_overloaded_op(bcx, expr, method_call,
2277
                                                    datum, None, Some(SaveIn(scratch.val)),
2278
                                                    false));
2279
            scratch.to_expr_datum()
2280
        }
2281 2282 2283 2284
        None => {
            // Not overloaded. We already have a pointer we know how to deref.
            datum
        }
2285 2286
    };

2287
    let r = match datum.ty.sty {
2288
        ty::TyBox(content_ty) => {
2289 2290 2291 2292 2293
            // Make sure we have an lvalue datum here to get the
            // proper cleanups scheduled
            let datum = unpack_datum!(
                bcx, datum.to_lvalue_datum(bcx, "deref", expr.id));

2294
            if type_is_sized(bcx.tcx(), content_ty) {
2295
                let ptr = load_ty(bcx, datum.val, datum.ty);
2296
                DatumBlock::new(bcx, Datum::new(ptr, content_ty, LvalueExpr(datum.kind)))
N
Nick Cameron 已提交
2297
            } else {
2298 2299 2300 2301 2302
                // A fat pointer and a DST lvalue have the same representation
                // just different types. Since there is no temporary for `*e`
                // here (because it is unsized), we cannot emulate the sized
                // object code path for running drop glue and free. Instead,
                // we schedule cleanup for `e`, turning it into an lvalue.
2303

2304 2305
                let lval = Lvalue::new("expr::deref_once ty_uniq");
                let datum = Datum::new(datum.val, content_ty, LvalueExpr(lval));
2306
                DatumBlock::new(bcx, datum)
2307
            }
2308 2309
        }

2310 2311
        ty::TyRawPtr(ty::TypeAndMut { ty: content_ty, .. }) |
        ty::TyRef(_, ty::TypeAndMut { ty: content_ty, .. }) => {
2312
            let lval = Lvalue::new("expr::deref_once ptr");
2313
            if type_is_sized(bcx.tcx(), content_ty) {
N
Nick Cameron 已提交
2314 2315 2316 2317 2318 2319 2320
                let ptr = datum.to_llscalarish(bcx);

                // Always generate an lvalue datum, even if datum.mode is
                // an rvalue.  This is because datum.mode is only an
                // rvalue for non-owning pointers like &T or *T, in which
                // case cleanup *is* scheduled elsewhere, by the true
                // owner (or, in the case of *T, by the user).
2321
                DatumBlock::new(bcx, Datum::new(ptr, content_ty, LvalueExpr(lval)))
N
Nick Cameron 已提交
2322
            } else {
2323
                // A fat pointer and a DST lvalue have the same representation
N
Nick Cameron 已提交
2324
                // just different types.
2325
                DatumBlock::new(bcx, Datum::new(datum.val, content_ty, LvalueExpr(lval)))
2326
            }
2327 2328 2329 2330 2331
        }

        _ => {
            bcx.tcx().sess.span_bug(
                expr.span,
2332
                &format!("deref invoked on expr of invalid type {:?}",
2333
                        datum.ty));
2334 2335 2336
        }
    };

2337
    debug!("deref_once(expr={}, method_call={:?}, result={})",
2338
           expr.id, method_call, r.datum.to_string(ccx));
2339 2340

    return r;
2341
}
2342

2343
#[derive(Debug)]
2344 2345 2346 2347
enum OverflowOp {
    Add,
    Sub,
    Mul,
2348 2349
    Shl,
    Shr,
2350 2351 2352
}

impl OverflowOp {
2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372
    fn codegen_strategy(&self) -> OverflowCodegen {
        use self::OverflowCodegen::{ViaIntrinsic, ViaInputCheck};
        match *self {
            OverflowOp::Add => ViaIntrinsic(OverflowOpViaIntrinsic::Add),
            OverflowOp::Sub => ViaIntrinsic(OverflowOpViaIntrinsic::Sub),
            OverflowOp::Mul => ViaIntrinsic(OverflowOpViaIntrinsic::Mul),

            OverflowOp::Shl => ViaInputCheck(OverflowOpViaInputCheck::Shl),
            OverflowOp::Shr => ViaInputCheck(OverflowOpViaInputCheck::Shr),
        }
    }
}

enum OverflowCodegen {
    ViaIntrinsic(OverflowOpViaIntrinsic),
    ViaInputCheck(OverflowOpViaInputCheck),
}

enum OverflowOpViaInputCheck { Shl, Shr, }

2373
#[derive(Debug)]
2374 2375 2376 2377 2378 2379 2380
enum OverflowOpViaIntrinsic { Add, Sub, Mul, }

impl OverflowOpViaIntrinsic {
    fn to_intrinsic<'blk, 'tcx>(&self, bcx: Block<'blk, 'tcx>, lhs_ty: Ty) -> ValueRef {
        let name = self.to_intrinsic_name(bcx.tcx(), lhs_ty);
        bcx.ccx().get_intrinsic(&name)
    }
2381
    fn to_intrinsic_name(&self, tcx: &ty::ctxt, ty: Ty) -> &'static str {
2382 2383
        use syntax::ast::IntTy::*;
        use syntax::ast::UintTy::*;
2384
        use middle::ty::{TyInt, TyUint};
2385 2386

        let new_sty = match ty.sty {
2387 2388 2389
            TyInt(TyIs) => match &tcx.sess.target.target.target_pointer_width[..] {
                "32" => TyInt(TyI32),
                "64" => TyInt(TyI64),
2390 2391
                _ => panic!("unsupported target word size")
            },
2392 2393 2394
            TyUint(TyUs) => match &tcx.sess.target.target.target_pointer_width[..] {
                "32" => TyUint(TyU32),
                "64" => TyUint(TyU64),
2395 2396
                _ => panic!("unsupported target word size")
            },
2397
            ref t @ TyUint(_) | ref t @ TyInt(_) => t.clone(),
2398 2399
            _ => panic!("tried to get overflow intrinsic for {:?} applied to non-int type",
                        *self)
2400 2401 2402
        };

        match *self {
2403
            OverflowOpViaIntrinsic::Add => match new_sty {
2404 2405 2406 2407
                TyInt(TyI8) => "llvm.sadd.with.overflow.i8",
                TyInt(TyI16) => "llvm.sadd.with.overflow.i16",
                TyInt(TyI32) => "llvm.sadd.with.overflow.i32",
                TyInt(TyI64) => "llvm.sadd.with.overflow.i64",
2408

2409 2410 2411 2412
                TyUint(TyU8) => "llvm.uadd.with.overflow.i8",
                TyUint(TyU16) => "llvm.uadd.with.overflow.i16",
                TyUint(TyU32) => "llvm.uadd.with.overflow.i32",
                TyUint(TyU64) => "llvm.uadd.with.overflow.i64",
2413 2414 2415

                _ => unreachable!(),
            },
2416
            OverflowOpViaIntrinsic::Sub => match new_sty {
2417 2418 2419 2420
                TyInt(TyI8) => "llvm.ssub.with.overflow.i8",
                TyInt(TyI16) => "llvm.ssub.with.overflow.i16",
                TyInt(TyI32) => "llvm.ssub.with.overflow.i32",
                TyInt(TyI64) => "llvm.ssub.with.overflow.i64",
2421

2422 2423 2424 2425
                TyUint(TyU8) => "llvm.usub.with.overflow.i8",
                TyUint(TyU16) => "llvm.usub.with.overflow.i16",
                TyUint(TyU32) => "llvm.usub.with.overflow.i32",
                TyUint(TyU64) => "llvm.usub.with.overflow.i64",
2426 2427 2428

                _ => unreachable!(),
            },
2429
            OverflowOpViaIntrinsic::Mul => match new_sty {
2430 2431 2432 2433 2434 2435 2436 2437 2438
                TyInt(TyI8) => "llvm.smul.with.overflow.i8",
                TyInt(TyI16) => "llvm.smul.with.overflow.i16",
                TyInt(TyI32) => "llvm.smul.with.overflow.i32",
                TyInt(TyI64) => "llvm.smul.with.overflow.i64",

                TyUint(TyU8) => "llvm.umul.with.overflow.i8",
                TyUint(TyU16) => "llvm.umul.with.overflow.i16",
                TyUint(TyU32) => "llvm.umul.with.overflow.i32",
                TyUint(TyU64) => "llvm.umul.with.overflow.i64",
2439 2440 2441 2442 2443 2444

                _ => unreachable!(),
            },
        }
    }

2445 2446 2447 2448 2449 2450 2451
    fn build_intrinsic_call<'blk, 'tcx>(&self, bcx: Block<'blk, 'tcx>,
                                        info: NodeIdAndSpan,
                                        lhs_t: Ty<'tcx>, lhs: ValueRef,
                                        rhs: ValueRef,
                                        binop_debug_loc: DebugLoc)
                                        -> (Block<'blk, 'tcx>, ValueRef) {
        let llfn = self.to_intrinsic(bcx, lhs_t);
2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469

        let val = Call(bcx, llfn, &[lhs, rhs], None, binop_debug_loc);
        let result = ExtractValue(bcx, val, 0); // iN operation result
        let overflow = ExtractValue(bcx, val, 1); // i1 "did it overflow?"

        let cond = ICmp(bcx, llvm::IntEQ, overflow, C_integral(Type::i1(bcx.ccx()), 1, false),
                        binop_debug_loc);

        let expect = bcx.ccx().get_intrinsic(&"llvm.expect.i1");
        Call(bcx, expect, &[cond, C_integral(Type::i1(bcx.ccx()), 0, false)],
             None, binop_debug_loc);

        let bcx =
            base::with_cond(bcx, cond, |bcx|
                controlflow::trans_fail(bcx, info,
                    InternedString::new("arithmetic operation overflowed")));

        (bcx, result)
2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491
    }
}

impl OverflowOpViaInputCheck {
    fn build_with_input_check<'blk, 'tcx>(&self,
                                          bcx: Block<'blk, 'tcx>,
                                          info: NodeIdAndSpan,
                                          lhs_t: Ty<'tcx>,
                                          lhs: ValueRef,
                                          rhs: ValueRef,
                                          binop_debug_loc: DebugLoc)
                                          -> (Block<'blk, 'tcx>, ValueRef)
    {
        let lhs_llty = val_ty(lhs);
        let rhs_llty = val_ty(rhs);

        // Panic if any bits are set outside of bits that we always
        // mask in.
        //
        // Note that the mask's value is derived from the LHS type
        // (since that is where the 32/64 distinction is relevant) but
        // the mask's type must match the RHS type (since they will
2492
        // both be fed into an and-binop)
2493
        let invert_mask = shift_mask_val(bcx, lhs_llty, rhs_llty, true);
2494 2495

        let outer_bits = And(bcx, rhs, invert_mask, binop_debug_loc);
2496
        let cond = build_nonzero_check(bcx, outer_bits, binop_debug_loc);
2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511
        let result = match *self {
            OverflowOpViaInputCheck::Shl =>
                build_unchecked_lshift(bcx, lhs, rhs, binop_debug_loc),
            OverflowOpViaInputCheck::Shr =>
                build_unchecked_rshift(bcx, lhs_t, lhs, rhs, binop_debug_loc),
        };
        let bcx =
            base::with_cond(bcx, cond, |bcx|
                controlflow::trans_fail(bcx, info,
                    InternedString::new("shift operation overflowed")));

        (bcx, result)
    }
}

2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528
// Check if an integer or vector contains a nonzero element.
fn build_nonzero_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                   value: ValueRef,
                                   binop_debug_loc: DebugLoc) -> ValueRef {
    let llty = val_ty(value);
    let kind = llty.kind();
    match kind {
        TypeKind::Integer => ICmp(bcx, llvm::IntNE, value, C_null(llty), binop_debug_loc),
        TypeKind::Vector => {
            // Check if any elements of the vector are nonzero by treating
            // it as a wide integer and checking if the integer is nonzero.
            let width = llty.vector_length() as u64 * llty.element_type().int_width();
            let int_value = BitCast(bcx, value, Type::ix(bcx.ccx(), width));
            build_nonzero_check(bcx, int_value, binop_debug_loc)
        },
        _ => panic!("build_nonzero_check: expected Integer or Vector, found {:?}", kind),
    }
2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544
}

fn with_overflow_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, oop: OverflowOp, info: NodeIdAndSpan,
                                   lhs_t: Ty<'tcx>, lhs: ValueRef,
                                   rhs: ValueRef,
                                   binop_debug_loc: DebugLoc)
                                   -> (Block<'blk, 'tcx>, ValueRef) {
    if bcx.unreachable.get() { return (bcx, _Undef(lhs)); }
    if bcx.ccx().check_overflow() {

        match oop.codegen_strategy() {
            OverflowCodegen::ViaIntrinsic(oop) =>
                oop.build_intrinsic_call(bcx, info, lhs_t, lhs, rhs, binop_debug_loc),
            OverflowCodegen::ViaInputCheck(oop) =>
                oop.build_with_input_check(bcx, info, lhs_t, lhs, rhs, binop_debug_loc),
        }
2545 2546 2547 2548 2549
    } else {
        let res = match oop {
            OverflowOp::Add => Add(bcx, lhs, rhs, binop_debug_loc),
            OverflowOp::Sub => Sub(bcx, lhs, rhs, binop_debug_loc),
            OverflowOp::Mul => Mul(bcx, lhs, rhs, binop_debug_loc),
2550 2551 2552 2553 2554

            OverflowOp::Shl =>
                build_unchecked_lshift(bcx, lhs, rhs, binop_debug_loc),
            OverflowOp::Shr =>
                build_unchecked_rshift(bcx, lhs_t, lhs, rhs, binop_debug_loc),
2555 2556 2557 2558
        };
        (bcx, res)
    }
}
A
Ariel Ben-Yehuda 已提交
2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572

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

2573
fn expr_kind(tcx: &ty::ctxt, expr: &hir::Expr) -> ExprKind {
2574
    if tcx.is_method_call(expr.id) {
A
Ariel Ben-Yehuda 已提交
2575 2576 2577 2578
        // 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.
2579
            hir::ExprAssignOp(..) => ExprKind::RvalueStmt,
A
Ariel Ben-Yehuda 已提交
2580 2581

            // the deref method invoked for `*a` always yields an `&T`
2582
            hir::ExprUnary(hir::UnDeref, _) => ExprKind::Lvalue,
A
Ariel Ben-Yehuda 已提交
2583 2584

            // the index method invoked for `a[i]` always yields an `&T`
2585
            hir::ExprIndex(..) => ExprKind::Lvalue,
A
Ariel Ben-Yehuda 已提交
2586 2587 2588 2589 2590 2591 2592

            // in the general case, result could be any type, use DPS
            _ => ExprKind::RvalueDps
        };
    }

    match expr.node {
2593
        hir::ExprPath(..) => {
2594
            match tcx.resolve_expr(expr) {
A
Ariel Ben-Yehuda 已提交
2595
                def::DefStruct(_) | def::DefVariant(..) => {
2596
                    if let ty::TyBareFn(..) = tcx.node_id_to_type(expr.id).sty {
A
Ariel Ben-Yehuda 已提交
2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632
                        // 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));
                }
            }
        }

2633 2634 2635 2636
        hir::ExprUnary(hir::UnDeref, _) |
        hir::ExprField(..) |
        hir::ExprTupField(..) |
        hir::ExprIndex(..) => {
A
Ariel Ben-Yehuda 已提交
2637 2638 2639
            ExprKind::Lvalue
        }

2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650
        hir::ExprCall(..) |
        hir::ExprMethodCall(..) |
        hir::ExprStruct(..) |
        hir::ExprRange(..) |
        hir::ExprTup(..) |
        hir::ExprIf(..) |
        hir::ExprMatch(..) |
        hir::ExprClosure(..) |
        hir::ExprBlock(..) |
        hir::ExprRepeat(..) |
        hir::ExprVec(..) => {
A
Ariel Ben-Yehuda 已提交
2651 2652 2653
            ExprKind::RvalueDps
        }

2654
        hir::ExprLit(ref lit) if ast_util::lit_is_str(&**lit) => {
A
Ariel Ben-Yehuda 已提交
2655 2656 2657
            ExprKind::RvalueDps
        }

2658 2659 2660 2661 2662 2663 2664 2665
        hir::ExprBreak(..) |
        hir::ExprAgain(..) |
        hir::ExprRet(..) |
        hir::ExprWhile(..) |
        hir::ExprLoop(..) |
        hir::ExprAssign(..) |
        hir::ExprInlineAsm(..) |
        hir::ExprAssignOp(..) => {
A
Ariel Ben-Yehuda 已提交
2666 2667 2668
            ExprKind::RvalueStmt
        }

2669 2670
        hir::ExprLit(_) | // Note: LitStr is carved out above
        hir::ExprUnary(..) |
2671
        hir::ExprBox(_) |
2672 2673 2674
        hir::ExprAddrOf(..) |
        hir::ExprBinary(..) |
        hir::ExprCast(..) => {
A
Ariel Ben-Yehuda 已提交
2675 2676 2677 2678
            ExprKind::RvalueDatum
        }
    }
}