| //! This module contains the functionality to convert from the wacky tcx data | |
| //! structures into the THIR. The `builder` is generally ignorant of the tcx, | |
| //! etc., and instead goes through the `Cx` for most of its work. | |
| use rustc_data_structures::steal::Steal; | |
| use rustc_errors::ErrorGuaranteed; | |
| use rustc_hir::def::DefKind; | |
| use rustc_hir::def_id::{DefId, LocalDefId}; | |
| use rustc_hir::lang_items::LangItem; | |
| use rustc_hir::{self as hir, HirId, find_attr}; | |
| use rustc_middle::bug; | |
| use rustc_middle::thir::*; | |
| use rustc_middle::ty::{self, TyCtxt}; | |
| /// Query implementation for [`TyCtxt::thir_body`]. | |
| pub(crate) fn thir_body<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| owner_def: LocalDefId, | |
| ) -> Result<(&'tcx Steal<Thir<'tcx>>, ExprId), ErrorGuaranteed> { | |
| debug_assert!(!tcx.is_type_const(owner_def.to_def_id()), "thir_body queried for type_const"); | |
| let body = tcx.hir_body_owned_by(owner_def); | |
| let mut cx: ThirBuildCx<'tcx> = ThirBuildCx::new(tcx, owner_def); | |
| if let Some(reported) = cx.typeck_results.tainted_by_errors { | |
| return Err(reported); | |
| } | |
| // Lower the params before the body's expression so errors from params are shown first. | |
| let owner_id = tcx.local_def_id_to_hir_id(owner_def); | |
| if let Some(fn_decl) = tcx.hir_fn_decl_by_hir_id(owner_id) { | |
| let closure_env_param = cx.closure_env_param(owner_def, owner_id); | |
| let explicit_params = cx.explicit_params(owner_id, fn_decl, &body); | |
| cx.thir.params = closure_env_param.into_iter().chain(explicit_params).collect(); | |
| // The resume argument may be missing, in that case we need to provide it here. | |
| // It will always be `()` in this case. | |
| if tcx.is_coroutine(owner_def.to_def_id()) && body.params.is_empty() { | |
| cx.thir.params.push(Param { | |
| ty: tcx.types.unit, | |
| pat: None, | |
| ty_span: None, | |
| self_kind: None, | |
| hir_id: None, | |
| }); | |
| } | |
| } | |
| let expr = cx.mirror_expr(body.value); | |
| Ok((tcx.alloc_steal_thir(cx.thir), expr)) | |
| } | |
| /// Context for lowering HIR to THIR for a single function body (or other kind of body). | |
| pub(crate) struct ThirBuildCx<'tcx> { | |
| tcx: TyCtxt<'tcx>, | |
| /// The THIR data that this context is building. | |
| thir: Thir<'tcx>, | |
| typing_env: ty::TypingEnv<'tcx>, | |
| typeck_results: &'tcx ty::TypeckResults<'tcx>, | |
| /// False to indicate that adjustments should not be applied. Only used for `custom_mir` | |
| apply_adjustments: bool, | |
| /// The `DefId` of the owner of this body. | |
| body_owner: DefId, | |
| } | |
| impl<'tcx> ThirBuildCx<'tcx> { | |
| fn new(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Self { | |
| let typeck_results = tcx.typeck(def); | |
| let hir_id = tcx.local_def_id_to_hir_id(def); | |
| let body_type = match tcx.hir_body_owner_kind(def) { | |
| rustc_hir::BodyOwnerKind::Fn | rustc_hir::BodyOwnerKind::Closure => { | |
| // fetch the fully liberated fn signature (that is, all bound | |
| // types/lifetimes replaced) | |
| BodyTy::Fn(typeck_results.liberated_fn_sigs()[hir_id]) | |
| } | |
| rustc_hir::BodyOwnerKind::Const { .. } | rustc_hir::BodyOwnerKind::Static(_) => { | |
| // Get the revealed type of this const. This is *not* the adjusted | |
| // type of its body, which may be a subtype of this type. For | |
| // example: | |
| // | |
| // fn foo(_: &()) {} | |
| // static X: fn(&'static ()) = foo; | |
| // | |
| // The adjusted type of the body of X is `for<'a> fn(&'a ())` which | |
| // is not the same as the type of X. We need the type of the return | |
| // place to be the type of the constant because NLL typeck will | |
| // equate them. | |
| BodyTy::Const(typeck_results.node_type(hir_id)) | |
| } | |
| rustc_hir::BodyOwnerKind::GlobalAsm => { | |
| BodyTy::GlobalAsm(typeck_results.node_type(hir_id)) | |
| } | |
| }; | |
| Self { | |
| tcx, | |
| thir: Thir::new(body_type), | |
| // FIXME(#132279): We're in a body, we should use a typing | |
| // mode which reveals the opaque types defined by that body. | |
| typing_env: ty::TypingEnv::non_body_analysis(tcx, def), | |
| typeck_results, | |
| body_owner: def.to_def_id(), | |
| apply_adjustments: !find_attr!(tcx, hir_id, CustomMir(..)), | |
| } | |
| } | |
| fn pattern_from_hir(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tcx>> { | |
| self.pattern_from_hir_with_annotation(pat, None) | |
| } | |
| fn pattern_from_hir_with_annotation( | |
| &mut self, | |
| pat: &'tcx hir::Pat<'tcx>, | |
| let_stmt_type: Option<&hir::Ty<'tcx>>, | |
| ) -> Box<Pat<'tcx>> { | |
| crate::thir::pattern::pat_from_hir( | |
| self, | |
| self.tcx, | |
| self.typing_env, | |
| self.typeck_results, | |
| pat, | |
| let_stmt_type, | |
| ) | |
| } | |
| fn closure_env_param(&self, owner_def: LocalDefId, expr_id: HirId) -> Option<Param<'tcx>> { | |
| if self.tcx.def_kind(owner_def) != DefKind::Closure { | |
| return None; | |
| } | |
| let closure_ty = self.typeck_results.node_type(expr_id); | |
| Some(match *closure_ty.kind() { | |
| ty::Coroutine(..) => { | |
| Param { ty: closure_ty, pat: None, ty_span: None, self_kind: None, hir_id: None } | |
| } | |
| ty::Closure(_, args) => { | |
| let closure_env_ty = self.tcx.closure_env_ty( | |
| closure_ty, | |
| args.as_closure().kind(), | |
| self.tcx.lifetimes.re_erased, | |
| ); | |
| Param { | |
| ty: closure_env_ty, | |
| pat: None, | |
| ty_span: None, | |
| self_kind: None, | |
| hir_id: None, | |
| } | |
| } | |
| ty::CoroutineClosure(_, args) => { | |
| let closure_env_ty = self.tcx.closure_env_ty( | |
| closure_ty, | |
| args.as_coroutine_closure().kind(), | |
| self.tcx.lifetimes.re_erased, | |
| ); | |
| Param { | |
| ty: closure_env_ty, | |
| pat: None, | |
| ty_span: None, | |
| self_kind: None, | |
| hir_id: None, | |
| } | |
| } | |
| _ => bug!("unexpected closure type: {closure_ty}"), | |
| }) | |
| } | |
| fn explicit_params( | |
| &mut self, | |
| owner_id: HirId, | |
| fn_decl: &'tcx hir::FnDecl<'tcx>, | |
| body: &'tcx hir::Body<'tcx>, | |
| ) -> impl Iterator<Item = Param<'tcx>> { | |
| let fn_sig = self.typeck_results.liberated_fn_sigs()[owner_id]; | |
| body.params.iter().enumerate().map(move |(index, param)| { | |
| let ty_span = fn_decl | |
| .inputs | |
| .get(index) | |
| // Make sure that inferred closure args have no type span | |
| .and_then(|ty| if param.pat.span != ty.span { Some(ty.span) } else { None }); | |
| let self_kind = if index == 0 && fn_decl.implicit_self().has_implicit_self() { | |
| Some(fn_decl.implicit_self()) | |
| } else { | |
| None | |
| }; | |
| // C-variadic fns also have a `VaList` input that's not listed in `fn_sig` | |
| // (as it's created inside the body itself, not passed in from outside). | |
| let ty = if fn_decl.c_variadic() && index == fn_decl.inputs.len() { | |
| let va_list_did = self.tcx.require_lang_item(LangItem::VaList, param.span); | |
| self.tcx | |
| .type_of(va_list_did) | |
| .instantiate(self.tcx, &[self.tcx.lifetimes.re_erased.into()]) | |
| .skip_norm_wip() | |
| } else { | |
| fn_sig.inputs()[index] | |
| }; | |
| let pat: Box<Pat<'tcx>> = self.pattern_from_hir(param.pat); | |
| Param { pat: Some(pat), ty, ty_span, self_kind, hir_id: Some(param.hir_id) } | |
| }) | |
| } | |
| fn user_args_applied_to_ty_of_hir_id( | |
| &self, | |
| hir_id: HirId, | |
| ) -> Option<ty::CanonicalUserType<'tcx>> { | |
| crate::thir::util::user_args_applied_to_ty_of_hir_id(self.tcx, self.typeck_results, hir_id) | |
| } | |
| } | |
| mod block; | |
| mod expr; |