mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-26 13:01:27 +03:00
49e2983d6d
Simplify find_attr! for HirId usage Add a `HasAttrs<'tcx, Tcx>` trait to `rustc_hir` that allows `find_attr!` to accept `DefId`, `LocalDefId`, `OwnerId`, and `HirId` directly, instead of requiring callers to manually fetch the attribute slice first. Before: `find_attr!(tcx.hir_attrs(hir_id), SomeAttr)` After: `find_attr!(tcx, hir_id, SomeAttr)` The trait is defined in `rustc_hir` with a generic `Tcx` parameter to avoid a dependency cycle (`rustc_hir` cannot depend on `rustc_middle`). The four concrete impls for `TyCtxt` are in `rustc_middle`. Fixes https://github.com/rust-lang/rust/issues/153103
1572 lines
69 KiB
Rust
1572 lines
69 KiB
Rust
use itertools::Itertools;
|
|
use rustc_abi::{FIRST_VARIANT, FieldIdx, Size, VariantIdx};
|
|
use rustc_ast::UnsafeBinderCastKind;
|
|
use rustc_data_structures::stack::ensure_sufficient_stack;
|
|
use rustc_hir as hir;
|
|
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
|
|
use rustc_hir::{LangItem, find_attr};
|
|
use rustc_index::Idx;
|
|
use rustc_middle::hir::place::{
|
|
Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind,
|
|
};
|
|
use rustc_middle::middle::region;
|
|
use rustc_middle::mir::{self, AssignOp, BinOp, BorrowKind, UnOp};
|
|
use rustc_middle::thir::*;
|
|
use rustc_middle::ty::adjustment::{
|
|
Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, PointerCoercion,
|
|
};
|
|
use rustc_middle::ty::{
|
|
self, AdtKind, GenericArgs, InlineConstArgs, InlineConstArgsParts, ScalarInt, Ty, UpvarArgs,
|
|
};
|
|
use rustc_middle::{bug, span_bug};
|
|
use rustc_span::Span;
|
|
use tracing::{debug, info, instrument, trace};
|
|
|
|
use crate::errors::*;
|
|
use crate::thir::cx::ThirBuildCx;
|
|
|
|
impl<'tcx> ThirBuildCx<'tcx> {
|
|
/// Create a THIR expression for the given HIR expression. This expands all
|
|
/// adjustments and directly adds the type information from the
|
|
/// `typeck_results`. See the [dev-guide] for more details.
|
|
///
|
|
/// (The term "mirror" in this case does not refer to "flipped" or
|
|
/// "reversed".)
|
|
///
|
|
/// [dev-guide]: https://rustc-dev-guide.rust-lang.org/thir.html
|
|
pub(crate) fn mirror_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> ExprId {
|
|
// `mirror_expr` is recursing very deep. Make sure the stack doesn't overflow.
|
|
ensure_sufficient_stack(|| self.mirror_expr_inner(expr))
|
|
}
|
|
|
|
pub(crate) fn mirror_exprs(&mut self, exprs: &'tcx [hir::Expr<'tcx>]) -> Box<[ExprId]> {
|
|
// `mirror_exprs` may also recurse deeply, so it needs protection from stack overflow.
|
|
// Note that we *could* forward to `mirror_expr` for that, but we can consolidate the
|
|
// overhead of stack growth by doing it outside the iteration.
|
|
ensure_sufficient_stack(|| exprs.iter().map(|expr| self.mirror_expr_inner(expr)).collect())
|
|
}
|
|
|
|
#[instrument(level = "trace", skip(self, hir_expr))]
|
|
pub(super) fn mirror_expr_inner(&mut self, hir_expr: &'tcx hir::Expr<'tcx>) -> ExprId {
|
|
let expr_scope =
|
|
region::Scope { local_id: hir_expr.hir_id.local_id, data: region::ScopeData::Node };
|
|
|
|
trace!(?hir_expr.hir_id, ?hir_expr.span);
|
|
|
|
let mut expr = self.make_mirror_unadjusted(hir_expr);
|
|
|
|
trace!(?expr.ty);
|
|
|
|
// Now apply adjustments, if any.
|
|
if self.apply_adjustments {
|
|
for adjustment in self.typeck_results.expr_adjustments(hir_expr) {
|
|
trace!(?expr, ?adjustment);
|
|
let span = expr.span;
|
|
expr = self.apply_adjustment(hir_expr, expr, adjustment, span);
|
|
}
|
|
}
|
|
|
|
trace!(?expr.ty, "after adjustments");
|
|
|
|
// Finally, wrap this up in the expr's scope.
|
|
expr = Expr {
|
|
temp_scope_id: expr_scope.local_id,
|
|
ty: expr.ty,
|
|
span: hir_expr.span,
|
|
kind: ExprKind::Scope {
|
|
region_scope: expr_scope,
|
|
value: self.thir.exprs.push(expr),
|
|
hir_id: hir_expr.hir_id,
|
|
},
|
|
};
|
|
|
|
// OK, all done!
|
|
self.thir.exprs.push(expr)
|
|
}
|
|
|
|
#[instrument(level = "trace", skip(self, expr, span))]
|
|
fn apply_adjustment(
|
|
&mut self,
|
|
hir_expr: &'tcx hir::Expr<'tcx>,
|
|
mut expr: Expr<'tcx>,
|
|
adjustment: &Adjustment<'tcx>,
|
|
mut span: Span,
|
|
) -> Expr<'tcx> {
|
|
let Expr { temp_scope_id, .. } = expr;
|
|
|
|
// Adjust the span from the block, to the last expression of the
|
|
// block. This is a better span when returning a mutable reference
|
|
// with too short a lifetime. The error message will use the span
|
|
// from the assignment to the return place, which should only point
|
|
// at the returned value, not the entire function body.
|
|
//
|
|
// fn return_short_lived<'a>(x: &'a mut i32) -> &'static mut i32 {
|
|
// x
|
|
// // ^ error message points at this expression.
|
|
// }
|
|
let mut adjust_span = |expr: &mut Expr<'tcx>| {
|
|
if let ExprKind::Block { block } = expr.kind
|
|
&& let Some(last_expr) = self.thir[block].expr
|
|
{
|
|
span = self.thir[last_expr].span;
|
|
expr.span = span;
|
|
}
|
|
};
|
|
|
|
let kind = match adjustment.kind {
|
|
Adjust::Pointer(cast) => {
|
|
if cast == PointerCoercion::Unsize {
|
|
adjust_span(&mut expr);
|
|
}
|
|
|
|
let is_from_as_cast = if let hir::Node::Expr(hir::Expr {
|
|
kind: hir::ExprKind::Cast(..),
|
|
span: cast_span,
|
|
..
|
|
}) = self.tcx.parent_hir_node(hir_expr.hir_id)
|
|
{
|
|
// Use the whole span of the `x as T` expression for the coercion.
|
|
span = *cast_span;
|
|
true
|
|
} else {
|
|
false
|
|
};
|
|
ExprKind::PointerCoercion {
|
|
cast,
|
|
source: self.thir.exprs.push(expr),
|
|
is_from_as_cast,
|
|
}
|
|
}
|
|
Adjust::NeverToAny if adjustment.target.is_never() => return expr,
|
|
Adjust::NeverToAny => ExprKind::NeverToAny { source: self.thir.exprs.push(expr) },
|
|
Adjust::Deref(DerefAdjustKind::Builtin) => {
|
|
adjust_span(&mut expr);
|
|
ExprKind::Deref { arg: self.thir.exprs.push(expr) }
|
|
}
|
|
Adjust::Deref(DerefAdjustKind::Pin) => {
|
|
adjust_span(&mut expr);
|
|
// pointer = ($expr).pointer
|
|
let pin_ty = expr.ty.pinned_ty().expect("Deref(Pin) with non-Pin type");
|
|
let pointer_target = ExprKind::Field {
|
|
lhs: self.thir.exprs.push(expr),
|
|
variant_index: FIRST_VARIANT,
|
|
name: FieldIdx::ZERO,
|
|
};
|
|
let expr = Expr { temp_scope_id, ty: pin_ty, span, kind: pointer_target };
|
|
// expr = *pointer
|
|
ExprKind::Deref { arg: self.thir.exprs.push(expr) }
|
|
}
|
|
Adjust::Deref(DerefAdjustKind::Overloaded(deref)) => {
|
|
// We don't need to do call adjust_span here since
|
|
// deref coercions always start with a built-in deref.
|
|
let call_def_id = deref.method_call(self.tcx);
|
|
let overloaded_callee =
|
|
Ty::new_fn_def(self.tcx, call_def_id, self.tcx.mk_args(&[expr.ty.into()]));
|
|
|
|
expr = Expr {
|
|
temp_scope_id,
|
|
ty: Ty::new_ref(self.tcx, self.tcx.lifetimes.re_erased, expr.ty, deref.mutbl),
|
|
span,
|
|
kind: ExprKind::Borrow {
|
|
borrow_kind: deref.mutbl.to_borrow_kind(),
|
|
arg: self.thir.exprs.push(expr),
|
|
},
|
|
};
|
|
|
|
let expr = Box::new([self.thir.exprs.push(expr)]);
|
|
|
|
self.overloaded_place(
|
|
hir_expr,
|
|
adjustment.target,
|
|
Some(overloaded_callee),
|
|
expr,
|
|
deref.span,
|
|
)
|
|
}
|
|
Adjust::Borrow(AutoBorrow::Ref(m)) => ExprKind::Borrow {
|
|
borrow_kind: m.to_borrow_kind(),
|
|
arg: self.thir.exprs.push(expr),
|
|
},
|
|
Adjust::Borrow(AutoBorrow::RawPtr(mutability)) => {
|
|
ExprKind::RawBorrow { mutability, arg: self.thir.exprs.push(expr) }
|
|
}
|
|
Adjust::Borrow(AutoBorrow::Pin(mutbl)) => {
|
|
// expr = &pin (mut|const|) arget
|
|
let borrow_kind = match mutbl {
|
|
hir::Mutability::Mut => BorrowKind::Mut { kind: mir::MutBorrowKind::Default },
|
|
hir::Mutability::Not => BorrowKind::Shared,
|
|
};
|
|
let new_pin_target =
|
|
Ty::new_ref(self.tcx, self.tcx.lifetimes.re_erased, expr.ty, mutbl);
|
|
let arg = self.thir.exprs.push(expr);
|
|
let expr = self.thir.exprs.push(Expr {
|
|
temp_scope_id,
|
|
ty: new_pin_target,
|
|
span,
|
|
kind: ExprKind::Borrow { borrow_kind, arg },
|
|
});
|
|
|
|
// kind = Pin { pointer }
|
|
let pin_did = self.tcx.require_lang_item(rustc_hir::LangItem::Pin, span);
|
|
let args = self.tcx.mk_args(&[new_pin_target.into()]);
|
|
let kind = ExprKind::Adt(Box::new(AdtExpr {
|
|
adt_def: self.tcx.adt_def(pin_did),
|
|
variant_index: FIRST_VARIANT,
|
|
args,
|
|
fields: Box::new([FieldExpr { name: FieldIdx::ZERO, expr }]),
|
|
user_ty: None,
|
|
base: AdtExprBase::None,
|
|
}));
|
|
|
|
debug!(?kind);
|
|
kind
|
|
}
|
|
Adjust::ReborrowPin(mutbl) => {
|
|
debug!("apply ReborrowPin adjustment");
|
|
// Rewrite `$expr` as `Pin { __pointer: &(mut)? *($expr).__pointer }`
|
|
|
|
// We'll need these types later on
|
|
let pin_ty_args = match expr.ty.kind() {
|
|
ty::Adt(_, args) => args,
|
|
_ => bug!("ReborrowPin with non-Pin type"),
|
|
};
|
|
let pin_ty = pin_ty_args.iter().next().unwrap().expect_ty();
|
|
let ptr_target_ty = match pin_ty.kind() {
|
|
ty::Ref(_, ty, _) => *ty,
|
|
_ => bug!("ReborrowPin with non-Ref type"),
|
|
};
|
|
|
|
// pointer = ($expr).__pointer
|
|
let pointer_target = ExprKind::Field {
|
|
lhs: self.thir.exprs.push(expr),
|
|
variant_index: FIRST_VARIANT,
|
|
name: FieldIdx::ZERO,
|
|
};
|
|
let arg = Expr { temp_scope_id, ty: pin_ty, span, kind: pointer_target };
|
|
let arg = self.thir.exprs.push(arg);
|
|
|
|
// arg = *pointer
|
|
let expr = ExprKind::Deref { arg };
|
|
let arg = self.thir.exprs.push(Expr {
|
|
temp_scope_id,
|
|
ty: ptr_target_ty,
|
|
span,
|
|
kind: expr,
|
|
});
|
|
|
|
// expr = &mut target
|
|
let borrow_kind = match mutbl {
|
|
hir::Mutability::Mut => BorrowKind::Mut { kind: mir::MutBorrowKind::Default },
|
|
hir::Mutability::Not => BorrowKind::Shared,
|
|
};
|
|
let new_pin_target =
|
|
Ty::new_ref(self.tcx, self.tcx.lifetimes.re_erased, ptr_target_ty, mutbl);
|
|
let expr = self.thir.exprs.push(Expr {
|
|
temp_scope_id,
|
|
ty: new_pin_target,
|
|
span,
|
|
kind: ExprKind::Borrow { borrow_kind, arg },
|
|
});
|
|
|
|
// kind = Pin { __pointer: pointer }
|
|
let pin_did = self.tcx.require_lang_item(rustc_hir::LangItem::Pin, span);
|
|
let args = self.tcx.mk_args(&[new_pin_target.into()]);
|
|
let kind = ExprKind::Adt(Box::new(AdtExpr {
|
|
adt_def: self.tcx.adt_def(pin_did),
|
|
variant_index: FIRST_VARIANT,
|
|
args,
|
|
fields: Box::new([FieldExpr { name: FieldIdx::ZERO, expr }]),
|
|
user_ty: None,
|
|
base: AdtExprBase::None,
|
|
}));
|
|
|
|
debug!(?kind);
|
|
kind
|
|
}
|
|
};
|
|
|
|
Expr { temp_scope_id, ty: adjustment.target, span, kind }
|
|
}
|
|
|
|
/// Lowers a cast expression.
|
|
///
|
|
/// Dealing with user type annotations is left to the caller.
|
|
fn mirror_expr_cast(
|
|
&mut self,
|
|
source: &'tcx hir::Expr<'tcx>,
|
|
temp_scope_id: hir::ItemLocalId,
|
|
span: Span,
|
|
) -> ExprKind<'tcx> {
|
|
let tcx = self.tcx;
|
|
|
|
// Check to see if this cast is a "coercion cast", where the cast is actually done
|
|
// using a coercion (or is a no-op).
|
|
if self.typeck_results.is_coercion_cast(source.hir_id) {
|
|
// Convert the lexpr to a vexpr.
|
|
ExprKind::Use { source: self.mirror_expr(source) }
|
|
} else if self.typeck_results.expr_ty(source).is_ref() {
|
|
// Special cased so that we can type check that the element
|
|
// type of the source matches the pointed to type of the
|
|
// destination.
|
|
ExprKind::PointerCoercion {
|
|
source: self.mirror_expr(source),
|
|
cast: PointerCoercion::ArrayToPointer,
|
|
is_from_as_cast: true,
|
|
}
|
|
} else if let hir::ExprKind::Path(ref qpath) = source.kind
|
|
&& let res = self.typeck_results.qpath_res(qpath, source.hir_id)
|
|
&& let ty = self.typeck_results.node_type(source.hir_id)
|
|
&& let ty::Adt(adt_def, args) = ty.kind()
|
|
&& let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), variant_ctor_id) = res
|
|
{
|
|
// Check whether this is casting an enum variant discriminant.
|
|
// To prevent cycles, we refer to the discriminant initializer,
|
|
// which is always an integer and thus doesn't need to know the
|
|
// enum's layout (or its tag type) to compute it during const eval.
|
|
// Example:
|
|
// enum Foo {
|
|
// A,
|
|
// B = A as isize + 4,
|
|
// }
|
|
// The correct solution would be to add symbolic computations to miri,
|
|
// so we wouldn't have to compute and store the actual value
|
|
|
|
let idx = adt_def.variant_index_with_ctor_id(variant_ctor_id);
|
|
let (discr_did, discr_offset) = adt_def.discriminant_def_for_variant(idx);
|
|
|
|
use rustc_middle::ty::util::IntTypeExt;
|
|
let ty = adt_def.repr().discr_type();
|
|
let discr_ty = ty.to_ty(tcx);
|
|
|
|
let size = tcx
|
|
.layout_of(self.typing_env.as_query_input(discr_ty))
|
|
.unwrap_or_else(|e| panic!("could not compute layout for {discr_ty:?}: {e:?}"))
|
|
.size;
|
|
|
|
let (lit, overflowing) = ScalarInt::truncate_from_uint(discr_offset as u128, size);
|
|
if overflowing {
|
|
// An erroneous enum with too many variants for its repr will emit E0081 and E0370
|
|
self.tcx.dcx().span_delayed_bug(
|
|
source.span,
|
|
"overflowing enum wasn't rejected by hir analysis",
|
|
);
|
|
}
|
|
let kind = ExprKind::NonHirLiteral { lit, user_ty: None };
|
|
let offset = self.thir.exprs.push(Expr { temp_scope_id, ty: discr_ty, span, kind });
|
|
|
|
let source = match discr_did {
|
|
// in case we are offsetting from a computed discriminant
|
|
// and not the beginning of discriminants (which is always `0`)
|
|
Some(did) => {
|
|
let kind = ExprKind::NamedConst { def_id: did, args, user_ty: None };
|
|
let lhs =
|
|
self.thir.exprs.push(Expr { temp_scope_id, ty: discr_ty, span, kind });
|
|
let bin = ExprKind::Binary { op: BinOp::Add, lhs, rhs: offset };
|
|
self.thir.exprs.push(Expr { temp_scope_id, ty: discr_ty, span, kind: bin })
|
|
}
|
|
None => offset,
|
|
};
|
|
|
|
ExprKind::Cast { source }
|
|
} else {
|
|
// Default to `ExprKind::Cast` for all explicit casts.
|
|
// MIR building then picks the right MIR casts based on the types.
|
|
ExprKind::Cast { source: self.mirror_expr(source) }
|
|
}
|
|
}
|
|
|
|
#[instrument(level = "debug", skip(self), ret)]
|
|
fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> {
|
|
let tcx = self.tcx;
|
|
let expr_ty = self.typeck_results.expr_ty(expr);
|
|
let mk_expr =
|
|
|kind, ty| Expr { temp_scope_id: expr.hir_id.local_id, span: expr.span, ty, kind };
|
|
|
|
let kind = match expr.kind {
|
|
// Here comes the interesting stuff:
|
|
hir::ExprKind::MethodCall(segment, receiver, args, fn_span) => {
|
|
// Rewrite a.b(c) into UFCS form like Trait::b(a, c)
|
|
let expr = self.method_callee(expr, segment.ident.span, None);
|
|
info!("Using method span: {:?}", expr.span);
|
|
let args = std::iter::once(receiver)
|
|
.chain(args.iter())
|
|
.map(|expr| self.mirror_expr(expr))
|
|
.collect();
|
|
ExprKind::Call {
|
|
ty: expr.ty,
|
|
fun: self.thir.exprs.push(expr),
|
|
args,
|
|
from_hir_call: true,
|
|
fn_span,
|
|
}
|
|
}
|
|
|
|
hir::ExprKind::Call(fun, ref args) => {
|
|
if self.typeck_results.is_method_call(expr) {
|
|
// The callee is something implementing Fn, FnMut, or FnOnce.
|
|
// Find the actual method implementation being called and
|
|
// build the appropriate UFCS call expression with the
|
|
// callee-object as expr parameter.
|
|
|
|
// rewrite f(u, v) into FnOnce::call_once(f, (u, v))
|
|
|
|
let method = self.method_callee(expr, fun.span, None);
|
|
|
|
let arg_tys = args.iter().map(|e| self.typeck_results.expr_ty_adjusted(e));
|
|
let tupled_args = Expr {
|
|
ty: Ty::new_tup_from_iter(tcx, arg_tys),
|
|
temp_scope_id: expr.hir_id.local_id,
|
|
span: expr.span,
|
|
kind: ExprKind::Tuple { fields: self.mirror_exprs(args) },
|
|
};
|
|
let tupled_args = self.thir.exprs.push(tupled_args);
|
|
|
|
ExprKind::Call {
|
|
ty: method.ty,
|
|
fun: self.thir.exprs.push(method),
|
|
args: Box::new([self.mirror_expr(fun), tupled_args]),
|
|
from_hir_call: true,
|
|
fn_span: expr.span,
|
|
}
|
|
} else {
|
|
// Tuple-like ADTs are represented as ExprKind::Call. We convert them here.
|
|
let adt_data = if let hir::ExprKind::Path(ref qpath) = fun.kind
|
|
&& let Some(adt_def) = expr_ty.ty_adt_def()
|
|
{
|
|
match qpath {
|
|
hir::QPath::Resolved(_, path) => match path.res {
|
|
Res::Def(DefKind::Ctor(_, CtorKind::Fn), ctor_id) => {
|
|
Some((adt_def, adt_def.variant_index_with_ctor_id(ctor_id)))
|
|
}
|
|
Res::SelfCtor(..) => Some((adt_def, FIRST_VARIANT)),
|
|
_ => None,
|
|
},
|
|
hir::QPath::TypeRelative(_ty, _) => {
|
|
if let Some((DefKind::Ctor(_, CtorKind::Fn), ctor_id)) =
|
|
self.typeck_results.type_dependent_def(fun.hir_id)
|
|
{
|
|
Some((adt_def, adt_def.variant_index_with_ctor_id(ctor_id)))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
None
|
|
};
|
|
if let Some((adt_def, index)) = adt_data {
|
|
let node_args = self.typeck_results.node_args(fun.hir_id);
|
|
let user_provided_types = self.typeck_results.user_provided_types();
|
|
let user_ty =
|
|
user_provided_types.get(fun.hir_id).copied().map(|mut u_ty| {
|
|
if let ty::UserTypeKind::TypeOf(did, _) = &mut u_ty.value.kind {
|
|
*did = adt_def.did();
|
|
}
|
|
Box::new(u_ty)
|
|
});
|
|
debug!("make_mirror_unadjusted: (call) user_ty={:?}", user_ty);
|
|
|
|
let field_refs = args
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(idx, e)| FieldExpr {
|
|
name: FieldIdx::new(idx),
|
|
expr: self.mirror_expr(e),
|
|
})
|
|
.collect();
|
|
ExprKind::Adt(Box::new(AdtExpr {
|
|
adt_def,
|
|
args: node_args,
|
|
variant_index: index,
|
|
fields: field_refs,
|
|
user_ty,
|
|
base: AdtExprBase::None,
|
|
}))
|
|
} else {
|
|
ExprKind::Call {
|
|
ty: self.typeck_results.node_type(fun.hir_id),
|
|
fun: self.mirror_expr(fun),
|
|
args: self.mirror_exprs(args),
|
|
from_hir_call: true,
|
|
fn_span: expr.span,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
hir::ExprKind::Use(expr, span) => {
|
|
ExprKind::ByUse { expr: self.mirror_expr(expr), span }
|
|
}
|
|
|
|
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mutbl, arg) => {
|
|
ExprKind::Borrow { borrow_kind: mutbl.to_borrow_kind(), arg: self.mirror_expr(arg) }
|
|
}
|
|
|
|
hir::ExprKind::AddrOf(hir::BorrowKind::Raw, mutability, arg) => {
|
|
ExprKind::RawBorrow { mutability, arg: self.mirror_expr(arg) }
|
|
}
|
|
|
|
// Make `&pin mut $expr` and `&pin const $expr` into
|
|
// `Pin { __pointer: &mut { $expr } }` and `Pin { __pointer: &$expr }`.
|
|
hir::ExprKind::AddrOf(hir::BorrowKind::Pin, mutbl, arg_expr) => match expr_ty.kind() {
|
|
&ty::Adt(adt_def, args) if tcx.is_lang_item(adt_def.did(), hir::LangItem::Pin) => {
|
|
let ty = args.type_at(0);
|
|
let arg_ty = self.typeck_results.expr_ty(arg_expr);
|
|
let mut arg = self.mirror_expr(arg_expr);
|
|
// For `&pin mut $place` where `$place` is not `Unpin`, move the place
|
|
// `$place` to ensure it will not be used afterwards.
|
|
if mutbl.is_mut() && !arg_ty.is_unpin(self.tcx, self.typing_env) {
|
|
let block = self.thir.blocks.push(Block {
|
|
targeted_by_break: false,
|
|
region_scope: region::Scope {
|
|
local_id: arg_expr.hir_id.local_id,
|
|
data: region::ScopeData::Node,
|
|
},
|
|
span: arg_expr.span,
|
|
stmts: Box::new([]),
|
|
expr: Some(arg),
|
|
safety_mode: BlockSafety::Safe,
|
|
});
|
|
arg = self.thir.exprs.push(Expr {
|
|
temp_scope_id: arg_expr.hir_id.local_id,
|
|
ty: arg_ty,
|
|
span: arg_expr.span,
|
|
kind: ExprKind::Block { block },
|
|
});
|
|
}
|
|
let expr = self.thir.exprs.push(Expr {
|
|
temp_scope_id: expr.hir_id.local_id,
|
|
ty,
|
|
span: expr.span,
|
|
kind: ExprKind::Borrow { borrow_kind: mutbl.to_borrow_kind(), arg },
|
|
});
|
|
ExprKind::Adt(Box::new(AdtExpr {
|
|
adt_def,
|
|
variant_index: FIRST_VARIANT,
|
|
args,
|
|
fields: Box::new([FieldExpr { name: FieldIdx::from(0u32), expr }]),
|
|
user_ty: None,
|
|
base: AdtExprBase::None,
|
|
}))
|
|
}
|
|
_ => span_bug!(expr.span, "unexpected type for pinned borrow: {:?}", expr_ty),
|
|
},
|
|
|
|
hir::ExprKind::Block(blk, _) => ExprKind::Block { block: self.mirror_block(blk) },
|
|
|
|
hir::ExprKind::Assign(lhs, rhs, _) => {
|
|
ExprKind::Assign { lhs: self.mirror_expr(lhs), rhs: self.mirror_expr(rhs) }
|
|
}
|
|
|
|
hir::ExprKind::AssignOp(op, lhs, rhs) => {
|
|
if self.typeck_results.is_method_call(expr) {
|
|
let lhs = self.mirror_expr(lhs);
|
|
let rhs = self.mirror_expr(rhs);
|
|
self.overloaded_operator(expr, Box::new([lhs, rhs]))
|
|
} else {
|
|
ExprKind::AssignOp {
|
|
op: assign_op(op.node),
|
|
lhs: self.mirror_expr(lhs),
|
|
rhs: self.mirror_expr(rhs),
|
|
}
|
|
}
|
|
}
|
|
|
|
hir::ExprKind::Lit(lit) => ExprKind::Literal { lit, neg: false },
|
|
|
|
hir::ExprKind::Binary(op, lhs, rhs) => {
|
|
if self.typeck_results.is_method_call(expr) {
|
|
let lhs = self.mirror_expr(lhs);
|
|
let rhs = self.mirror_expr(rhs);
|
|
self.overloaded_operator(expr, Box::new([lhs, rhs]))
|
|
} else {
|
|
match op.node {
|
|
hir::BinOpKind::And => ExprKind::LogicalOp {
|
|
op: LogicalOp::And,
|
|
lhs: self.mirror_expr(lhs),
|
|
rhs: self.mirror_expr(rhs),
|
|
},
|
|
hir::BinOpKind::Or => ExprKind::LogicalOp {
|
|
op: LogicalOp::Or,
|
|
lhs: self.mirror_expr(lhs),
|
|
rhs: self.mirror_expr(rhs),
|
|
},
|
|
_ => {
|
|
let op = bin_op(op.node);
|
|
ExprKind::Binary {
|
|
op,
|
|
lhs: self.mirror_expr(lhs),
|
|
rhs: self.mirror_expr(rhs),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
hir::ExprKind::Index(lhs, index, brackets_span) => {
|
|
if self.typeck_results.is_method_call(expr) {
|
|
let lhs = self.mirror_expr(lhs);
|
|
let index = self.mirror_expr(index);
|
|
self.overloaded_place(
|
|
expr,
|
|
expr_ty,
|
|
None,
|
|
Box::new([lhs, index]),
|
|
brackets_span,
|
|
)
|
|
} else {
|
|
ExprKind::Index { lhs: self.mirror_expr(lhs), index: self.mirror_expr(index) }
|
|
}
|
|
}
|
|
|
|
hir::ExprKind::Unary(hir::UnOp::Deref, arg) => {
|
|
if self.typeck_results.is_method_call(expr) {
|
|
let arg = self.mirror_expr(arg);
|
|
self.overloaded_place(expr, expr_ty, None, Box::new([arg]), expr.span)
|
|
} else {
|
|
ExprKind::Deref { arg: self.mirror_expr(arg) }
|
|
}
|
|
}
|
|
|
|
hir::ExprKind::Unary(hir::UnOp::Not, arg) => {
|
|
if self.typeck_results.is_method_call(expr) {
|
|
let arg = self.mirror_expr(arg);
|
|
self.overloaded_operator(expr, Box::new([arg]))
|
|
} else {
|
|
ExprKind::Unary { op: UnOp::Not, arg: self.mirror_expr(arg) }
|
|
}
|
|
}
|
|
|
|
hir::ExprKind::Unary(hir::UnOp::Neg, arg) => {
|
|
if self.typeck_results.is_method_call(expr) {
|
|
let arg = self.mirror_expr(arg);
|
|
self.overloaded_operator(expr, Box::new([arg]))
|
|
} else if let hir::ExprKind::Lit(lit) = arg.kind {
|
|
ExprKind::Literal { lit, neg: true }
|
|
} else {
|
|
ExprKind::Unary { op: UnOp::Neg, arg: self.mirror_expr(arg) }
|
|
}
|
|
}
|
|
|
|
hir::ExprKind::Struct(qpath, fields, ref base) => match expr_ty.kind() {
|
|
ty::Adt(adt, args) => match adt.adt_kind() {
|
|
AdtKind::Struct | AdtKind::Union => {
|
|
let user_provided_types = self.typeck_results.user_provided_types();
|
|
let user_ty = user_provided_types.get(expr.hir_id).copied().map(Box::new);
|
|
debug!("make_mirror_unadjusted: (struct/union) user_ty={:?}", user_ty);
|
|
ExprKind::Adt(Box::new(AdtExpr {
|
|
adt_def: *adt,
|
|
variant_index: FIRST_VARIANT,
|
|
args,
|
|
user_ty,
|
|
fields: self.field_refs(fields),
|
|
base: match base {
|
|
hir::StructTailExpr::Base(base) => AdtExprBase::Base(FruInfo {
|
|
base: self.mirror_expr(base),
|
|
field_types: self.typeck_results.fru_field_types()[expr.hir_id]
|
|
.iter()
|
|
.copied()
|
|
.collect(),
|
|
}),
|
|
hir::StructTailExpr::DefaultFields(_) => {
|
|
AdtExprBase::DefaultFields(
|
|
self.typeck_results.fru_field_types()[expr.hir_id]
|
|
.iter()
|
|
.copied()
|
|
.collect(),
|
|
)
|
|
}
|
|
hir::StructTailExpr::None
|
|
| hir::StructTailExpr::NoneWithError(_) => AdtExprBase::None,
|
|
},
|
|
}))
|
|
}
|
|
AdtKind::Enum => {
|
|
let res = self.typeck_results.qpath_res(qpath, expr.hir_id);
|
|
match res {
|
|
Res::Def(DefKind::Variant, variant_id) => {
|
|
assert!(matches!(
|
|
base,
|
|
hir::StructTailExpr::None
|
|
| hir::StructTailExpr::DefaultFields(_)
|
|
| hir::StructTailExpr::NoneWithError(_)
|
|
));
|
|
|
|
let index = adt.variant_index_with_id(variant_id);
|
|
let user_provided_types = self.typeck_results.user_provided_types();
|
|
let user_ty =
|
|
user_provided_types.get(expr.hir_id).copied().map(Box::new);
|
|
debug!("make_mirror_unadjusted: (variant) user_ty={:?}", user_ty);
|
|
ExprKind::Adt(Box::new(AdtExpr {
|
|
adt_def: *adt,
|
|
variant_index: index,
|
|
args,
|
|
user_ty,
|
|
fields: self.field_refs(fields),
|
|
base: match base {
|
|
hir::StructTailExpr::DefaultFields(_) => {
|
|
AdtExprBase::DefaultFields(
|
|
self.typeck_results.fru_field_types()[expr.hir_id]
|
|
.iter()
|
|
.copied()
|
|
.collect(),
|
|
)
|
|
}
|
|
hir::StructTailExpr::Base(base) => {
|
|
span_bug!(base.span, "unexpected res: {:?}", res);
|
|
}
|
|
hir::StructTailExpr::None
|
|
| hir::StructTailExpr::NoneWithError(_) => {
|
|
AdtExprBase::None
|
|
}
|
|
},
|
|
}))
|
|
}
|
|
_ => {
|
|
span_bug!(expr.span, "unexpected res: {:?}", res);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_ => {
|
|
span_bug!(expr.span, "unexpected type for struct literal: {:?}", expr_ty);
|
|
}
|
|
},
|
|
|
|
hir::ExprKind::Closure(hir::Closure { .. }) => {
|
|
let closure_ty = self.typeck_results.expr_ty(expr);
|
|
let (def_id, args, movability) = match *closure_ty.kind() {
|
|
ty::Closure(def_id, args) => (def_id, UpvarArgs::Closure(args), None),
|
|
ty::Coroutine(def_id, args) => {
|
|
(def_id, UpvarArgs::Coroutine(args), Some(tcx.coroutine_movability(def_id)))
|
|
}
|
|
ty::CoroutineClosure(def_id, args) => {
|
|
(def_id, UpvarArgs::CoroutineClosure(args), None)
|
|
}
|
|
_ => {
|
|
span_bug!(expr.span, "closure expr w/o closure type: {:?}", closure_ty);
|
|
}
|
|
};
|
|
let def_id = def_id.expect_local();
|
|
|
|
let upvars = self
|
|
.tcx
|
|
.closure_captures(def_id)
|
|
.iter()
|
|
.zip_eq(args.upvar_tys())
|
|
.map(|(captured_place, ty)| {
|
|
let upvars = self.capture_upvar(expr, captured_place, ty);
|
|
self.thir.exprs.push(upvars)
|
|
})
|
|
.collect();
|
|
|
|
// Convert the closure fake reads, if any, from hir `Place` to ExprRef
|
|
let fake_reads = match self.typeck_results.closure_fake_reads.get(&def_id) {
|
|
Some(fake_reads) => fake_reads
|
|
.iter()
|
|
.map(|(place, cause, hir_id)| {
|
|
let expr = self.convert_captured_hir_place(expr, place.clone());
|
|
(self.thir.exprs.push(expr), *cause, *hir_id)
|
|
})
|
|
.collect(),
|
|
None => Vec::new(),
|
|
};
|
|
|
|
ExprKind::Closure(Box::new(ClosureExpr {
|
|
closure_id: def_id,
|
|
args,
|
|
upvars,
|
|
movability,
|
|
fake_reads,
|
|
}))
|
|
}
|
|
|
|
hir::ExprKind::Path(ref qpath) => {
|
|
let res = self.typeck_results.qpath_res(qpath, expr.hir_id);
|
|
self.convert_path_expr(expr, res)
|
|
}
|
|
|
|
hir::ExprKind::InlineAsm(asm) => ExprKind::InlineAsm(Box::new(InlineAsmExpr {
|
|
asm_macro: asm.asm_macro,
|
|
template: asm.template,
|
|
operands: asm
|
|
.operands
|
|
.iter()
|
|
.map(|(op, _op_sp)| match *op {
|
|
hir::InlineAsmOperand::In { reg, expr } => {
|
|
InlineAsmOperand::In { reg, expr: self.mirror_expr(expr) }
|
|
}
|
|
hir::InlineAsmOperand::Out { reg, late, ref expr } => {
|
|
InlineAsmOperand::Out {
|
|
reg,
|
|
late,
|
|
expr: expr.map(|expr| self.mirror_expr(expr)),
|
|
}
|
|
}
|
|
hir::InlineAsmOperand::InOut { reg, late, expr } => {
|
|
InlineAsmOperand::InOut { reg, late, expr: self.mirror_expr(expr) }
|
|
}
|
|
hir::InlineAsmOperand::SplitInOut { reg, late, in_expr, ref out_expr } => {
|
|
InlineAsmOperand::SplitInOut {
|
|
reg,
|
|
late,
|
|
in_expr: self.mirror_expr(in_expr),
|
|
out_expr: out_expr.map(|expr| self.mirror_expr(expr)),
|
|
}
|
|
}
|
|
hir::InlineAsmOperand::Const { ref anon_const } => {
|
|
let ty = self.typeck_results.node_type(anon_const.hir_id);
|
|
let did = anon_const.def_id.to_def_id();
|
|
let typeck_root_def_id = tcx.typeck_root_def_id(did);
|
|
let parent_args = tcx.erase_and_anonymize_regions(
|
|
GenericArgs::identity_for_item(tcx, typeck_root_def_id),
|
|
);
|
|
let args =
|
|
InlineConstArgs::new(tcx, InlineConstArgsParts { parent_args, ty })
|
|
.args;
|
|
|
|
let uneval = mir::UnevaluatedConst::new(did, args);
|
|
let value = mir::Const::Unevaluated(uneval, ty);
|
|
InlineAsmOperand::Const { value, span: tcx.def_span(did) }
|
|
}
|
|
hir::InlineAsmOperand::SymFn { expr } => {
|
|
InlineAsmOperand::SymFn { value: self.mirror_expr(expr) }
|
|
}
|
|
hir::InlineAsmOperand::SymStatic { path: _, def_id } => {
|
|
InlineAsmOperand::SymStatic { def_id }
|
|
}
|
|
hir::InlineAsmOperand::Label { block } => {
|
|
InlineAsmOperand::Label { block: self.mirror_block(block) }
|
|
}
|
|
})
|
|
.collect(),
|
|
options: asm.options,
|
|
line_spans: asm.line_spans,
|
|
})),
|
|
|
|
hir::ExprKind::OffsetOf(_, _) => {
|
|
let offset_of_intrinsic = tcx.require_lang_item(LangItem::OffsetOf, expr.span);
|
|
let mk_u32_kind = |val: u32| ExprKind::NonHirLiteral {
|
|
lit: ScalarInt::try_from_uint(val, Size::from_bits(32)).unwrap(),
|
|
user_ty: None,
|
|
};
|
|
let mk_usize_kind = |val: u64| ExprKind::NonHirLiteral {
|
|
lit: ScalarInt::try_from_target_usize(val, tcx).unwrap(),
|
|
user_ty: None,
|
|
};
|
|
let mk_call =
|
|
|thir: &mut Thir<'tcx>, ty: Ty<'tcx>, variant: VariantIdx, field: FieldIdx| {
|
|
let fun_ty =
|
|
Ty::new_fn_def(tcx, offset_of_intrinsic, [ty::GenericArg::from(ty)]);
|
|
let fun = thir
|
|
.exprs
|
|
.push(mk_expr(ExprKind::ZstLiteral { user_ty: None }, fun_ty));
|
|
let variant =
|
|
thir.exprs.push(mk_expr(mk_u32_kind(variant.as_u32()), tcx.types.u32));
|
|
let field =
|
|
thir.exprs.push(mk_expr(mk_u32_kind(field.as_u32()), tcx.types.u32));
|
|
let args = Box::new([variant, field]);
|
|
ExprKind::Call {
|
|
ty: fun_ty,
|
|
fun,
|
|
args,
|
|
from_hir_call: false,
|
|
fn_span: expr.span,
|
|
}
|
|
};
|
|
|
|
let indices = self.typeck_results.offset_of_data().get(expr.hir_id).unwrap();
|
|
let mut expr = None::<ExprKind<'_>>;
|
|
|
|
for &(container, variant, field) in indices.iter() {
|
|
let next = mk_call(&mut self.thir, container, variant, field);
|
|
expr = Some(match expr.take() {
|
|
None => next,
|
|
Some(last) => {
|
|
let last = self.thir.exprs.push(mk_expr(last, tcx.types.usize));
|
|
let next = self.thir.exprs.push(mk_expr(next, tcx.types.usize));
|
|
ExprKind::Binary { op: BinOp::Add, lhs: last, rhs: next }
|
|
}
|
|
});
|
|
}
|
|
|
|
expr.unwrap_or_else(|| mk_usize_kind(0))
|
|
}
|
|
|
|
hir::ExprKind::ConstBlock(ref anon_const) => {
|
|
let ty = self.typeck_results.node_type(anon_const.hir_id);
|
|
let did = anon_const.def_id.to_def_id();
|
|
let typeck_root_def_id = tcx.typeck_root_def_id(did);
|
|
let parent_args = tcx.erase_and_anonymize_regions(GenericArgs::identity_for_item(
|
|
tcx,
|
|
typeck_root_def_id,
|
|
));
|
|
let args = InlineConstArgs::new(tcx, InlineConstArgsParts { parent_args, ty }).args;
|
|
|
|
ExprKind::ConstBlock { did, args }
|
|
}
|
|
// Now comes the rote stuff:
|
|
hir::ExprKind::Repeat(v, _) => {
|
|
let ty = self.typeck_results.expr_ty(expr);
|
|
let ty::Array(_, count) = ty.kind() else {
|
|
span_bug!(expr.span, "unexpected repeat expr ty: {:?}", ty);
|
|
};
|
|
|
|
ExprKind::Repeat { value: self.mirror_expr(v), count: *count }
|
|
}
|
|
hir::ExprKind::Ret(v) => ExprKind::Return { value: v.map(|v| self.mirror_expr(v)) },
|
|
hir::ExprKind::Become(call) => ExprKind::Become { value: self.mirror_expr(call) },
|
|
hir::ExprKind::Break(dest, ref value) => {
|
|
if find_attr!(self.tcx, expr.hir_id, ConstContinue(_)) {
|
|
match dest.target_id {
|
|
Ok(target_id) => {
|
|
let (Some(value), Some(_)) = (value, dest.label) else {
|
|
let span = expr.span;
|
|
self.tcx.dcx().emit_fatal(ConstContinueMissingLabelOrValue { span })
|
|
};
|
|
|
|
ExprKind::ConstContinue {
|
|
label: region::Scope {
|
|
local_id: target_id.local_id,
|
|
data: region::ScopeData::Node,
|
|
},
|
|
value: self.mirror_expr(value),
|
|
}
|
|
}
|
|
Err(err) => bug!("invalid loop id for break: {}", err),
|
|
}
|
|
} else {
|
|
match dest.target_id {
|
|
Ok(target_id) => ExprKind::Break {
|
|
label: region::Scope {
|
|
local_id: target_id.local_id,
|
|
data: region::ScopeData::Node,
|
|
},
|
|
value: value.map(|value| self.mirror_expr(value)),
|
|
},
|
|
Err(err) => bug!("invalid loop id for break: {}", err),
|
|
}
|
|
}
|
|
}
|
|
hir::ExprKind::Continue(dest) => match dest.target_id {
|
|
Ok(loop_id) => ExprKind::Continue {
|
|
label: region::Scope {
|
|
local_id: loop_id.local_id,
|
|
data: region::ScopeData::Node,
|
|
},
|
|
},
|
|
Err(err) => bug!("invalid loop id for continue: {}", err),
|
|
},
|
|
hir::ExprKind::Let(let_expr) => ExprKind::Let {
|
|
expr: self.mirror_expr(let_expr.init),
|
|
pat: self.pattern_from_hir(let_expr.pat),
|
|
},
|
|
hir::ExprKind::If(cond, then, else_opt) => ExprKind::If {
|
|
if_then_scope: region::Scope {
|
|
local_id: then.hir_id.local_id,
|
|
data: {
|
|
if expr.span.at_least_rust_2024() {
|
|
region::ScopeData::IfThenRescope
|
|
} else {
|
|
region::ScopeData::IfThen
|
|
}
|
|
},
|
|
},
|
|
cond: self.mirror_expr(cond),
|
|
then: self.mirror_expr(then),
|
|
else_opt: else_opt.map(|el| self.mirror_expr(el)),
|
|
},
|
|
hir::ExprKind::Match(discr, arms, match_source) => ExprKind::Match {
|
|
scrutinee: self.mirror_expr(discr),
|
|
arms: arms.iter().map(|a| self.convert_arm(a)).collect(),
|
|
match_source,
|
|
},
|
|
hir::ExprKind::Loop(body, ..) => {
|
|
if find_attr!(self.tcx, expr.hir_id, LoopMatch(_)) {
|
|
let dcx = self.tcx.dcx();
|
|
|
|
// Accept either `state = expr` or `state = expr;`.
|
|
let loop_body_expr = match body.stmts {
|
|
[] => match body.expr {
|
|
Some(expr) => expr,
|
|
None => dcx.emit_fatal(LoopMatchMissingAssignment { span: body.span }),
|
|
},
|
|
[single] if body.expr.is_none() => match single.kind {
|
|
hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr) => expr,
|
|
_ => dcx.emit_fatal(LoopMatchMissingAssignment { span: body.span }),
|
|
},
|
|
[first @ last] | [first, .., last] => dcx
|
|
.emit_fatal(LoopMatchBadStatements { span: first.span.to(last.span) }),
|
|
};
|
|
|
|
let hir::ExprKind::Assign(state, rhs_expr, _) = loop_body_expr.kind else {
|
|
dcx.emit_fatal(LoopMatchMissingAssignment { span: loop_body_expr.span })
|
|
};
|
|
|
|
let hir::ExprKind::Block(block_body, _) = rhs_expr.kind else {
|
|
dcx.emit_fatal(LoopMatchBadRhs { span: rhs_expr.span })
|
|
};
|
|
|
|
// The labeled block should contain one match expression, but defining items is
|
|
// allowed.
|
|
for stmt in block_body.stmts {
|
|
if !matches!(stmt.kind, rustc_hir::StmtKind::Item(_)) {
|
|
dcx.emit_fatal(LoopMatchBadStatements { span: stmt.span })
|
|
}
|
|
}
|
|
|
|
let Some(block_body_expr) = block_body.expr else {
|
|
dcx.emit_fatal(LoopMatchBadRhs { span: block_body.span })
|
|
};
|
|
|
|
let hir::ExprKind::Match(scrutinee, arms, _match_source) = block_body_expr.kind
|
|
else {
|
|
dcx.emit_fatal(LoopMatchBadRhs { span: block_body_expr.span })
|
|
};
|
|
|
|
fn local(
|
|
cx: &mut ThirBuildCx<'_>,
|
|
expr: &rustc_hir::Expr<'_>,
|
|
) -> Option<hir::HirId> {
|
|
if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind
|
|
&& let Res::Local(hir_id) = path.res
|
|
&& !cx.is_upvar(hir_id)
|
|
{
|
|
return Some(hir_id);
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
let Some(scrutinee_hir_id) = local(self, scrutinee) else {
|
|
dcx.emit_fatal(LoopMatchInvalidMatch { span: scrutinee.span })
|
|
};
|
|
|
|
if local(self, state) != Some(scrutinee_hir_id) {
|
|
dcx.emit_fatal(LoopMatchInvalidUpdate {
|
|
scrutinee: scrutinee.span,
|
|
lhs: state.span,
|
|
})
|
|
}
|
|
|
|
ExprKind::LoopMatch {
|
|
state: self.mirror_expr(state),
|
|
region_scope: region::Scope {
|
|
local_id: block_body.hir_id.local_id,
|
|
data: region::ScopeData::Node,
|
|
},
|
|
|
|
match_data: Box::new(LoopMatchMatchData {
|
|
scrutinee: self.mirror_expr(scrutinee),
|
|
arms: arms.iter().map(|a| self.convert_arm(a)).collect(),
|
|
span: block_body_expr.span,
|
|
}),
|
|
}
|
|
} else {
|
|
let block_ty = self.typeck_results.node_type(body.hir_id);
|
|
let block = self.mirror_block(body);
|
|
let body = self.thir.exprs.push(Expr {
|
|
ty: block_ty,
|
|
temp_scope_id: body.hir_id.local_id,
|
|
span: self.thir[block].span,
|
|
kind: ExprKind::Block { block },
|
|
});
|
|
ExprKind::Loop { body }
|
|
}
|
|
}
|
|
hir::ExprKind::Field(source, ..) => ExprKind::Field {
|
|
lhs: self.mirror_expr(source),
|
|
variant_index: FIRST_VARIANT,
|
|
name: self.typeck_results.field_index(expr.hir_id),
|
|
},
|
|
hir::ExprKind::Cast(source, cast_ty) => {
|
|
// Check for a user-given type annotation on this `cast`
|
|
let user_provided_types = self.typeck_results.user_provided_types();
|
|
let user_ty = user_provided_types.get(cast_ty.hir_id);
|
|
|
|
debug!(
|
|
"cast({:?}) has ty w/ hir_id {:?} and user provided ty {:?}",
|
|
expr, cast_ty.hir_id, user_ty,
|
|
);
|
|
|
|
let cast = self.mirror_expr_cast(source, expr.hir_id.local_id, expr.span);
|
|
|
|
if let Some(user_ty) = user_ty {
|
|
// NOTE: Creating a new Expr and wrapping a Cast inside of it may be
|
|
// inefficient, revisit this when performance becomes an issue.
|
|
let cast_expr = self.thir.exprs.push(Expr {
|
|
temp_scope_id: expr.hir_id.local_id,
|
|
ty: expr_ty,
|
|
span: expr.span,
|
|
kind: cast,
|
|
});
|
|
debug!("make_mirror_unadjusted: (cast) user_ty={:?}", user_ty);
|
|
|
|
ExprKind::ValueTypeAscription {
|
|
source: cast_expr,
|
|
user_ty: Some(Box::new(*user_ty)),
|
|
user_ty_span: cast_ty.span,
|
|
}
|
|
} else {
|
|
cast
|
|
}
|
|
}
|
|
hir::ExprKind::Type(source, ty) => {
|
|
let user_provided_types = self.typeck_results.user_provided_types();
|
|
let user_ty = user_provided_types.get(ty.hir_id).copied().map(Box::new);
|
|
debug!("make_mirror_unadjusted: (type) user_ty={:?}", user_ty);
|
|
let mirrored = self.mirror_expr(source);
|
|
if source.is_syntactic_place_expr() {
|
|
ExprKind::PlaceTypeAscription {
|
|
source: mirrored,
|
|
user_ty,
|
|
user_ty_span: ty.span,
|
|
}
|
|
} else {
|
|
ExprKind::ValueTypeAscription {
|
|
source: mirrored,
|
|
user_ty,
|
|
user_ty_span: ty.span,
|
|
}
|
|
}
|
|
}
|
|
|
|
hir::ExprKind::UnsafeBinderCast(UnsafeBinderCastKind::Unwrap, source, _ty) => {
|
|
// FIXME(unsafe_binders): Take into account the ascribed type, too.
|
|
let mirrored = self.mirror_expr(source);
|
|
if source.is_syntactic_place_expr() {
|
|
ExprKind::PlaceUnwrapUnsafeBinder { source: mirrored }
|
|
} else {
|
|
ExprKind::ValueUnwrapUnsafeBinder { source: mirrored }
|
|
}
|
|
}
|
|
hir::ExprKind::UnsafeBinderCast(UnsafeBinderCastKind::Wrap, source, _ty) => {
|
|
// FIXME(unsafe_binders): Take into account the ascribed type, too.
|
|
let mirrored = self.mirror_expr(source);
|
|
ExprKind::WrapUnsafeBinder { source: mirrored }
|
|
}
|
|
|
|
hir::ExprKind::DropTemps(source) => ExprKind::Use { source: self.mirror_expr(source) },
|
|
hir::ExprKind::Array(fields) => ExprKind::Array { fields: self.mirror_exprs(fields) },
|
|
hir::ExprKind::Tup(fields) => ExprKind::Tuple { fields: self.mirror_exprs(fields) },
|
|
|
|
hir::ExprKind::Yield(v, _) => ExprKind::Yield { value: self.mirror_expr(v) },
|
|
hir::ExprKind::Err(_) => unreachable!("cannot lower a `hir::ExprKind::Err` to THIR"),
|
|
};
|
|
|
|
mk_expr(kind, expr_ty)
|
|
}
|
|
|
|
fn user_args_applied_to_res(
|
|
&mut self,
|
|
hir_id: hir::HirId,
|
|
res: Res,
|
|
) -> Option<Box<ty::CanonicalUserType<'tcx>>> {
|
|
debug!("user_args_applied_to_res: res={:?}", res);
|
|
let user_provided_type = match res {
|
|
// A reference to something callable -- e.g., a fn, method, or
|
|
// a tuple-struct or tuple-variant. This has the type of a
|
|
// `Fn` but with the user-given generic parameters.
|
|
Res::Def(DefKind::Fn, _)
|
|
| Res::Def(DefKind::AssocFn, _)
|
|
| Res::Def(DefKind::Ctor(_, CtorKind::Fn), _)
|
|
| Res::Def(DefKind::Const { .. }, _)
|
|
| Res::Def(DefKind::AssocConst { .. }, _) => {
|
|
self.typeck_results.user_provided_types().get(hir_id).copied().map(Box::new)
|
|
}
|
|
|
|
// A unit struct/variant which is used as a value (e.g.,
|
|
// `None`). This has the type of the enum/struct that defines
|
|
// this variant -- but with the generic parameters given by the
|
|
// user.
|
|
Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => {
|
|
self.user_args_applied_to_ty_of_hir_id(hir_id).map(Box::new)
|
|
}
|
|
|
|
// `Self` is used in expression as a tuple struct constructor or a unit struct constructor
|
|
Res::SelfCtor(_) => self.user_args_applied_to_ty_of_hir_id(hir_id).map(Box::new),
|
|
|
|
_ => bug!("user_args_applied_to_res: unexpected res {:?} at {:?}", res, hir_id),
|
|
};
|
|
debug!("user_args_applied_to_res: user_provided_type={:?}", user_provided_type);
|
|
user_provided_type
|
|
}
|
|
|
|
fn method_callee(
|
|
&mut self,
|
|
expr: &hir::Expr<'_>,
|
|
span: Span,
|
|
overloaded_callee: Option<Ty<'tcx>>,
|
|
) -> Expr<'tcx> {
|
|
let (ty, user_ty) = match overloaded_callee {
|
|
Some(fn_def) => (fn_def, None),
|
|
None => {
|
|
let (kind, def_id) =
|
|
self.typeck_results.type_dependent_def(expr.hir_id).unwrap_or_else(|| {
|
|
span_bug!(expr.span, "no type-dependent def for method callee")
|
|
});
|
|
let user_ty = self.user_args_applied_to_res(expr.hir_id, Res::Def(kind, def_id));
|
|
debug!("method_callee: user_ty={:?}", user_ty);
|
|
(
|
|
Ty::new_fn_def(self.tcx, def_id, self.typeck_results.node_args(expr.hir_id)),
|
|
user_ty,
|
|
)
|
|
}
|
|
};
|
|
Expr {
|
|
temp_scope_id: expr.hir_id.local_id,
|
|
ty,
|
|
span,
|
|
kind: ExprKind::ZstLiteral { user_ty },
|
|
}
|
|
}
|
|
|
|
fn convert_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) -> ArmId {
|
|
let arm = Arm {
|
|
pattern: self.pattern_from_hir(&arm.pat),
|
|
guard: arm.guard.as_ref().map(|g| self.mirror_expr(g)),
|
|
body: self.mirror_expr(arm.body),
|
|
hir_id: arm.hir_id,
|
|
scope: region::Scope { local_id: arm.hir_id.local_id, data: region::ScopeData::Node },
|
|
span: arm.span,
|
|
};
|
|
self.thir.arms.push(arm)
|
|
}
|
|
|
|
fn convert_path_expr(&mut self, expr: &'tcx hir::Expr<'tcx>, res: Res) -> ExprKind<'tcx> {
|
|
let args = self.typeck_results.node_args(expr.hir_id);
|
|
match res {
|
|
// A regular function, constructor function or a constant.
|
|
Res::Def(DefKind::Fn, _)
|
|
| Res::Def(DefKind::AssocFn, _)
|
|
| Res::Def(DefKind::Ctor(_, CtorKind::Fn), _)
|
|
| Res::SelfCtor(_) => {
|
|
let user_ty = self.user_args_applied_to_res(expr.hir_id, res);
|
|
ExprKind::ZstLiteral { user_ty }
|
|
}
|
|
|
|
Res::Def(DefKind::ConstParam, def_id) => {
|
|
let hir_id = self.tcx.local_def_id_to_hir_id(def_id.expect_local());
|
|
let generics = self.tcx.generics_of(hir_id.owner);
|
|
let Some(&index) = generics.param_def_id_to_index.get(&def_id) else {
|
|
span_bug!(
|
|
expr.span,
|
|
"Should have already errored about late bound consts: {def_id:?}"
|
|
);
|
|
};
|
|
let name = self.tcx.hir_name(hir_id);
|
|
let param = ty::ParamConst::new(index, name);
|
|
|
|
ExprKind::ConstParam { param, def_id }
|
|
}
|
|
|
|
Res::Def(DefKind::Const { .. }, def_id)
|
|
| Res::Def(DefKind::AssocConst { .. }, def_id) => {
|
|
let user_ty = self.user_args_applied_to_res(expr.hir_id, res);
|
|
ExprKind::NamedConst { def_id, args, user_ty }
|
|
}
|
|
|
|
Res::Def(DefKind::Ctor(_, CtorKind::Const), def_id) => {
|
|
let user_provided_types = self.typeck_results.user_provided_types();
|
|
let user_ty = user_provided_types.get(expr.hir_id).copied().map(Box::new);
|
|
debug!("convert_path_expr: user_ty={:?}", user_ty);
|
|
let ty = self.typeck_results.node_type(expr.hir_id);
|
|
match ty.kind() {
|
|
// A unit struct/variant which is used as a value.
|
|
// We return a completely different ExprKind here to account for this special case.
|
|
ty::Adt(adt_def, args) => ExprKind::Adt(Box::new(AdtExpr {
|
|
adt_def: *adt_def,
|
|
variant_index: adt_def.variant_index_with_ctor_id(def_id),
|
|
args,
|
|
user_ty,
|
|
fields: Box::new([]),
|
|
base: AdtExprBase::None,
|
|
})),
|
|
_ => bug!("unexpected ty: {:?}", ty),
|
|
}
|
|
}
|
|
|
|
// A source Rust `path::to::STATIC` is a place expr like *&ident is.
|
|
// In THIR, we make them exactly equivalent by inserting the implied *& or *&raw,
|
|
// but distinguish between &STATIC and &THREAD_LOCAL as they have different semantics
|
|
Res::Def(DefKind::Static { .. }, id) => {
|
|
// this is &raw for extern static or static mut, and & for other statics
|
|
let ty = self.tcx.static_ptr_ty(id, self.typing_env);
|
|
let kind = if self.tcx.is_thread_local_static(id) {
|
|
ExprKind::ThreadLocalRef(id)
|
|
} else {
|
|
let alloc_id = self.tcx.reserve_and_set_static_alloc(id);
|
|
ExprKind::StaticRef { alloc_id, ty, def_id: id }
|
|
};
|
|
ExprKind::Deref {
|
|
arg: self.thir.exprs.push(Expr {
|
|
ty,
|
|
temp_scope_id: expr.hir_id.local_id,
|
|
span: expr.span,
|
|
kind,
|
|
}),
|
|
}
|
|
}
|
|
|
|
Res::Local(var_hir_id) => self.convert_var(var_hir_id),
|
|
|
|
_ => span_bug!(expr.span, "res `{:?}` not yet implemented", res),
|
|
}
|
|
}
|
|
|
|
fn convert_var(&mut self, var_hir_id: hir::HirId) -> ExprKind<'tcx> {
|
|
// We want upvars here not captures.
|
|
// Captures will be handled in MIR.
|
|
let is_upvar = self.is_upvar(var_hir_id);
|
|
|
|
debug!(
|
|
"convert_var({:?}): is_upvar={}, body_owner={:?}",
|
|
var_hir_id, is_upvar, self.body_owner
|
|
);
|
|
|
|
if is_upvar {
|
|
ExprKind::UpvarRef {
|
|
closure_def_id: self.body_owner,
|
|
var_hir_id: LocalVarId(var_hir_id),
|
|
}
|
|
} else {
|
|
ExprKind::VarRef { id: LocalVarId(var_hir_id) }
|
|
}
|
|
}
|
|
|
|
fn overloaded_operator(
|
|
&mut self,
|
|
expr: &'tcx hir::Expr<'tcx>,
|
|
args: Box<[ExprId]>,
|
|
) -> ExprKind<'tcx> {
|
|
let fun = self.method_callee(expr, expr.span, None);
|
|
let fun = self.thir.exprs.push(fun);
|
|
ExprKind::Call {
|
|
ty: self.thir[fun].ty,
|
|
fun,
|
|
args,
|
|
from_hir_call: false,
|
|
fn_span: expr.span,
|
|
}
|
|
}
|
|
|
|
fn overloaded_place(
|
|
&mut self,
|
|
expr: &'tcx hir::Expr<'tcx>,
|
|
place_ty: Ty<'tcx>,
|
|
overloaded_callee: Option<Ty<'tcx>>,
|
|
args: Box<[ExprId]>,
|
|
span: Span,
|
|
) -> ExprKind<'tcx> {
|
|
// For an overloaded *x or x[y] expression of type T, the method
|
|
// call returns an &T and we must add the deref so that the types
|
|
// line up (this is because `*x` and `x[y]` represent places):
|
|
|
|
// Reconstruct the output assuming it's a reference with the
|
|
// same region and mutability as the receiver. This holds for
|
|
// `Deref(Mut)::deref(_mut)` and `Index(Mut)::index(_mut)`.
|
|
let ty::Ref(region, _, mutbl) = *self.thir[args[0]].ty.kind() else {
|
|
span_bug!(span, "overloaded_place: receiver is not a reference");
|
|
};
|
|
let ref_ty = Ty::new_ref(self.tcx, region, place_ty, mutbl);
|
|
|
|
// construct the complete expression `foo()` for the overloaded call,
|
|
// which will yield the &T type
|
|
let fun = self.method_callee(expr, span, overloaded_callee);
|
|
let fun = self.thir.exprs.push(fun);
|
|
let fun_ty = self.thir[fun].ty;
|
|
let ref_expr = self.thir.exprs.push(Expr {
|
|
temp_scope_id: expr.hir_id.local_id,
|
|
ty: ref_ty,
|
|
span,
|
|
kind: ExprKind::Call { ty: fun_ty, fun, args, from_hir_call: false, fn_span: span },
|
|
});
|
|
|
|
// construct and return a deref wrapper `*foo()`
|
|
ExprKind::Deref { arg: ref_expr }
|
|
}
|
|
|
|
fn convert_captured_hir_place(
|
|
&mut self,
|
|
closure_expr: &'tcx hir::Expr<'tcx>,
|
|
place: HirPlace<'tcx>,
|
|
) -> Expr<'tcx> {
|
|
let temp_scope_id = closure_expr.hir_id.local_id;
|
|
let var_ty = place.base_ty;
|
|
|
|
// The result of capture analysis in `rustc_hir_typeck/src/upvar.rs` represents a captured path
|
|
// as it's seen for use within the closure and not at the time of closure creation.
|
|
//
|
|
// That is we see expect to see it start from a captured upvar and not something that is local
|
|
// to the closure's parent.
|
|
let var_hir_id = match place.base {
|
|
HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
|
|
base => bug!("Expected an upvar, found {:?}", base),
|
|
};
|
|
|
|
let mut captured_place_expr = Expr {
|
|
temp_scope_id,
|
|
ty: var_ty,
|
|
span: closure_expr.span,
|
|
kind: self.convert_var(var_hir_id),
|
|
};
|
|
|
|
for proj in place.projections.iter() {
|
|
let kind = match proj.kind {
|
|
HirProjectionKind::Deref => {
|
|
ExprKind::Deref { arg: self.thir.exprs.push(captured_place_expr) }
|
|
}
|
|
HirProjectionKind::Field(field, variant_index) => ExprKind::Field {
|
|
lhs: self.thir.exprs.push(captured_place_expr),
|
|
variant_index,
|
|
name: field,
|
|
},
|
|
HirProjectionKind::OpaqueCast => {
|
|
ExprKind::Use { source: self.thir.exprs.push(captured_place_expr) }
|
|
}
|
|
HirProjectionKind::UnwrapUnsafeBinder => ExprKind::PlaceUnwrapUnsafeBinder {
|
|
source: self.thir.exprs.push(captured_place_expr),
|
|
},
|
|
HirProjectionKind::Index | HirProjectionKind::Subslice => {
|
|
// We don't capture these projections, so we can ignore them here
|
|
continue;
|
|
}
|
|
};
|
|
|
|
captured_place_expr =
|
|
Expr { temp_scope_id, ty: proj.ty, span: closure_expr.span, kind };
|
|
}
|
|
|
|
captured_place_expr
|
|
}
|
|
|
|
fn capture_upvar(
|
|
&mut self,
|
|
closure_expr: &'tcx hir::Expr<'tcx>,
|
|
captured_place: &'tcx ty::CapturedPlace<'tcx>,
|
|
upvar_ty: Ty<'tcx>,
|
|
) -> Expr<'tcx> {
|
|
let upvar_capture = captured_place.info.capture_kind;
|
|
let captured_place_expr =
|
|
self.convert_captured_hir_place(closure_expr, captured_place.place.clone());
|
|
let temp_scope_id = closure_expr.hir_id.local_id;
|
|
|
|
match upvar_capture {
|
|
ty::UpvarCapture::ByValue => captured_place_expr,
|
|
ty::UpvarCapture::ByUse => {
|
|
let span = captured_place_expr.span;
|
|
let expr_id = self.thir.exprs.push(captured_place_expr);
|
|
|
|
Expr {
|
|
temp_scope_id,
|
|
ty: upvar_ty,
|
|
span: closure_expr.span,
|
|
kind: ExprKind::ByUse { expr: expr_id, span },
|
|
}
|
|
}
|
|
ty::UpvarCapture::ByRef(upvar_borrow) => {
|
|
let borrow_kind = match upvar_borrow {
|
|
ty::BorrowKind::Immutable => BorrowKind::Shared,
|
|
ty::BorrowKind::UniqueImmutable => {
|
|
BorrowKind::Mut { kind: mir::MutBorrowKind::ClosureCapture }
|
|
}
|
|
ty::BorrowKind::Mutable => {
|
|
BorrowKind::Mut { kind: mir::MutBorrowKind::Default }
|
|
}
|
|
};
|
|
Expr {
|
|
temp_scope_id,
|
|
ty: upvar_ty,
|
|
span: closure_expr.span,
|
|
kind: ExprKind::Borrow {
|
|
borrow_kind,
|
|
arg: self.thir.exprs.push(captured_place_expr),
|
|
},
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn is_upvar(&mut self, var_hir_id: hir::HirId) -> bool {
|
|
self.tcx
|
|
.upvars_mentioned(self.body_owner)
|
|
.is_some_and(|upvars| upvars.contains_key(&var_hir_id))
|
|
}
|
|
|
|
/// Converts a list of named fields (i.e., for struct-like struct/enum ADTs) into FieldExpr.
|
|
fn field_refs(&mut self, fields: &'tcx [hir::ExprField<'tcx>]) -> Box<[FieldExpr]> {
|
|
fields
|
|
.iter()
|
|
.map(|field| FieldExpr {
|
|
name: self.typeck_results.field_index(field.hir_id),
|
|
expr: self.mirror_expr(field.expr),
|
|
})
|
|
.collect()
|
|
}
|
|
}
|
|
|
|
trait ToBorrowKind {
|
|
fn to_borrow_kind(&self) -> BorrowKind;
|
|
}
|
|
|
|
impl ToBorrowKind for AutoBorrowMutability {
|
|
fn to_borrow_kind(&self) -> BorrowKind {
|
|
use rustc_middle::ty::adjustment::AllowTwoPhase;
|
|
match *self {
|
|
AutoBorrowMutability::Mut { allow_two_phase_borrow } => BorrowKind::Mut {
|
|
kind: match allow_two_phase_borrow {
|
|
AllowTwoPhase::Yes => mir::MutBorrowKind::TwoPhaseBorrow,
|
|
AllowTwoPhase::No => mir::MutBorrowKind::Default,
|
|
},
|
|
},
|
|
AutoBorrowMutability::Not => BorrowKind::Shared,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ToBorrowKind for hir::Mutability {
|
|
fn to_borrow_kind(&self) -> BorrowKind {
|
|
match *self {
|
|
hir::Mutability::Mut => BorrowKind::Mut { kind: mir::MutBorrowKind::Default },
|
|
hir::Mutability::Not => BorrowKind::Shared,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn bin_op(op: hir::BinOpKind) -> BinOp {
|
|
match op {
|
|
hir::BinOpKind::Add => BinOp::Add,
|
|
hir::BinOpKind::Sub => BinOp::Sub,
|
|
hir::BinOpKind::Mul => BinOp::Mul,
|
|
hir::BinOpKind::Div => BinOp::Div,
|
|
hir::BinOpKind::Rem => BinOp::Rem,
|
|
hir::BinOpKind::BitXor => BinOp::BitXor,
|
|
hir::BinOpKind::BitAnd => BinOp::BitAnd,
|
|
hir::BinOpKind::BitOr => BinOp::BitOr,
|
|
hir::BinOpKind::Shl => BinOp::Shl,
|
|
hir::BinOpKind::Shr => BinOp::Shr,
|
|
hir::BinOpKind::Eq => BinOp::Eq,
|
|
hir::BinOpKind::Lt => BinOp::Lt,
|
|
hir::BinOpKind::Le => BinOp::Le,
|
|
hir::BinOpKind::Ne => BinOp::Ne,
|
|
hir::BinOpKind::Ge => BinOp::Ge,
|
|
hir::BinOpKind::Gt => BinOp::Gt,
|
|
_ => bug!("no equivalent for ast binop {:?}", op),
|
|
}
|
|
}
|
|
|
|
fn assign_op(op: hir::AssignOpKind) -> AssignOp {
|
|
match op {
|
|
hir::AssignOpKind::AddAssign => AssignOp::AddAssign,
|
|
hir::AssignOpKind::SubAssign => AssignOp::SubAssign,
|
|
hir::AssignOpKind::MulAssign => AssignOp::MulAssign,
|
|
hir::AssignOpKind::DivAssign => AssignOp::DivAssign,
|
|
hir::AssignOpKind::RemAssign => AssignOp::RemAssign,
|
|
hir::AssignOpKind::BitXorAssign => AssignOp::BitXorAssign,
|
|
hir::AssignOpKind::BitAndAssign => AssignOp::BitAndAssign,
|
|
hir::AssignOpKind::BitOrAssign => AssignOp::BitOrAssign,
|
|
hir::AssignOpKind::ShlAssign => AssignOp::ShlAssign,
|
|
hir::AssignOpKind::ShrAssign => AssignOp::ShrAssign,
|
|
}
|
|
}
|