diff --git a/CHANGELOG.md b/CHANGELOG.md index 554288901..b1610b7de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,17 @@ Changes to the Rust Engine: expressions, pattern-matching) (#1623) - Update name rendering infrastructure in the Lean backend (#1623, #1624) +Changes to the frontend: +- Add an explicit `Self: Trait` clause to trait methods and consts (#1559) +- Fix `ImplExpr::Builtin` that had some type errors (#1559) +- Improve the translation of `Drop` information (#1559) +- Add variance information to type parameters (#1559) +- Cleanup the `State` infrastructure a little bit (#1559) +- Add information about the metadata to use in unsize coercions (#1559) +- Resolve `dyn Trait` predicates (#1559) +- Many improvements to `FullDef` (#1559) +- Add infrastructure to get a monomorphized `FullDef`; this is used in charon to monomorphize a crate graph (#1559) + Miscellaneous: - A lean tutorial has been added to the hax website. diff --git a/engine/backends/fstar/fstar_backend.ml b/engine/backends/fstar/fstar_backend.ml index 276314711..b523b4585 100644 --- a/engine/backends/fstar/fstar_backend.ml +++ b/engine/backends/fstar/fstar_backend.ml @@ -2003,6 +2003,42 @@ let unsize_as_identity = in visitor#visit_item () +(** Rewrites, in trait, local bounds refereing to `Self` into the impl expr of + kind `Self`. *) +let local_self_bound_as_self (i : AST.item) : AST.item = + match i.v with + | Trait { name; generics; _ } -> + let generic_eq (param : generic_param) (value : generic_value) = + (let* id = + match (param.kind, value) with + | GPConst _, GConst { e = LocalVar id } -> Some id + | GPType, GType (TParam id) -> Some id + | GPLifetime _, GLifetime _ -> Some param.ident + | _ -> None + in + Some ([%eq: local_ident] id param.ident)) + |> Option.value ~default:false + in + let generics_eq params values = + match List.for_all2 ~f:generic_eq params values with + | List.Or_unequal_lengths.Ok v -> v + | List.Or_unequal_lengths.Unequal_lengths -> false + in + (object + inherit [_] U.Visitors.map as super + + method! visit_impl_expr () ie = + match ie with + | { kind = LocalBound _; goal = { args; trait } } + when [%eq: concrete_ident] trait name + && generics_eq generics.params args -> + { ie with kind = Self } + | _ -> ie + end) + #visit_item + () i + | _ -> i + let apply_phases (bo : BackendOptions.t) (items : Ast.Rust.item list) : AST.item list = let items = @@ -2022,6 +2058,7 @@ let apply_phases (bo : BackendOptions.t) (items : Ast.Rust.item list) : TransformToInputLanguage.ditems items |> List.map ~f:unsize_as_identity |> List.map ~f:unsize_as_identity + |> List.map ~f:local_self_bound_as_self |> List.map ~f:U.Mappers.add_typ_ascription in items diff --git a/engine/lib/import_thir.ml b/engine/lib/import_thir.ml index a7caad68e..488af1546 100644 --- a/engine/lib/import_thir.ml +++ b/engine/lib/import_thir.ml @@ -547,7 +547,8 @@ end) : EXPR = struct { item = { - value = { def_id = id; generic_args; impl_exprs; in_trait }; + value = + { def_id = id; generic_args; impl_exprs; in_trait; _ }; _; }; _; @@ -1025,7 +1026,7 @@ end) : EXPR = struct | ClosureFnPointer Safe | ReifyFnPointer -> (* we have arrow types, we do not distinguish between top-level functions and closures *) (c_expr source).e - | Unsize -> + | Unsize _ -> (* https://doc.rust-lang.org/std/marker/trait.Unsize.html *) (U.call Rust_primitives__unsize [ c_expr source ] span typ).e (* let source = c_expr source in *) @@ -1114,21 +1115,25 @@ end) : EXPR = struct | Error -> assertion_failure [ span ] "got type `Error`: Rust compilation probably failed." - | Dynamic (predicates, _region, Dyn) -> ( + | Dynamic (_, predicates, _region) -> ( let goals, non_traits = List.partition_map - ~f:(fun pred -> - match pred.value with - | Trait { args; def_id } -> + ~f:(fun ((clause, _span) : Types.clause * _) -> + match clause.kind.value with + | Trait { trait_ref; _ } -> let goal : dyn_trait_goal = { - trait = Concrete_ident.of_def_id ~value:false def_id; - non_self_args = List.map ~f:(c_generic_value span) args; + trait = + Concrete_ident.of_def_id ~value:false + trait_ref.value.def_id; + non_self_args = + List.map ~f:(c_generic_value span) + (List.tl_exn trait_ref.value.generic_args); } in First goal | _ -> Second ()) - predicates + predicates.predicates in match non_traits with | [] -> TDyn { witness = W.dyn; goals } @@ -1150,7 +1155,7 @@ end) : EXPR = struct and c_impl_expr (span : Thir.span) (ie : Thir.impl_expr) : impl_expr = let goal = c_trait_ref span ie.trait.value in - let impl = { kind = c_impl_expr_atom span ie.impl; goal } in + let impl = { kind = c_impl_expr_atom span ie.impl goal; goal } in match ie.impl with | Concrete { value = { impl_exprs = []; _ }; _ } -> impl | Concrete { value = { impl_exprs; _ }; _ } -> @@ -1163,7 +1168,7 @@ end) : EXPR = struct let args = List.map ~f:(c_generic_value span) tr.value.generic_args in { trait; args } - and c_impl_expr_atom (span : Thir.span) (ie : Thir.impl_expr_atom) : + and c_impl_expr_atom (span : Thir.span) (ie : Thir.impl_expr_atom) goal : impl_expr_kind = let browse_path (item_kind : impl_expr_kind) (chunk : Thir.impl_expr_path_chunk) = @@ -1196,8 +1201,7 @@ end) : EXPR = struct List.fold ~init ~f:browse_path path | Dyn -> Dyn | SelfImpl { path; _ } -> List.fold ~init:Self ~f:browse_path path - | Builtin { trait; _ } -> Builtin (c_trait_ref span trait.value) - | Drop _ -> failwith @@ "impl_expr_atom: Drop" + | Builtin _ -> Builtin goal | Error str -> failwith @@ "impl_expr_atom: Error " ^ str and c_generic_value (span : Thir.span) (ty : Thir.generic_arg) : generic_value diff --git a/frontend/exporter/src/body.rs b/frontend/exporter/src/body.rs index 436a42f98..6ff26b6a1 100644 --- a/frontend/exporter/src/body.rs +++ b/frontend/exporter/src/body.rs @@ -13,6 +13,7 @@ mod module { def_id::{DefId as RDefId, LocalDefId as RLocalDefId}, hir_id::OwnerId as ROwnerId, }; + use rustc_middle::ty; mod store { //! This module helps at store bodies to avoid stealing. @@ -97,7 +98,7 @@ mod module { } pub use store::*; - pub fn get_thir<'tcx, S: UnderOwnerState<'tcx>>( + pub fn get_thir<'tcx, S: BaseState<'tcx>>( did: RLocalDefId, s: &S, ) -> ( @@ -122,7 +123,11 @@ mod module { } pub trait IsBody: Sized + std::fmt::Debug + Clone + 'static { - fn body<'tcx, S: UnderOwnerState<'tcx>>(did: RDefId, s: &S) -> Option; + fn body<'tcx, S: UnderOwnerState<'tcx>>( + s: &S, + did: RDefId, + instantiate: Option>, + ) -> Option; /// Reuse a MIR body we already got. Panic if that's impossible. fn from_mir<'tcx, S: UnderOwnerState<'tcx>>( @@ -133,7 +138,7 @@ mod module { } } - pub fn make_fn_def<'tcx, Body: IsBody, S: UnderOwnerState<'tcx>>( + pub fn make_fn_def<'tcx, Body: IsBody, S: BaseState<'tcx>>( fn_sig: &rustc_hir::FnSig, body_id: &rustc_hir::BodyId, s: &S, @@ -142,11 +147,11 @@ mod module { let ldid = hir_id.owner.def_id; let (thir, expr_entrypoint) = get_thir(ldid, s); - let s = &with_owner_id(s.base(), thir.clone(), (), ldid.to_def_id()); + let s = &s.with_owner_id(ldid.to_def_id()).with_thir(thir.clone()); FnDef { params: thir.params.raw.sinto(s), ret: thir.exprs[expr_entrypoint].ty.sinto(s), - body: Body::body(ldid.to_def_id(), s).s_unwrap(s), + body: Body::body(s, ldid.to_def_id(), None).s_unwrap(s), sig_span: fn_sig.span.sinto(s), header: fn_sig.header.sinto(s), } @@ -161,13 +166,17 @@ mod module { // be local. It is safe to do so, because if we have access to HIR objects, // it necessarily means we are exploring a local item (we don't have // access to the HIR of external objects, only their MIR). - Body::body(s.base().tcx.hir_body_owner_def_id(id).to_def_id(), s).s_unwrap(s) + Body::body(s, s.base().tcx.hir_body_owner_def_id(id).to_def_id(), None).s_unwrap(s) } mod implementations { use super::*; impl IsBody for () { - fn body<'tcx, S: UnderOwnerState<'tcx>>(_did: RDefId, _s: &S) -> Option { + fn body<'tcx, S: UnderOwnerState<'tcx>>( + _s: &S, + _did: RDefId, + _instantiate: Option>, + ) -> Option { Some(()) } fn from_mir<'tcx, S: UnderOwnerState<'tcx>>( @@ -178,9 +187,15 @@ mod module { } } impl IsBody for ThirBody { - fn body<'tcx, S: UnderOwnerState<'tcx>>(did: RDefId, s: &S) -> Option { + fn body<'tcx, S: BaseState<'tcx>>( + s: &S, + did: RDefId, + instantiate: Option>, + ) -> Option { let did = did.as_local()?; let (thir, expr) = get_thir(did, s); + assert!(instantiate.is_none(), "monomorphized thir isn't supported"); + let s = &s.with_owner_id(did.to_def_id()); Some(if *CORE_EXTRACTION_MODE { let expr = &thir.exprs[expr]; Decorated { @@ -191,22 +206,33 @@ mod module { span: expr.span.sinto(s), } } else { - expr.sinto(&with_owner_id(s.base(), thir, (), did.to_def_id())) + expr.sinto(&s.with_thir(thir)) }) } } impl IsBody for (A, B) { - fn body<'tcx, S: UnderOwnerState<'tcx>>(did: RDefId, s: &S) -> Option { - Some((A::body(did, s)?, B::body(did, s)?)) + fn body<'tcx, S: UnderOwnerState<'tcx>>( + s: &S, + did: RDefId, + instantiate: Option>, + ) -> Option { + Some((A::body(s, did, instantiate)?, B::body(s, did, instantiate)?)) } } impl IsBody for MirBody { - fn body<'tcx, S: UnderOwnerState<'tcx>>(did: RDefId, s: &S) -> Option { - MirKind::get_mir(s.base().tcx, did, |body| { - let body = Rc::new(body.clone()); - body.sinto(&with_owner_id(s.base(), (), body.clone(), did)) + fn body<'tcx, S: UnderOwnerState<'tcx>>( + s: &S, + did: RDefId, + instantiate: Option>, + ) -> Option { + let tcx = s.base().tcx; + let typing_env = s.typing_env(); + MirKind::get_mir(tcx, did, |body| { + let body = substitute(tcx, typing_env, instantiate, body.clone()); + let body = Rc::new(body); + body.sinto(&s.with_mir(body.clone())) }) } fn from_mir<'tcx, S: UnderOwnerState<'tcx>>( @@ -214,13 +240,7 @@ mod module { body: rustc_middle::mir::Body<'tcx>, ) -> Option { let body = Rc::new(body.clone()); - let s = &State { - base: s.base(), - owner_id: s.owner_id(), - mir: body.clone(), - binder: (), - thir: (), - }; + let s = &s.with_mir(body.clone()); Some(body.sinto(s)) } } diff --git a/frontend/exporter/src/id_table.rs b/frontend/exporter/src/id_table.rs index cbcb335f1..174b2105f 100644 --- a/frontend/exporter/src/id_table.rs +++ b/frontend/exporter/src/id_table.rs @@ -24,7 +24,7 @@ use std::{ /// Unique IDs in a ID table. #[derive_group(Serializers)] -#[derive(Default, Clone, Debug, JsonSchema, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Default, Clone, Copy, Debug, JsonSchema, Hash, PartialEq, Eq, PartialOrd, Ord)] #[serde(transparent)] pub struct Id { id: u32, @@ -88,7 +88,7 @@ impl SupportedType for ItemRefContents { } /// A node is a bundle of an ID with a value. -#[derive(Deserialize, Serialize, Debug, JsonSchema, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Deserialize, Serialize, Debug, JsonSchema, PartialOrd, Ord)] #[serde(into = "serde_repr::NodeRepr")] #[serde(try_from = "serde_repr::NodeRepr")] pub struct Node> { @@ -111,6 +111,12 @@ impl + Hash> Hash for Node { self.value.as_ref().hash(state); } } +impl + Eq> Eq for Node {} +impl + PartialEq> PartialEq for Node { + fn eq(&self, other: &Self) -> bool { + self.value == other.value + } +} /// Manual implementation of `Clone` that doesn't require a `Clone` /// bound on `T`. @@ -203,6 +209,10 @@ impl> Node { pub fn inner(&self) -> &Arc { &self.value } + + pub fn id(&self) -> Id { + self.id + } } /// Wrapper for a type `T` that creates a bundle containing both a ID diff --git a/frontend/exporter/src/rustc_utils.rs b/frontend/exporter/src/rustc_utils.rs index 1d7f51bfc..97ced417f 100644 --- a/frontend/exporter/src/rustc_utils.rs +++ b/frontend/exporter/src/rustc_utils.rs @@ -1,5 +1,33 @@ use crate::prelude::*; -use rustc_middle::ty; +use rustc_hir::def::DefKind as RDefKind; +use rustc_middle::{mir, ty}; + +pub fn inst_binder<'tcx, T>( + tcx: ty::TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + args: Option>, + x: ty::EarlyBinder<'tcx, T>, +) -> T +where + T: ty::TypeFoldable> + Clone, +{ + match args { + None => x.instantiate_identity(), + Some(args) => tcx.normalize_erasing_regions(typing_env, x.instantiate(tcx, args)), + } +} + +pub fn substitute<'tcx, T>( + tcx: ty::TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + args: Option>, + x: T, +) -> T +where + T: ty::TypeFoldable>, +{ + inst_binder(tcx, typing_env, args, ty::EarlyBinder::bind(x)) +} #[extension_traits::extension(pub trait SubstBinder)] impl<'tcx, T: ty::TypeFoldable>> ty::Binder<'tcx, T> { @@ -12,6 +40,16 @@ impl<'tcx, T: ty::TypeFoldable>> ty::Binder<'tcx, T> { } } +/// Whether the item can have generic parameters. +pub(crate) fn can_have_generics<'tcx>(tcx: ty::TyCtxt<'tcx>, def_id: RDefId) -> bool { + use RDefKind::*; + match get_def_kind(tcx, def_id) { + Mod | ConstParam | TyParam | LifetimeParam | Macro(..) | ExternCrate | Use | ForeignMod + | GlobalAsm => false, + _ => true, + } +} + #[tracing::instrument(skip(s))] pub(crate) fn get_variant_information<'s, S: UnderOwnerState<'s>>( adt_def: &ty::AdtDef<'s>, @@ -76,7 +114,13 @@ pub trait HasParamEnv<'tcx> { impl<'tcx, S: UnderOwnerState<'tcx>> HasParamEnv<'tcx> for S { fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.base().tcx.param_env(self.owner_id()) + let tcx = self.base().tcx; + let def_id = self.owner_id(); + if can_have_generics(tcx, def_id) { + tcx.param_env(def_id) + } else { + ty::ParamEnv::empty() + } } fn typing_env(&self) -> ty::TypingEnv<'tcx> { ty::TypingEnv { @@ -114,3 +158,212 @@ pub fn get_closest_parent_type( _ => get_closest_parent_type(tcx, tcx.parent(id)), } } + +/// Gets the visibility (`pub` or not) of the definition. Returns `None` for defs that don't have a +/// meaningful visibility. +pub fn get_def_visibility<'tcx>( + tcx: ty::TyCtxt<'tcx>, + def_id: RDefId, + def_kind: RDefKind, +) -> Option { + use RDefKind::*; + match def_kind { + AssocConst + | AssocFn + | Const + | Enum + | Field + | Fn + | ForeignTy + | Macro { .. } + | Mod + | Static { .. } + | Struct + | Trait + | TraitAlias + | TyAlias { .. } + | Union + | Use + | Variant => Some(tcx.visibility(def_id).is_public()), + // These kinds don't have visibility modifiers (which would cause `visibility` to panic). + AnonConst + | AssocTy + | Closure + | ConstParam + | Ctor { .. } + | ExternCrate + | ForeignMod + | GlobalAsm + | Impl { .. } + | InlineConst + | LifetimeParam + | OpaqueTy + | SyntheticCoroutineBody + | TyParam => None, + } +} + +/// Gets the attributes of the definition. +pub fn get_def_attrs<'tcx>( + tcx: ty::TyCtxt<'tcx>, + def_id: RDefId, + def_kind: RDefKind, +) -> &'tcx [rustc_hir::Attribute] { + use RDefKind::*; + match def_kind { + // These kinds cause `get_attrs_unchecked` to panic. + ConstParam | LifetimeParam | TyParam | ForeignMod => &[], + _ => tcx.get_attrs_unchecked(def_id), + } +} + +/// Gets the children of a module. +pub fn get_mod_children<'tcx>( + tcx: ty::TyCtxt<'tcx>, + def_id: RDefId, +) -> Vec<(Option, RDefId)> { + match def_id.as_local() { + Some(ldid) => match tcx.hir_node_by_def_id(ldid) { + rustc_hir::Node::Crate(m) + | rustc_hir::Node::Item(&rustc_hir::Item { + kind: rustc_hir::ItemKind::Mod(_, m), + .. + }) => m + .item_ids + .iter() + .map(|&item_id| { + let opt_ident = tcx.hir_item(item_id).kind.ident(); + let def_id = item_id.owner_id.to_def_id(); + (opt_ident, def_id) + }) + .collect(), + node => panic!("DefKind::Module is an unexpected node: {node:?}"), + }, + None => tcx + .module_children(def_id) + .iter() + .map(|child| (Some(child.ident), child.res.def_id())) + .collect(), + } +} + +/// Gets the children of an `extern` block. Empty if the block is not defined in the current crate. +pub fn get_foreign_mod_children<'tcx>(tcx: ty::TyCtxt<'tcx>, def_id: RDefId) -> Vec { + match def_id.as_local() { + Some(ldid) => tcx + .hir_node_by_def_id(ldid) + .expect_item() + .expect_foreign_mod() + .1 + .iter() + .map(|foreign_item_ref| foreign_item_ref.owner_id.to_def_id()) + .collect(), + None => vec![], + } +} + +/// The signature of a method impl may be a subtype of the one expected from the trait decl, as in +/// the example below. For correctness, we must be able to map from the method generics declared in +/// the trait to the actual method generics. Because this would require type inference, we instead +/// simply return the declared signature. This will cause issues if it is possible to use such a +/// more-specific implementation with its more-specific type, but we have a few other issues with +/// lifetime-generic function pointers anyway so this is unlikely to cause problems. +/// +/// ```ignore +/// trait MyCompare: Sized { +/// fn compare(self, other: Other) -> bool; +/// } +/// impl<'a> MyCompare<&'a ()> for &'a () { +/// // This implementation is more general because it works for non-`'a` refs. Note that only +/// // late-bound vars may differ in this way. +/// // `<&'a () as MyCompare<&'a ()>>::compare` has type `fn<'b>(&'a (), &'b ()) -> bool`, +/// // but type `fn(&'a (), &'a ()) -> bool` was expected from the trait declaration. +/// fn compare<'b>(self, _other: &'b ()) -> bool { +/// true +/// } +/// } +/// ``` +pub fn get_method_sig<'tcx>( + tcx: ty::TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + def_id: RDefId, + method_args: Option>, +) -> ty::PolyFnSig<'tcx> { + let real_sig = inst_binder(tcx, typing_env, method_args, tcx.fn_sig(def_id)); + let item = tcx.associated_item(def_id); + if !matches!(item.container, ty::AssocItemContainer::Impl) { + return real_sig; + } + let Some(decl_method_id) = item.trait_item_def_id else { + return real_sig; + }; + let declared_sig = tcx.fn_sig(decl_method_id); + + // TODO(Nadrieril): Temporary hack: if the signatures have the same number of bound vars, we + // keep the real signature. While the declared signature is more correct, it is also less + // normalized and we can't normalize without erasing regions but regions are crucial in + // function signatures. Hence we cheat here, until charon gains proper normalization + // capabilities. + if declared_sig.skip_binder().bound_vars().len() == real_sig.bound_vars().len() { + return real_sig; + } + + let impl_def_id = item.container_id(tcx); + let method_args = + method_args.unwrap_or_else(|| ty::GenericArgs::identity_for_item(tcx, def_id)); + // The trait predicate that is implemented by the surrounding impl block. + let implemented_trait_ref = tcx + .impl_trait_ref(impl_def_id) + .unwrap() + .instantiate(tcx, method_args); + // Construct arguments for the declared method generics in the context of the implemented + // method generics. + let decl_args = method_args.rebase_onto(tcx, impl_def_id, implemented_trait_ref.args); + let sig = declared_sig.instantiate(tcx, decl_args); + // Avoids accidentally using the same lifetime name twice in the same scope + // (once in impl parameters, second in the method declaration late-bound vars). + let sig = tcx.anonymize_bound_vars(sig); + normalize(tcx, typing_env, sig) +} + +pub fn closure_once_shim<'tcx>( + tcx: ty::TyCtxt<'tcx>, + closure_ty: ty::Ty<'tcx>, +) -> Option> { + let ty::Closure(def_id, args) = closure_ty.kind() else { + unreachable!() + }; + let instance = match args.as_closure().kind() { + ty::ClosureKind::Fn | ty::ClosureKind::FnMut => { + ty::Instance::fn_once_adapter_instance(tcx, *def_id, args) + } + ty::ClosureKind::FnOnce => return None, + }; + let mir = tcx.instance_mir(instance.def).clone(); + let mir = ty::EarlyBinder::bind(mir).instantiate(tcx, instance.args); + Some(mir) +} + +pub fn drop_glue_shim<'tcx>( + tcx: ty::TyCtxt<'tcx>, + def_id: RDefId, + instantiate: Option>, +) -> Option> { + let drop_in_place = + tcx.require_lang_item(rustc_hir::LangItem::DropInPlace, rustc_span::DUMMY_SP); + let ty = tcx.type_of(def_id); + let ty = match instantiate { + None => { + if !tcx.generics_of(def_id).is_empty() { + // Hack: layout code panics if it can't fully normalize types, which can happen e.g. with a + // trait associated type. For now we only translate the glue for monomorphic types. + return None; + } + ty.instantiate_identity() + } + Some(args) => ty.instantiate(tcx, args), + }; + let instance_kind = ty::InstanceKind::DropGlue(drop_in_place, Some(ty)); + let mir = tcx.instance_mir(instance_kind).clone(); + Some(mir) +} diff --git a/frontend/exporter/src/sinto.rs b/frontend/exporter/src/sinto.rs index 3e775ceac..000e739dd 100644 --- a/frontend/exporter/src/sinto.rs +++ b/frontend/exporter/src/sinto.rs @@ -49,6 +49,14 @@ impl, R: SInto> SInto for (L, R) } } +impl, B: SInto, C: SInto> SInto + for (A, B, C) +{ + fn sinto(&self, s: &S) -> (AA, BB, CC) { + (self.0.sinto(s), self.1.sinto(s), self.2.sinto(s)) + } +} + impl> SInto> for Option { fn sinto(&self, s: &S) -> Option { self.as_ref().map(|x| x.sinto(s)) diff --git a/frontend/exporter/src/state.rs b/frontend/exporter/src/state.rs index 18a5b749b..6e184cbba 100644 --- a/frontend/exporter/src/state.rs +++ b/frontend/exporter/src/state.rs @@ -12,52 +12,12 @@ macro_rules! mk_aux { self.$field.clone() } } - pub trait []<$($lts)*> { - type Out; - // fn []<$($lts,)*$($gen)*>(self: Self, $field: $($field_type)+<$($lts)*>) -> $state<$($params)*>; - fn [](self: Self, $field: $($field_type)+<$($lts)*>) -> Self::Out; - } - // const _: &str = stringify!( - #[allow(unused)] - impl<$($lts,)*$($gen_full)*> []<$($lts,)*> for $state<$($gen_full)*> { - type Out = $state<$($params)*>; - fn [](self: Self, $field: $($field_type)+<$($lts)*>) -> Self::Out { - let __this_field = $field; - let $state { $($fields,)* } = self; - let $field = __this_field; - $state { $($fields,)* } - } - } - // ); - // pub trait []<$($lts,)*$($gen_full)*> { - // fn [](self: Self, $field: $($field_type)+<$($lts)*>) -> $state<$($params)*>; - // } - // impl<$($lts,)*$($gen_full)*> []<$($lts,)*$($gen_full)*> for $state<$($gen_full)*> { - // fn [](self: Self, $field: $($field_type)+<$($lts)*>) -> $state<$($params)*> { - // let __this_field = $field; - // let $state { $($fields,)* } = self; - // let $field = __this_field; - // $state { $($fields,)* } - // } - // } } }; } -macro_rules! mk_is_state_trait { - ($lts:tt {$($finished:tt)*} {{$f0:ident, $($l:tt)*} $($f:tt)*} $($generic:tt)*) => { - paste! {mk_is_state_trait!{ - $lts {$($finished)* [] <$($l)*> +} {$($f)*} $($generic)* - // $lts {$($finished)* [] <$($l)* $($generic)*> +} {$($f)*} $($generic)* - }} - }; - ({$($glts:lifetime,)*} {$($finished:tt)*} {} $($generic:tt)*) => { - pub trait IsState<$($glts,)*> = $($finished)*; - }; -} macro_rules! mk { (struct $state:ident<$($glts:lifetime),*> {$($field:ident : {$($lts:lifetime),*} $field_type:ty),*$(,)?}) => { mk!(@$state {} {$($field)*} {$($field: {$($lts),*} {$field_type},)*}); - mk_is_state_trait!({$($glts,)*} {} {$({$field, $($lts,)*})*} $([<$field:camel>],)*); }; (@$state:ident {$($acc:tt)*} $fields:tt { $field:ident : $lts:tt $field_type:tt @@ -128,32 +88,30 @@ mod types { pub per_item: HashMap>, /// A ID table session, providing fresh IDs. pub id_table_session: id_table::Session, + /// Map that recovers rustc args for a given `ItemRef`. + pub reverse_item_refs_map: HashMap>, } /// Defines a mapping from types to types, for use with `TypeMap`. - pub struct FullDefMapper {} + pub struct FullDefMapper; impl TypeMapper for FullDefMapper { type Value = Arc>; } - /// Defines a mapping from types to types, for use with `TypeMap`. - pub struct PromotedFullDefsMapper {} - impl TypeMapper for PromotedFullDefsMapper { - type Value = HashMap>>; - } - /// Per-item cache #[derive(Default)] pub struct ItemCache<'tcx> { /// The translated `DefId`. pub def_id: Option, /// The translated definitions, generic in the Body kind. - pub full_def: TypeMap, - /// The Promoted constants of this body, if any. - pub promoteds: TypeMap, + /// Each rustc `DefId` gives several hax `DefId`s: one for each promoted constant (if any), + /// and the base one represented by `None`. Moreover we can instantiate definitions with + /// generic arguments. + pub full_defs: + HashMap<(Option, Option>), TypeMap>, /// Cache the `Ty` translations. pub tys: HashMap, Ty>, - /// Cache the `ItemRef` translations. + /// Cache the `ItemRef` translations. This is fast because `GenericArgsRef` is interned. pub item_refs: HashMap<(RDefId, ty::GenericArgsRef<'tcx>), ItemRef>, /// Cache the trait resolution engine for each item. pub predicate_searcher: Option>, @@ -203,23 +161,32 @@ mod types { mk!( struct State<'tcx> { base: {'tcx} types::Base, + owner_id: {} rustc_hir::def_id::DefId, thir: {'tcx} types::RcThir, mir: {'tcx} types::RcMir, - owner_id: {} rustc_hir::def_id::DefId, binder: {'tcx} types::UnitBinder, + ty: {'tcx} rustc_middle::ty::Ty, } ); pub use self::types::*; -pub type StateWithBase<'tcx> = State, (), (), (), ()>; -pub type StateWithOwner<'tcx> = State, (), (), rustc_hir::def_id::DefId, ()>; +pub type StateWithBase<'tcx> = State, (), (), (), (), ()>; +pub type StateWithOwner<'tcx> = State, rustc_hir::def_id::DefId, (), (), (), ()>; pub type StateWithBinder<'tcx> = - State, (), (), rustc_hir::def_id::DefId, types::UnitBinder<'tcx>>; + State, rustc_hir::def_id::DefId, (), (), types::UnitBinder<'tcx>, ()>; pub type StateWithThir<'tcx> = - State, types::RcThir<'tcx>, (), rustc_hir::def_id::DefId, ()>; + State, rustc_hir::def_id::DefId, types::RcThir<'tcx>, (), (), ()>; +pub type StateWithThirAndTy<'tcx> = State< + Base<'tcx>, + rustc_hir::def_id::DefId, + types::RcThir<'tcx>, + (), + (), + rustc_middle::ty::Ty<'tcx>, +>; pub type StateWithMir<'tcx> = - State, (), types::RcMir<'tcx>, rustc_hir::def_id::DefId, ()>; + State, rustc_hir::def_id::DefId, (), types::RcMir<'tcx>, (), ()>; impl<'tcx> StateWithBase<'tcx> { pub fn new( @@ -227,98 +194,96 @@ impl<'tcx> StateWithBase<'tcx> { options: hax_frontend_exporter_options::Options, ) -> Self { Self { + base: Base::new(tcx, options), + owner_id: (), thir: (), mir: (), - owner_id: (), binder: (), - base: Base::new(tcx, options), + ty: (), } } } -impl<'tcx> StateWithOwner<'tcx> { - pub fn new_from_state_and_id>(s: &S, id: rustc_hir::def_id::DefId) -> Self { +pub trait BaseState<'tcx>: HasBase<'tcx> + Clone { + /// Updates the OnwerId in a state, making sure to override `opt_def_id` in base as well. + fn with_owner_id(&self, owner_id: rustc_hir::def_id::DefId) -> StateWithOwner<'tcx> { + let mut base = self.base(); + base.opt_def_id = Some(owner_id); State { + owner_id, + base, thir: (), mir: (), - owner_id: id, binder: (), - base: s.base().clone(), + ty: (), } } } -impl<'tcx> StateWithMir<'tcx> { - pub fn new_from_mir( - tcx: rustc_middle::ty::TyCtxt<'tcx>, - options: hax_frontend_exporter_options::Options, - mir: rustc_middle::mir::Body<'tcx>, - owner_id: rustc_hir::def_id::DefId, - ) -> Self { - Self { +impl<'tcx, T: HasBase<'tcx> + Clone> BaseState<'tcx> for T {} + +/// State of anything below an `owner`. +pub trait UnderOwnerState<'tcx>: BaseState<'tcx> + HasOwnerId { + fn with_base(&self, base: types::Base<'tcx>) -> StateWithOwner<'tcx> { + State { + owner_id: self.owner_id(), + base, thir: (), - mir: Rc::new(mir), - owner_id, + mir: (), binder: (), - base: Base::new(tcx, options), + ty: (), } } -} -impl<'tcx> StateWithThir<'tcx> { - pub fn from_thir( - base: Base<'tcx>, - owner_id: rustc_hir::def_id::DefId, - thir: types::RcThir<'tcx>, - ) -> Self { - Self { + fn with_binder(&self, binder: types::UnitBinder<'tcx>) -> StateWithBinder<'tcx> { + State { + base: self.base(), + owner_id: self.owner_id(), + binder, + thir: (), + mir: (), + ty: (), + } + } + fn with_thir(&self, thir: types::RcThir<'tcx>) -> StateWithThir<'tcx> { + State { + base: self.base(), + owner_id: self.owner_id(), thir, mir: (), - owner_id, binder: (), - base, + ty: (), } } -} - -impl<'tcx> StateWithOwner<'tcx> { - pub fn from_under_owner>(s: &S) -> Self { - Self { - base: s.base(), - owner_id: s.owner_id(), + fn with_mir(&self, mir: types::RcMir<'tcx>) -> StateWithMir<'tcx> { + State { + base: self.base(), + owner_id: self.owner_id(), + mir, thir: (), - mir: (), binder: (), + ty: (), } } } - -/// Updates the OnwerId in a state, making sure to override `opt_def_id` in base as well. -pub fn with_owner_id( - mut base: types::Base<'_>, - thir: THIR, - mir: MIR, - owner_id: rustc_hir::def_id::DefId, -) -> State, THIR, MIR, rustc_hir::def_id::DefId, ()> { - base.opt_def_id = Some(owner_id); - State { - thir, - owner_id, - base, - mir, - binder: (), - } -} - -pub trait BaseState<'tcx> = HasBase<'tcx> + Clone + IsState<'tcx>; - -/// State of anything below a `owner_id`. -pub trait UnderOwnerState<'tcx> = BaseState<'tcx> + HasOwnerId; +impl<'tcx, T: BaseState<'tcx> + HasOwnerId> UnderOwnerState<'tcx> for T {} /// State of anything below a binder. pub trait UnderBinderState<'tcx> = UnderOwnerState<'tcx> + HasBinder<'tcx>; /// While translating expressions, we expect to always have a THIR /// body and an `owner_id` in the state -pub trait ExprState<'tcx> = UnderOwnerState<'tcx> + HasThir<'tcx>; +pub trait ExprState<'tcx>: UnderOwnerState<'tcx> + HasThir<'tcx> { + fn with_ty(&self, ty: rustc_middle::ty::Ty<'tcx>) -> StateWithThirAndTy<'tcx> { + State { + base: self.base(), + owner_id: self.owner_id(), + thir: self.thir(), + mir: (), + binder: (), + ty, + } + } +} +impl<'tcx, T> ExprState<'tcx> for T where T: UnderOwnerState<'tcx> + HasThir<'tcx> {} pub trait WithGlobalCacheExt<'tcx>: BaseState<'tcx> { /// Access the global cache. You must not call `sinto` within this function as this will likely @@ -342,13 +307,24 @@ pub trait WithItemCacheExt<'tcx>: UnderOwnerState<'tcx> { fn with_cache(&self, f: impl FnOnce(&mut ItemCache<'tcx>) -> T) -> T { self.with_item_cache(self.owner_id(), f) } + fn with_predicate_searcher(&self, f: impl FnOnce(&mut PredicateSearcher<'tcx>) -> T) -> T { + self.with_cache(|cache| { + f(cache.predicate_searcher.get_or_insert_with(|| { + PredicateSearcher::new_for_owner( + self.base().tcx, + self.owner_id(), + self.base().options.bounds_options, + ) + })) + }) + } } impl<'tcx, S: UnderOwnerState<'tcx>> WithItemCacheExt<'tcx> for S {} impl ImplInfos { - fn from(base: Base<'_>, did: rustc_hir::def_id::DefId) -> Self { - let tcx = base.tcx; - let s = &with_owner_id(base, (), (), did); + fn from<'tcx, S: BaseState<'tcx>>(s: &S, did: rustc_hir::def_id::DefId) -> Self { + let tcx = s.base().tcx; + let s = &s.with_owner_id(did); Self { generics: tcx.generics_of(did).sinto(s), @@ -357,7 +333,7 @@ impl ImplInfos { .impl_trait_ref(did) .map(|trait_ref| trait_ref.instantiate_identity()) .sinto(s), - clauses: predicates_defined_on(tcx, did).predicates.sinto(s), + clauses: predicates_defined_on(tcx, did).as_ref().sinto(s), } } } @@ -393,6 +369,6 @@ pub fn impl_def_ids_to_impled_types_and_bounds<'tcx, S: BaseState<'tcx>>( }) ) }) - .map(|did| (did.sinto(s), ImplInfos::from(s.base(), did))) + .map(|did| (did.sinto(s), ImplInfos::from(s, did))) .collect() } diff --git a/frontend/exporter/src/traits.rs b/frontend/exporter/src/traits.rs index fb6bc8831..9fecb5d8c 100644 --- a/frontend/exporter/src/traits.rs +++ b/frontend/exporter/src/traits.rs @@ -1,12 +1,12 @@ use crate::prelude::*; #[cfg(feature = "rustc")] -mod resolution; +pub mod resolution; #[cfg(feature = "rustc")] mod utils; #[cfg(feature = "rustc")] pub use utils::{ - ToPolyTraitRef, erase_and_norm, implied_predicates, is_sized_related_trait, + Predicates, ToPolyTraitRef, erase_and_norm, erase_free_regions, implied_predicates, normalize, predicates_defined_on, required_predicates, self_predicate, }; @@ -17,6 +17,9 @@ use rustc_middle::ty; #[cfg(feature = "rustc")] use rustc_span::def_id::DefId as RDefId; +#[cfg(feature = "rustc")] +pub use utils::is_sized_related_trait; + #[derive_group(Serializers)] #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] pub enum ImplExprPathChunk { @@ -52,19 +55,12 @@ impl<'tcx, S: UnderOwnerState<'tcx>> SInto for resolution: resolution::PathChunk::AssocItem { item, generic_args, - impl_exprs, predicate, index, .. } => ImplExprPathChunk::AssocItem { - item: ItemRef::new( - s, - item.def_id.sinto(s), - generic_args.sinto(s), - impl_exprs.sinto(s), - None, - ), - assoc_item: item.sinto(s), + item: translate_item_ref(s, item.def_id, generic_args), + assoc_item: AssocItem::sfrom(s, item), predicate: predicate.sinto(s), predicate_id: <_ as SInto<_, Clause>>::sinto(predicate, s).id, index: index.sinto(s), @@ -88,14 +84,8 @@ impl<'tcx, S: UnderOwnerState<'tcx>> SInto for resolution: #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] pub enum ImplExprAtom { /// A concrete `impl Trait for Type {}` item. - #[custom_arm(FROM_TYPE::Concrete { def_id, generics, impl_exprs } => TO_TYPE::Concrete( - ItemRef::new( - s, - def_id.sinto(s), - generics.sinto(s), - impl_exprs.sinto(s), - None, - ) + #[custom_arm(FROM_TYPE::Concrete { def_id, generics } => TO_TYPE::Concrete( + translate_item_ref(s, *def_id, generics), ),)] Concrete(ItemRef), /// A context-bound clause like `where T: Trait`. @@ -125,27 +115,37 @@ pub enum ImplExprAtom { /// `dyn Trait` implements `Trait` using a built-in implementation; this refers to that /// built-in implementation. Dyn, - /// A virtual `Drop` implementation. - /// `Drop` doesn't work like a real trait but we want to pretend it does. If a type has a - /// user-defined `impl Drop for X` we just use the `Concrete` variant, but if it doesn't we use - /// this variant to supply the data needed to know what code will run on drop. - Drop(DropData), /// A built-in trait whose implementation is computed by the compiler, such as `FnMut`. This /// morally points to an invisible `impl` block; as such it contains the information we may /// need from one. Builtin { - r#trait: Binder, + /// Extra data for the given trait. + trait_data: BuiltinTraitData, /// The `ImplExpr`s required to satisfy the implied predicates on the trait declaration. /// E.g. since `FnMut: FnOnce`, a built-in `T: FnMut` impl would have an `ImplExpr` for `T: /// FnOnce`. impl_exprs: Vec, /// The values of the associated types for this trait. - types: Vec<(DefId, Ty)>, + types: Vec<(DefId, Ty, Vec)>, }, /// An error happened while resolving traits. Error(String), } +#[derive(AdtInto)] +#[args(<'tcx, S: UnderOwnerState<'tcx> >, from: resolution::BuiltinTraitData<'tcx>, state: S as s)] +#[derive_group(Serializers)] +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] +pub enum BuiltinTraitData { + /// A virtual `Drop` implementation. + /// `Drop` doesn't work like a real trait but we want to pretend it does. If a type has a + /// user-defined `impl Drop for X` we just use the `Concrete` variant, but if it doesn't we use + /// this variant to supply the data needed to know what code will run on drop. + Drop(DropData), + /// Some other builtin trait. + Other, +} + #[derive(AdtInto)] #[args(<'tcx, S: UnderOwnerState<'tcx> >, from: resolution::DropData<'tcx>, state: S as s)] #[derive_group(Serializers)] @@ -200,7 +200,7 @@ pub fn super_clause_to_clause_and_impl_expr<'tcx, S: UnderOwnerState<'tcx>>( let original_predicate_id = { // We don't want the id of the substituted clause id, but the // original clause id (with, i.e., `Self`) - let s = &with_owner_id(s.base(), (), (), impl_trait_ref.def_id()); + let s = &s.with_owner_id(impl_trait_ref.def_id()); clause.sinto(s).id }; let new_clause = clause.instantiate_supertrait(tcx, impl_trait_ref); @@ -219,7 +219,7 @@ pub fn super_clause_to_clause_and_impl_expr<'tcx, S: UnderOwnerState<'tcx>>( /// This is the entrypoint of the solving. #[cfg(feature = "rustc")] #[tracing::instrument(level = "trace", skip(s))] -pub fn solve_trait<'tcx, S: BaseState<'tcx> + HasOwnerId>( +pub fn solve_trait<'tcx, S: UnderOwnerState<'tcx>>( s: &S, trait_ref: rustc_middle::ty::PolyTraitRef<'tcx>, ) -> ImplExpr { @@ -231,18 +231,8 @@ pub fn solve_trait<'tcx, S: BaseState<'tcx> + HasOwnerId>( if let Some(impl_expr) = s.with_cache(|cache| cache.impl_exprs.get(&trait_ref).cloned()) { return impl_expr; } - let resolved = s.with_cache(|cache| { - cache - .predicate_searcher - .get_or_insert_with(|| { - PredicateSearcher::new_for_owner( - s.base().tcx, - s.owner_id(), - s.base().options.bounds_options, - ) - }) - .resolve(&trait_ref, &warn) - }); + let resolved = + s.with_predicate_searcher(|pred_searcher| pred_searcher.resolve(&trait_ref, &warn)); let impl_expr = match resolved { Ok(x) => x.sinto(s), Err(e) => crate::fatal!(s, "{}", e), @@ -259,51 +249,7 @@ pub fn translate_item_ref<'tcx, S: UnderOwnerState<'tcx>>( def_id: RDefId, generics: ty::GenericArgsRef<'tcx>, ) -> ItemRef { - let key = (def_id, generics); - if let Some(item) = s.with_cache(|cache| cache.item_refs.get(&key).cloned()) { - return item; - } - let mut impl_exprs = solve_item_required_traits(s, def_id, generics); - let mut generic_args = generics.sinto(s); - - // If this is an associated item, resolve the trait reference. - let trait_info = self_clause_for_item(s, def_id, generics); - // Fixup the generics. - if let Some(tinfo) = &trait_info { - // The generics are split in two: the arguments of the trait and the arguments of the - // method. - // - // For instance, if we have: - // ``` - // trait Foo { - // fn baz(...) { ... } - // } - // - // fn test(x: T) { - // x.baz(...); - // ... - // } - // ``` - // The generics for the call to `baz` will be the concatenation: ``, which we - // split into `` and ``. - let trait_ref = tinfo.r#trait.hax_skip_binder_ref(); - let num_trait_generics = trait_ref.generic_args.len(); - generic_args.drain(0..num_trait_generics); - let num_trait_trait_clauses = trait_ref.impl_exprs.len(); - impl_exprs.drain(0..num_trait_trait_clauses); - } - - let content = ItemRefContents { - def_id: def_id.sinto(s), - generic_args, - impl_exprs, - in_trait: trait_info, - }; - let item = content.intern(s); - s.with_cache(|cache| { - cache.item_refs.insert(key, item.clone()); - }); - item + ItemRef::translate(s, def_id, generics) } /// Solve the trait obligations for a specific item use (for example, a method call, an ADT, etc.) @@ -358,12 +304,11 @@ pub fn solve_item_implied_traits<'tcx, S: UnderOwnerState<'tcx>>( fn solve_item_traits_inner<'tcx, S: UnderOwnerState<'tcx>>( s: &S, generics: ty::GenericArgsRef<'tcx>, - predicates: ty::GenericPredicates<'tcx>, + predicates: utils::Predicates<'tcx>, ) -> Vec { let tcx = s.base().tcx; let typing_env = s.typing_env(); predicates - .predicates .iter() .map(|(clause, _span)| *clause) .filter_map(|clause| clause.as_trait_clause()) @@ -387,7 +332,6 @@ pub fn self_clause_for_item<'tcx, S: UnderOwnerState<'tcx>>( def_id: RDefId, generics: rustc_middle::ty::GenericArgsRef<'tcx>, ) -> Option { - use rustc_middle::ty::EarlyBinder; let tcx = s.base().tcx; let tr_def_id = tcx.trait_of_item(def_id)?; @@ -395,7 +339,7 @@ pub fn self_clause_for_item<'tcx, S: UnderOwnerState<'tcx>>( let self_pred = self_predicate(tcx, tr_def_id); // Substitute to be in the context of the current item. let generics = generics.truncate_to(tcx, tcx.generics_of(tr_def_id)); - let self_pred = EarlyBinder::bind(self_pred).instantiate(tcx, generics); + let self_pred = ty::EarlyBinder::bind(self_pred).instantiate(tcx, generics); // Resolve Some(solve_trait(s, self_pred)) diff --git a/frontend/exporter/src/traits/resolution.rs b/frontend/exporter/src/traits/resolution.rs index ceada7336..196d8d111 100644 --- a/frontend/exporter/src/traits/resolution.rs +++ b/frontend/exporter/src/traits/resolution.rs @@ -12,24 +12,17 @@ use rustc_middle::traits::CodegenObligationError; use rustc_middle::ty::{self, *}; use rustc_trait_selection::traits::ImplSource; -use crate::{self_predicate, traits::utils::erase_and_norm}; - -use super::utils::{ToPolyTraitRef, implied_predicates, normalize_bound_val, required_predicates}; +use super::utils::{ + self, ToPolyTraitRef, erase_and_norm, implied_predicates, normalize_bound_val, + required_predicates, self_predicate, +}; #[derive(Debug, Clone)] pub enum PathChunk<'tcx> { AssocItem { item: AssocItem, - /// The arguments provided to the item (for GATs). + /// The arguments provided to the item (for GATs). Includes trait args. generic_args: GenericArgsRef<'tcx>, - /// The impl exprs that must be satisfied to apply the given arguments to the item. E.g. - /// `T: Clone` in the following example: - /// ```ignore - /// trait Foo { - /// type Type: Debug; - /// } - /// ``` - impl_exprs: Vec>, /// The implemented predicate. predicate: PolyTraitPredicate<'tcx>, /// The index of this predicate in the list returned by `implied_predicates`. @@ -50,8 +43,6 @@ pub enum ImplExprAtom<'tcx> { Concrete { def_id: DefId, generics: GenericArgsRef<'tcx>, - /// The impl exprs that prove the clauses on the impl. - impl_exprs: Vec>, }, /// A context-bound clause like `where T: Trait`. LocalBound { @@ -74,27 +65,34 @@ pub enum ImplExprAtom<'tcx> { /// `dyn Trait` implements `Trait` using a built-in implementation; this refers to that /// built-in implementation. Dyn, - /// A virtual `Drop` implementation. - /// `Drop` doesn't work like a real trait but we want to pretend it does. If a type has a - /// user-defined `impl Drop for X` we just use the `Concrete` variant, but if it doesn't we use - /// this variant to supply the data needed to know what code will run on drop. - Drop(DropData<'tcx>), /// A built-in trait whose implementation is computed by the compiler, such as `FnMut`. This /// morally points to an invisible `impl` block; as such it contains the information we may /// need from one. Builtin { - r#trait: PolyTraitRef<'tcx>, + /// Extra data for the given trait. + trait_data: BuiltinTraitData<'tcx>, /// The `ImplExpr`s required to satisfy the implied predicates on the trait declaration. /// E.g. since `FnMut: FnOnce`, a built-in `T: FnMut` impl would have an `ImplExpr` for `T: /// FnOnce`. impl_exprs: Vec>, /// The values of the associated types for this trait. - types: Vec<(DefId, Ty<'tcx>)>, + types: Vec<(DefId, Ty<'tcx>, Vec>)>, }, /// An error happened while resolving traits. Error(String), } +#[derive(Debug, Clone)] +pub enum BuiltinTraitData<'tcx> { + /// A virtual `Drop` implementation. + /// `Drop` doesn't work like a real trait but we want to pretend it does. If a type has a + /// user-defined `impl Drop for X` we just use the `Concrete` variant, but if it doesn't we use + /// this variant to supply the data needed to know what code will run on drop. + Drop(DropData<'tcx>), + /// Some other builtin trait. + Other, +} + #[derive(Debug, Clone)] pub enum DropData<'tcx> { /// A drop that does nothing, e.g. for scalars and pointers. @@ -142,58 +140,57 @@ pub struct AnnotatedTraitPred<'tcx> { pub clause: PolyTraitPredicate<'tcx>, } +/// Returns the predicate to resolve as `Self`, if that makes sense in the current item. +/// Currently this predicate is only used inside trait declarations and their asosciated types. +fn initial_self_pred<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: rustc_span::def_id::DefId, +) -> Option> { + use DefKind::*; + let trait_def_id = match tcx.def_kind(def_id) { + Trait | TraitAlias => def_id, + // Hack: we don't support GATs well so for now we let assoc types refer to the + // implicit trait `Self` clause. Other associated items get an explicit `Self: + // Trait` clause passed to them so they don't need that. + AssocTy => tcx.parent(def_id), + _ => return None, + }; + let self_pred = self_predicate(tcx, trait_def_id).upcast(tcx); + Some(self_pred) +} + /// The predicates to use as a starting point for resolving trait references within this item. This -/// includes the "self" predicate if applicable and the `required_predicates` of this item and all -/// its parents, numbered starting from the parents. -fn initial_search_predicates<'tcx>( +/// includes the `required_predicates` of this item and all its parents. +fn local_bound_predicates<'tcx>( tcx: TyCtxt<'tcx>, def_id: rustc_span::def_id::DefId, options: BoundsOptions, -) -> Vec> { +) -> Vec> { fn acc_predicates<'tcx>( tcx: TyCtxt<'tcx>, def_id: rustc_span::def_id::DefId, options: BoundsOptions, - predicates: &mut Vec>, - pred_id: &mut usize, + predicates: &mut Vec>, ) { - let next_item_origin = |pred_id: &mut usize| { - let origin = BoundPredicateOrigin::Item(*pred_id); - *pred_id += 1; - origin - }; use DefKind::*; match tcx.def_kind(def_id) { - // These inherit some predicates from their parent. + // These inherit predicates from their parent. AssocTy | AssocFn | AssocConst | Closure | Ctor(..) | Variant => { let parent = tcx.parent(def_id); - acc_predicates(tcx, parent, options, predicates, pred_id); - } - Trait | TraitAlias => { - let self_pred = self_predicate(tcx, def_id).upcast(tcx); - predicates.push(AnnotatedTraitPred { - origin: BoundPredicateOrigin::SelfPred, - clause: self_pred, - }) + acc_predicates(tcx, parent, options, predicates); } _ => {} } predicates.extend( required_predicates(tcx, def_id, options) - .predicates .iter() .map(|(clause, _span)| *clause) - .filter_map(|clause| { - clause.as_trait_clause().map(|clause| AnnotatedTraitPred { - origin: next_item_origin(pred_id), - clause, - }) - }), + .filter_map(|clause| clause.as_trait_clause()), ); } let mut predicates = vec![]; - acc_predicates(tcx, def_id, options, &mut predicates, &mut 0); + acc_predicates(tcx, def_id, options, &mut predicates); predicates } @@ -205,7 +202,6 @@ fn parents_trait_predicates<'tcx>( ) -> Vec> { let self_trait_ref = pred.to_poly_trait_ref(); implied_predicates(tcx, pred.def_id(), options) - .predicates .iter() .map(|(clause, _span)| *clause) // Substitute with the `self` args so that the clause makes sense in the @@ -241,12 +237,16 @@ impl<'tcx> Candidate<'tcx> { } /// Stores a set of predicates along with where they came from. +#[derive(Clone)] pub struct PredicateSearcher<'tcx> { tcx: TyCtxt<'tcx>, typing_env: rustc_middle::ty::TypingEnv<'tcx>, /// Local clauses available in the current context. candidates: HashMap, Candidate<'tcx>>, + /// Whether to add `T: Drop` bounds for every type generic and associated item. options: BoundsOptions, + /// Count the number of bound clauses in scope; used to identify clauses uniquely. + bound_clause_count: usize, } impl<'tcx> PredicateSearcher<'tcx> { @@ -260,22 +260,58 @@ impl<'tcx> PredicateSearcher<'tcx> { }, candidates: Default::default(), options, + bound_clause_count: 0, }; - out.extend( - initial_search_predicates(tcx, owner_id, options) - .into_iter() - .map(|clause| Candidate { - path: vec![], - pred: clause.clause, - origin: clause, - }), + out.insert_predicates( + initial_self_pred(tcx, owner_id).map(|clause| AnnotatedTraitPred { + origin: BoundPredicateOrigin::SelfPred, + clause, + }), ); + out.insert_bound_predicates(local_bound_predicates(tcx, owner_id, options)); out } + /// Insert the bound clauses in the search context. Prefer inserting them all at once as this + /// will give priority to shorter resolution paths. Bound clauses are numbered from `0` in + /// insertion order. + pub fn insert_bound_predicates( + &mut self, + clauses: impl IntoIterator>, + ) { + let mut count = usize::MAX; + // Swap to avoid borrow conflicts. + std::mem::swap(&mut count, &mut self.bound_clause_count); + self.insert_predicates(clauses.into_iter().map(|clause| { + let i = count; + count += 1; + AnnotatedTraitPred { + origin: BoundPredicateOrigin::Item(i), + clause, + } + })); + std::mem::swap(&mut count, &mut self.bound_clause_count); + } + + /// Override the param env; we use this when resolving `dyn` predicates to add more clauses to + /// the scope. + pub fn set_param_env(&mut self, param_env: ParamEnv<'tcx>) { + self.typing_env.param_env = param_env; + } + + /// Insert annotated predicates in the search context. Prefer inserting them all at once as + /// this will give priority to shorter resolution paths. + fn insert_predicates(&mut self, preds: impl IntoIterator>) { + self.insert_candidates(preds.into_iter().map(|clause| Candidate { + path: vec![], + pred: clause.clause, + origin: clause, + })) + } + /// Insert new candidates and all their parent predicates. This deduplicates predicates /// to avoid divergence. - fn extend(&mut self, candidates: impl IntoIterator>) { + fn insert_candidates(&mut self, candidates: impl IntoIterator>) { let tcx = self.tcx; // Filter out duplicated candidates. let mut new_candidates = Vec::new(); @@ -288,19 +324,20 @@ impl<'tcx> PredicateSearcher<'tcx> { } } if !new_candidates.is_empty() { - self.extend_parents(new_candidates); + // Insert the parents all at once. + self.insert_candidate_parents(new_candidates); } } /// Add the parents of these candidates. This is a separate function to avoid /// polymorphic recursion due to the closures capturing the type parameters of this /// function. - fn extend_parents(&mut self, new_candidates: Vec>) { + fn insert_candidate_parents(&mut self, new_candidates: Vec>) { let tcx = self.tcx; // Then recursively add their parents. This way ensures a breadth-first order, // which means we select the shortest path when looking up predicates. let options = self.options; - self.extend(new_candidates.into_iter().flat_map(|candidate| { + self.insert_candidates(new_candidates.into_iter().flat_map(|candidate| { parents_trait_predicates(tcx, candidate.pred, options) .into_iter() .enumerate() @@ -331,9 +368,7 @@ impl<'tcx> PredicateSearcher<'tcx> { let TyKind::Alias(AliasTyKind::Projection, alias_ty) = ty.skip_binder().kind() else { return Ok(()); }; - let (trait_ref, item_args) = alias_ty.trait_ref_and_own_args(tcx); - let item_args = tcx.mk_args(item_args); - let trait_ref = ty.rebind(trait_ref).upcast(tcx); + let trait_ref = ty.rebind(alias_ty.trait_ref(tcx)).upcast(tcx); // The predicate we're looking for is is `::Type: OtherTrait`. We look up `T as // Trait` in the current context and add all the bounds on `Trait::Type` to our context. @@ -342,8 +377,8 @@ impl<'tcx> PredicateSearcher<'tcx> { }; // The bounds that hold on the associated type. - let item_bounds = implied_predicates(tcx, alias_ty.def_id, self.options) - .predicates + let item_bounds = implied_predicates(tcx, alias_ty.def_id, self.options); + let item_bounds = item_bounds .iter() .map(|(clause, _span)| *clause) .filter_map(|pred| pred.as_trait_clause()) @@ -351,12 +386,8 @@ impl<'tcx> PredicateSearcher<'tcx> { .map(|pred| EarlyBinder::bind(pred).instantiate(tcx, alias_ty.args)) .enumerate(); - // Resolve predicates required to mention the item. - let nested_impl_exprs = - self.resolve_item_required_predicates(alias_ty.def_id, alias_ty.args, warn)?; - // Add all the bounds on the corresponding associated item. - self.extend(item_bounds.map(|(index, pred)| { + self.insert_candidates(item_bounds.map(|(index, pred)| { let mut candidate = Candidate { path: trait_candidate.path.clone(), pred, @@ -364,8 +395,7 @@ impl<'tcx> PredicateSearcher<'tcx> { }; candidate.path.push(PathChunk::AssocItem { item: tcx.associated_item(alias_ty.def_id), - generic_args: item_args, - impl_exprs: nested_impl_exprs.clone(), + generic_args: alias_ty.args, predicate: pred, index, }); @@ -424,22 +454,24 @@ impl<'tcx> PredicateSearcher<'tcx> { let erased_tref = normalize_bound_val(self.tcx, self.typing_env, *tref); let trait_def_id = erased_tref.skip_binder().def_id; + let error = |msg: String| { + warn(&msg); + Ok(ImplExpr { + r#impl: ImplExprAtom::Error(msg), + r#trait: *tref, + }) + }; + let impl_source = shallow_resolve_trait_ref(tcx, self.typing_env.param_env, erased_tref); let atom = match impl_source { Ok(ImplSource::UserDefined(ImplSourceUserDefinedData { impl_def_id, args: generics, .. - })) => { - // Resolve the predicates required by the impl. - let impl_exprs = - self.resolve_item_required_predicates(impl_def_id, generics, warn)?; - ImplExprAtom::Concrete { - def_id: impl_def_id, - generics, - impl_exprs, - } - } + })) => ImplExprAtom::Concrete { + def_id: impl_def_id, + generics, + }, Ok(ImplSource::Param(_)) => { match self.resolve_local(erased_tref.upcast(self.tcx), warn)? { Some(candidate) => candidate.into_impl_expr(tcx), @@ -447,8 +479,7 @@ impl<'tcx> PredicateSearcher<'tcx> { let msg = format!( "Could not find a clause for `{tref:?}` in the item parameters" ); - warn(&msg); - ImplExprAtom::Error(msg) + return error(msg); } } } @@ -484,11 +515,18 @@ impl<'tcx> PredicateSearcher<'tcx> { return None; } } - Some((assoc.def_id, ty)) + let impl_exprs = self + .resolve_item_implied_predicates( + assoc.def_id, + erased_tref.skip_binder().args, + warn, + ) + .ok()?; + Some((assoc.def_id, ty, impl_exprs)) }) .collect(); ImplExprAtom::Builtin { - r#trait: *tref, + trait_data: BuiltinTraitData::Other, impl_exprs, types, } @@ -514,7 +552,7 @@ impl<'tcx> PredicateSearcher<'tcx> { // TODO: how to check if there is a real drop impl????? let ty = erased_tref.skip_binder().args[0].as_type().unwrap(); // Source of truth are `ty::needs_drop_components` and `tcx.needs_drop_raw`. - match ty.kind() { + let drop_data = match ty.kind() { // TODO: Does `UnsafeBinder` drop its contents? ty::Bool | ty::Char @@ -528,14 +566,14 @@ impl<'tcx> PredicateSearcher<'tcx> { | ty::FnDef(..) | ty::FnPtr(..) | ty::UnsafeBinder(..) - | ty::Never => ImplExprAtom::Drop(DropData::Noop), + | ty::Never => Ok(DropData::Noop), ty::Array(inner_ty, _) | ty::Pat(inner_ty, _) | ty::Slice(inner_ty) => { - ImplExprAtom::Drop(DropData::Glue { + Ok(DropData::Glue { ty, impl_exprs: vec![resolve_drop(*inner_ty)?], }) } - ty::Tuple(tys) => ImplExprAtom::Drop(DropData::Glue { + ty::Tuple(tys) => Ok(DropData::Glue { ty, impl_exprs: tys.iter().map(resolve_drop).try_collect()?, }), @@ -543,14 +581,13 @@ impl<'tcx> PredicateSearcher<'tcx> { // We should have been able to resolve the `T: Drop` clause above, if we // get here we don't know how to reconstruct the arguments to the impl. let msg = format!("Cannot resolve clause `{tref:?}`"); - warn(&msg); - ImplExprAtom::Error(msg) + return error(msg); } ty::Adt(_, args) | ty::Closure(_, args) | ty::Coroutine(_, args) | ty::CoroutineClosure(_, args) - | ty::CoroutineWitness(_, args) => ImplExprAtom::Drop(DropData::Glue { + | ty::CoroutineWitness(_, args) => Ok(DropData::Glue { ty, impl_exprs: args .iter() @@ -560,22 +597,21 @@ impl<'tcx> PredicateSearcher<'tcx> { }), // Every `dyn` has a `Drop` in its vtable, ergo we pretend that every `dyn` has // `Drop` in its list of traits. - ty::Dynamic(..) => ImplExprAtom::Dyn, + ty::Dynamic(..) => Err(ImplExprAtom::Dyn), ty::Param(..) | ty::Alias(..) | ty::Bound(..) => { if self.options.resolve_drop { // We've added `Drop` impls on everything, we should be able to resolve // it. match self.resolve_local(erased_tref.upcast(self.tcx), warn)? { - Some(candidate) => candidate.into_impl_expr(tcx), + Some(candidate) => Err(candidate.into_impl_expr(tcx)), None => { let msg = format!("Cannot find virtual `Drop` clause: `{tref:?}`"); - warn(&msg); - ImplExprAtom::Error(msg) + return error(msg); } } } else { - ImplExprAtom::Drop(DropData::Implicit) + Ok(DropData::Implicit) } } @@ -584,9 +620,23 @@ impl<'tcx> PredicateSearcher<'tcx> { "Cannot resolve clause `{tref:?}` \ because of a type error" ); - warn(&msg); - ImplExprAtom::Error(msg) + return error(msg); + } + }; + match drop_data { + Ok(drop_data) => { + let impl_exprs = self.resolve_item_implied_predicates( + trait_def_id, + erased_tref.skip_binder().args, + warn, + )?; + ImplExprAtom::Builtin { + trait_data: BuiltinTraitData::Drop(drop_data), + impl_exprs, + types: vec![], + } } + Err(atom) => atom, } } Err(e) => { @@ -594,8 +644,7 @@ impl<'tcx> PredicateSearcher<'tcx> { "Could not find a clause for `{tref:?}` \ in the current context: `{e:?}`" ); - warn(&msg); - ImplExprAtom::Error(msg) + return error(msg); } }; @@ -642,13 +691,12 @@ impl<'tcx> PredicateSearcher<'tcx> { pub fn resolve_predicates( &mut self, generics: GenericArgsRef<'tcx>, - predicates: GenericPredicates<'tcx>, + predicates: utils::Predicates<'tcx>, // Call back into hax-related code to display a nice warning. warn: &impl Fn(&str), ) -> Result>, String> { let tcx = self.tcx; predicates - .predicates .iter() .map(|(clause, _span)| *clause) .filter_map(|clause| clause.as_trait_clause()) diff --git a/frontend/exporter/src/traits/utils.rs b/frontend/exporter/src/traits/utils.rs index 96c5274cb..3930bff13 100644 --- a/frontend/exporter/src/traits/utils.rs +++ b/frontend/exporter/src/traits/utils.rs @@ -27,31 +27,56 @@ //! benefit of reducing the size of signatures. Moreover, the rules on which bounds are required vs //! implied are subtle. We may change this if this proves to be a problem. use hax_frontend_exporter_options::BoundsOptions; +use rustc_hir::LangItem; use rustc_hir::def::DefKind; use rustc_middle::ty::*; -use rustc_span::DUMMY_SP; use rustc_span::def_id::DefId; +use rustc_span::{DUMMY_SP, Span}; +use std::borrow::Cow; + +pub type Predicates<'tcx> = Cow<'tcx, [(Clause<'tcx>, Span)]>; /// Returns a list of type predicates for the definition with ID `def_id`, including inferred /// lifetime constraints. This is the basic list of predicates we use for essentially all items. -pub fn predicates_defined_on(tcx: TyCtxt<'_>, def_id: DefId) -> GenericPredicates<'_> { - let mut result = tcx.explicit_predicates_of(def_id); +pub fn predicates_defined_on(tcx: TyCtxt<'_>, def_id: DefId) -> Predicates<'_> { + let mut result = Cow::Borrowed(tcx.explicit_predicates_of(def_id).predicates); let inferred_outlives = tcx.inferred_outlives_of(def_id); if !inferred_outlives.is_empty() { - let inferred_outlives_iter = inferred_outlives - .iter() - .map(|(clause, span)| ((*clause).upcast(tcx), *span)); - result.predicates = tcx.arena.alloc_from_iter( - result - .predicates - .into_iter() - .copied() - .chain(inferred_outlives_iter), + result.to_mut().extend( + inferred_outlives + .iter() + .map(|(clause, span)| ((*clause).upcast(tcx), *span)), ); } result } +/// Add `T: Drop` bounds for every generic parameter of the given item. +fn add_drop_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + predicates: &mut Vec<(Clause<'tcx>, Span)>, +) { + let def_kind = tcx.def_kind(def_id); + if matches!(def_kind, DefKind::Closure) { + // Closures have fictitious weird type parameters in their `own_args` that we don't want to + // add `Drop` bounds for. + return; + } + // Add a `T: Drop` bound for every generic. + let drop_trait = tcx.lang_items().drop_trait().unwrap(); + let extra_bounds = tcx + .generics_of(def_id) + .own_params + .iter() + .filter(|param| matches!(param.kind, GenericParamDefKind::Type { .. })) + .map(|param| tcx.mk_param_from_def(param)) + .map(|ty| Binder::dummy(TraitRef::new(tcx, drop_trait, [ty]))) + .map(|tref| tref.upcast(tcx)) + .map(|clause| (clause, DUMMY_SP)); + predicates.extend(extra_bounds); +} + /// The predicates that must hold to mention this item. E.g. /// /// ```ignore @@ -67,7 +92,7 @@ pub fn required_predicates<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, options: BoundsOptions, -) -> GenericPredicates<'tcx> { +) -> Predicates<'tcx> { use DefKind::*; let def_kind = tcx.def_kind(def_id); let mut predicates = match def_kind { @@ -89,25 +114,15 @@ pub fn required_predicates<'tcx>( // `predicates_defined_on` ICEs on other def kinds. _ => Default::default(), }; - if options.resolve_drop { - // Add a `T: Drop` bound for every generic, unless the current trait is `Drop` itself, or - // `Sized`. - let drop_trait = tcx.lang_items().drop_trait().unwrap(); - let sized_trait = tcx.lang_items().sized_trait().unwrap(); - if def_id != drop_trait && def_id != sized_trait { - let extra_bounds = tcx - .generics_of(def_id) - .own_params - .iter() - .filter(|param| matches!(param.kind, GenericParamDefKind::Type { .. })) - .map(|param| tcx.mk_param_from_def(param)) - .map(|ty| Binder::dummy(TraitRef::new(tcx, drop_trait, [ty]))) - .map(|tref| tref.upcast(tcx)) - .map(|clause| (clause, DUMMY_SP)); - predicates.predicates = tcx - .arena - .alloc_from_iter(predicates.predicates.iter().copied().chain(extra_bounds)); - } + // For associated items in trait definitions, we add an explicit `Self: Trait` clause. + if let Some(trait_def_id) = tcx.trait_of_item(def_id) { + let self_clause = self_predicate(tcx, trait_def_id).upcast(tcx); + predicates.to_mut().insert(0, (self_clause, DUMMY_SP)); + } + if options.resolve_drop && !matches!(def_kind, Trait | TraitAlias) { + // Add a `T: Drop` bound for every generic. For traits we consider these predicates implied + // instead of required. + add_drop_bounds(tcx, def_id, predicates.to_mut()); } if options.prune_sized { prune_sized_predicates(tcx, &mut predicates); @@ -138,36 +153,47 @@ pub fn implied_predicates<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, options: BoundsOptions, -) -> GenericPredicates<'tcx> { +) -> Predicates<'tcx> { use DefKind::*; let parent = tcx.opt_parent(def_id); let mut predicates = match tcx.def_kind(def_id) { // We consider all predicates on traits to be outputs - Trait | TraitAlias => predicates_defined_on(tcx, def_id), + Trait | TraitAlias => { + let mut predicates = predicates_defined_on(tcx, def_id); + if options.resolve_drop { + // Add a `T: Drop` bound for every generic, unless the current trait is `Drop` itself, or a + // built-in marker trait that we know doesn't need the bound. + if !matches!( + tcx.as_lang_item(def_id), + Some( + LangItem::Drop + | LangItem::Sized + | LangItem::MetaSized + | LangItem::PointeeSized + | LangItem::DiscriminantKind + | LangItem::PointeeTrait + | LangItem::Tuple + ) + ) { + add_drop_bounds(tcx, def_id, predicates.to_mut()); + } + } + predicates + } AssocTy if matches!(tcx.def_kind(parent.unwrap()), Trait) => { - let mut predicates = GenericPredicates { - parent, - // `skip_binder` is for the GAT `EarlyBinder` - predicates: tcx.explicit_item_bounds(def_id).skip_binder(), - ..GenericPredicates::default() - }; + // `skip_binder` is for the GAT `EarlyBinder` + let mut predicates = Cow::Borrowed(tcx.explicit_item_bounds(def_id).skip_binder()); if options.resolve_drop { // Add a `Drop` bound to the assoc item. let drop_trait = tcx.lang_items().drop_trait().unwrap(); let ty = Ty::new_projection(tcx, def_id, GenericArgs::identity_for_item(tcx, def_id)); let tref = Binder::dummy(TraitRef::new(tcx, drop_trait, [ty])); - predicates.predicates = tcx.arena.alloc_from_iter( - predicates - .predicates - .iter() - .copied() - .chain([(tref.upcast(tcx), DUMMY_SP)]), - ); + predicates.to_mut().push((tref.upcast(tcx), DUMMY_SP)); } predicates } - _ => GenericPredicates::default(), + _ => Predicates::default(), }; if options.prune_sized { prune_sized_predicates(tcx, &mut predicates); @@ -175,10 +201,27 @@ pub fn implied_predicates<'tcx>( predicates } +/// Normalize a value. +pub fn normalize<'tcx, T>(tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>, value: T) -> T +where + T: TypeFoldable> + Clone, +{ + use rustc_infer::infer::TyCtxtInferExt; + use rustc_middle::traits::ObligationCause; + use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; + let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); + infcx + .at(&ObligationCause::dummy(), param_env) + .query_normalize(value.clone()) + // We ignore the generated outlives relations. Unsure what we should do with them. + .map(|x| x.value) + .unwrap_or(value) +} + /// Erase free regions from the given value. Largely copied from `tcx.erase_regions`, but also /// erases bound regions that are bound outside `value`, so we can call this function inside a /// `Binder`. -fn erase_free_regions<'tcx, T>(tcx: TyCtxt<'tcx>, value: T) -> T +pub fn erase_free_regions<'tcx, T>(tcx: TyCtxt<'tcx>, value: T) -> T where T: TypeFoldable>, { @@ -260,12 +303,8 @@ pub fn is_sized_related_trait<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { /// Given a `GenericPredicates`, prune every occurence of a sized-related clause. /// Prunes bounds of the shape `T: MetaSized`, `T: Sized` or `T: PointeeSized`. -fn prune_sized_predicates<'tcx>( - tcx: TyCtxt<'tcx>, - generic_predicates: &mut GenericPredicates<'tcx>, -) { +fn prune_sized_predicates<'tcx>(tcx: TyCtxt<'tcx>, generic_predicates: &mut Predicates<'tcx>) { let predicates: Vec<(Clause<'tcx>, rustc_span::Span)> = generic_predicates - .predicates .iter() .filter(|(clause, _)| { clause.as_trait_clause().is_some_and(|trait_predicate| { @@ -274,8 +313,8 @@ fn prune_sized_predicates<'tcx>( }) .copied() .collect(); - if predicates.len() != generic_predicates.predicates.len() { - generic_predicates.predicates = tcx.arena.alloc_slice(&predicates); + if predicates.len() != generic_predicates.len() { + *generic_predicates.to_mut() = predicates; } } diff --git a/frontend/exporter/src/types/def_id.rs b/frontend/exporter/src/types/def_id.rs index 14196789c..3c07a6667 100644 --- a/frontend/exporter/src/types/def_id.rs +++ b/frontend/exporter/src/types/def_id.rs @@ -206,10 +206,6 @@ impl DefId { index: rustc_hir::def_id::DefIndex::from_u32(index), } } - pub fn promoted_id(&self) -> Option { - let (_, _, promoted) = self.index; - promoted - } /// Iterate over this element and its parents. pub fn ancestry(&self) -> impl Iterator { @@ -255,6 +251,13 @@ impl DefId { } } +impl DefId { + pub fn promoted_id(&self) -> Option { + let (_, _, promoted) = self.index; + promoted + } +} + impl std::ops::Deref for DefId { type Target = DefIdContents; fn deref(&self) -> &Self::Target { @@ -290,6 +293,7 @@ impl std::hash::Hash for DefId { // the information. self.krate.hash(state); self.path.hash(state); + self.promoted_id().hash(state); } } @@ -310,7 +314,7 @@ pub(crate) fn translate_def_id<'tcx, S: BaseState<'tcx>>(s: &S, def_id: RDefId) let tcx = s.base().tcx; let path = { // Set the def_id so the `CrateRoot` path item can fetch the crate name. - let state_with_id = with_owner_id(s.base(), (), (), def_id); + let state_with_id = s.with_owner_id(def_id); tcx.def_path(def_id) .data .iter() @@ -367,7 +371,7 @@ impl std::convert::From for Path { pub type GlobalIdent = DefId; #[cfg(all(not(feature = "extract_names_mode"), feature = "rustc"))] -impl<'tcx, S: UnderOwnerState<'tcx>> SInto for rustc_hir::def_id::LocalDefId { +impl<'tcx, S: BaseState<'tcx>> SInto for rustc_hir::def_id::LocalDefId { fn sinto(&self, st: &S) -> DefId { self.to_def_id().sinto(st) } diff --git a/frontend/exporter/src/types/hir.rs b/frontend/exporter/src/types/hir.rs index a0a0a3276..9c64101f5 100644 --- a/frontend/exporter/src/types/hir.rs +++ b/frontend/exporter/src/types/hir.rs @@ -225,11 +225,11 @@ pub struct Generics { } #[cfg(feature = "rustc")] -impl<'tcx, S: UnderOwnerState<'tcx>, Body: IsBody> SInto> for hir::ImplItemId { +impl<'tcx, S: BaseState<'tcx>, Body: IsBody> SInto> for hir::ImplItemId { fn sinto(&self, s: &S) -> ImplItem { let tcx: rustc_middle::ty::TyCtxt = s.base().tcx; let impl_item = tcx.hir_impl_item(*self); - let s = with_owner_id(s.base(), (), (), impl_item.owner_id.to_def_id()); + let s = s.with_owner_id(impl_item.owner_id.to_def_id()); impl_item.sinto(&s) } } @@ -257,12 +257,12 @@ pub enum LifetimeParamKind { /// Reflects [`hir::AnonConst`] #[derive_group(Serializers)] #[derive(AdtInto, Clone, Debug, JsonSchema)] -#[args(<'tcx, S: UnderOwnerState<'tcx>>, from: hir::AnonConst, state: S as s)] +#[args(<'tcx, S: BaseState<'tcx>>, from: hir::AnonConst, state: S as s)] pub struct AnonConst { pub hir_id: HirId, pub def_id: GlobalIdent, #[map({ - body_from_id::(*x, &with_owner_id(s.base(), (), (), hir_id.owner.to_def_id())) + body_from_id::(*x, &s.with_owner_id(hir_id.owner.to_def_id())) })] pub body: Body, } @@ -504,7 +504,7 @@ pub struct Variant { pub ident: Ident, pub hir_id: HirId, pub def_id: GlobalIdent, - #[map(x.sinto(&with_owner_id(s.base(), (), (), self.def_id.to_def_id())))] + #[map(x.sinto(&s.with_owner_id(self.def_id.to_def_id())))] pub data: VariantData, pub disr_expr: Option>, #[value({ @@ -645,13 +645,7 @@ pub enum ItemKind { Ident, Generics, #[map({ - let s = &State { - base: Base {ty_alias_mode: true, ..s.base()}, - owner_id: s.owner_id(), - thir: (), - mir: (), - binder: (), - }; + let s = &s.with_base(Base { ty_alias_mode: true, ..s.base() }); x.sinto(s) })] Ty, @@ -737,9 +731,9 @@ impl<'tcx, S: UnderOwnerState<'tcx>, Body: IsBody> SInto> for h } #[cfg(feature = "rustc")] -impl<'a, S: UnderOwnerState<'a>, Body: IsBody> SInto> for hir::TraitItemId { +impl<'a, S: BaseState<'a>, Body: IsBody> SInto> for hir::TraitItemId { fn sinto(&self, s: &S) -> TraitItem { - let s = with_owner_id(s.base(), (), (), self.owner_id.to_def_id()); + let s = s.with_owner_id(self.owner_id.to_def_id()); let tcx: rustc_middle::ty::TyCtxt = s.base().tcx; tcx.hir_trait_item(*self).sinto(&s) } @@ -826,7 +820,6 @@ fn region_bounds_at_current_owner<'tcx, S: UnderOwnerState<'tcx>>(s: &S) -> Gene .instantiate_identity() } else { predicates_defined_on(tcx, s.owner_id()) - .predicates .iter() .map(|(x, _span)| x) .copied() @@ -934,8 +927,8 @@ impl<'tcx, S: BaseState<'tcx>, Body: IsBody> SInto> for hir::Item< | TraitAlias(i, ..) => i.name.to_ident_string(), Use(..) | ForeignMod { .. } | GlobalAsm { .. } | Impl { .. } => String::new(), }; + let s = &s.with_owner_id(self.owner_id.to_def_id()); let tcx = s.base().tcx; - let s = &with_owner_id(s.base(), (), (), self.owner_id.to_def_id()); let owner_id: DefId = self.owner_id.sinto(s); let def_id = Path::from(owner_id.clone()) .ends_with(&[name]) diff --git a/frontend/exporter/src/types/mir.rs b/frontend/exporter/src/types/mir.rs index fda473a6a..352843904 100644 --- a/frontend/exporter/src/types/mir.rs +++ b/frontend/exporter/src/types/mir.rs @@ -258,10 +258,10 @@ fn translate_mir_const<'tcx, S: UnderOwnerState<'tcx>>( ); match ucv.promoted { Some(promoted) => { - let mut item = translate_item_ref(s, ucv.def, ucv.args); - item.mutate(s, |item| { + let item = translate_item_ref(s, ucv.def, ucv.args); + let item = item.mutate_def_id(s, |def_id| { // Construct a def_id for the promoted constant. - item.def_id = item.def_id.make_promoted_child(s, promoted.sinto(s)); + *def_id = def_id.make_promoted_child(s, promoted.sinto(s)); }); Promoted(item) } @@ -352,7 +352,7 @@ pub struct Terminator { } #[cfg(feature = "rustc")] -fn translate_terminator_kind_call<'tcx, S: BaseState<'tcx> + HasMir<'tcx> + HasOwnerId>( +fn translate_terminator_kind_call<'tcx, S: UnderOwnerState<'tcx> + HasMir<'tcx>>( s: &S, terminator: &rustc_middle::mir::TerminatorKind<'tcx>, ) -> TerminatorKind { @@ -435,7 +435,7 @@ fn translate_terminator_kind_call<'tcx, S: BaseState<'tcx> + HasMir<'tcx> + HasO } #[cfg(feature = "rustc")] -fn translate_terminator_kind_drop<'tcx, S: BaseState<'tcx> + HasMir<'tcx> + HasOwnerId>( +fn translate_terminator_kind_drop<'tcx, S: UnderOwnerState<'tcx> + HasMir<'tcx>>( s: &S, terminator: &rustc_middle::mir::TerminatorKind<'tcx>, ) -> TerminatorKind { @@ -863,8 +863,7 @@ pub enum AggregateKind { } #[derive_group(Serializers)] -#[derive(AdtInto, Clone, Debug, JsonSchema)] -#[args(<'tcx, S>, from: rustc_middle::mir::CastKind, state: S as _s)] +#[derive(Clone, Debug, JsonSchema)] pub enum CastKind { PointerExposeProvenance, PointerWithExposedProvenance, @@ -878,6 +877,32 @@ pub enum CastKind { Transmute, } +#[cfg(feature = "rustc")] +impl CastKind { + fn sfrom<'tcx, S: UnderOwnerState<'tcx>>( + s: &S, + kind: mir::CastKind, + src_ty: ty::Ty<'tcx>, + tgt_ty: ty::Ty<'tcx>, + ) -> CastKind { + match kind { + mir::CastKind::PointerExposeProvenance => CastKind::PointerExposeProvenance, + mir::CastKind::PointerWithExposedProvenance => CastKind::PointerWithExposedProvenance, + mir::CastKind::PointerCoercion(coercion, y) => { + let coercion = PointerCoercion::sfrom(s, coercion, src_ty, tgt_ty); + CastKind::PointerCoercion(coercion, y.sinto(s)) + } + mir::CastKind::IntToInt => CastKind::IntToInt, + mir::CastKind::FloatToInt => CastKind::FloatToInt, + mir::CastKind::FloatToFloat => CastKind::FloatToFloat, + mir::CastKind::IntToFloat => CastKind::IntToFloat, + mir::CastKind::PtrToPtr => CastKind::PtrToPtr, + mir::CastKind::FnPtrToPtr => CastKind::FnPtrToPtr, + mir::CastKind::Transmute => CastKind::Transmute, + } + } +} + #[derive_group(Serializers)] #[derive(AdtInto, Clone, Debug, JsonSchema)] #[args(<'tcx, S>, from: rustc_middle::mir::CoercionSource, state: S as _s)] @@ -902,17 +927,18 @@ pub enum NullOp { #[args(<'tcx, S: UnderOwnerState<'tcx> + HasMir<'tcx>>, from: rustc_middle::mir::Rvalue<'tcx>, state: S as s)] pub enum Rvalue { Use(Operand), - #[custom_arm( - rustc_middle::mir::Rvalue::Repeat(op, ce) => { - let op = op.sinto(s); - Rvalue::Repeat(op, ce.sinto(s)) - }, - )] Repeat(Operand, ConstantExpr), Ref(Region, BorrowKind, Place), ThreadLocalRef(DefId), RawPtr(RawPtrKind, Place), Len(Place), + #[custom_arm( + FROM_TYPE::Cast(kind, op, tgt_ty) => { + let src_ty = op.ty(&*s.mir(), s.base().tcx); + let kind = CastKind::sfrom(s, *kind, src_ty, *tgt_ty); + TO_TYPE::Cast(kind, op.sinto(s), tgt_ty.sinto(s)) + }, + )] Cast(CastKind, Operand, Ty), BinaryOp(BinOp, (Operand, Operand)), NullaryOp(NullOp, Ty), diff --git a/frontend/exporter/src/types/new/full_def.rs b/frontend/exporter/src/types/new/full_def.rs index acebc30f9..6596a0994 100644 --- a/frontend/exporter/src/types/new/full_def.rs +++ b/frontend/exporter/src/types/new/full_def.rs @@ -1,12 +1,13 @@ use crate::prelude::*; -use std::sync::Arc; #[cfg(feature = "rustc")] use rustc_hir::def::DefKind as RDefKind; #[cfg(feature = "rustc")] -use rustc_middle::{mir, ty}; +use rustc_middle::ty; #[cfg(feature = "rustc")] use rustc_span::def_id::DefId as RDefId; +#[cfg(feature = "rustc")] +use std::sync::Arc; /// Hack: charon used to rely on the old `()` default everywhere. To avoid big merge conflicts with /// in-flight PRs we're changing the default here. Eventually this should be removed. @@ -16,9 +17,9 @@ type DefaultFullDefBody = MirBody; #[derive_group(Serializers)] #[derive(Clone, Debug, JsonSchema)] pub struct FullDef { - pub def_id: DefId, - /// The enclosing item. - pub parent: Option, + /// A reference to the current item. If the item was provided with generic args, they are + /// stored here; otherwise the args are the identity_args for this item. + pub this: ItemRef, /// The span of the definition of this item (e.g. for a function this is is signature). pub span: Span, /// The span of the whole definition (including e.g. the function body). @@ -37,14 +38,19 @@ pub struct FullDef { } #[cfg(feature = "rustc")] -fn translate_full_def<'tcx, S, Body>(s: &S, def_id: &DefId) -> FullDef +/// Construct the `FullDefKind` for this item. If `args` is `Some`, the returned `FullDef` will be +/// instantiated with the provided generics. +fn translate_full_def<'tcx, S, Body>( + s: &S, + def_id: &DefId, + args: Option>, +) -> FullDef where - S: BaseState<'tcx>, + S: UnderOwnerState<'tcx>, Body: IsBody + TypeMappable, { let tcx = s.base().tcx; let rust_def_id = def_id.underlying_rust_def_id(); - let state_with_id = with_owner_id(s.base(), (), (), rust_def_id); let source_span; let attributes; let visibility; @@ -53,9 +59,9 @@ where let kind; match def_id.promoted_id() { None => { - let def_kind = get_def_kind(tcx, rust_def_id); - kind = def_kind.sinto(&state_with_id); + kind = translate_full_def_kind(s, rust_def_id, args); + let def_kind = get_def_kind(tcx, rust_def_id); source_span = rust_def_id.as_local().map(|ldid| tcx.source_span(ldid)); attributes = get_def_attrs(tcx, rust_def_id, def_kind).sinto(s); visibility = get_def_visibility(tcx, rust_def_id, def_kind); @@ -69,7 +75,11 @@ where } Some(promoted_id) => { - let parent_def = def_id.parent.as_ref().unwrap().full_def::<_, Body>(s); + let parent_def = def_id + .parent + .as_ref() + .unwrap() + .full_def_maybe_instantiated::<_, Body>(s, args); let parent_param_env = parent_def.param_env().unwrap(); let param_env = ParamEnv { generics: TyGenerics { @@ -80,20 +90,18 @@ where has_late_bound_regions: None, }, predicates: GenericPredicates { predicates: vec![] }, + parent: Some(parent_def.this().clone()), }; let body = get_promoted_mir(tcx, rust_def_id, promoted_id.as_rust_promoted_id()); + let body = substitute(tcx, s.typing_env(), args, body); source_span = Some(body.span); - let ty: Ty = body.local_decls[rustc_middle::mir::Local::ZERO] - .ty - .sinto(&state_with_id); - // Promoted constants only happen within MIR bodies; we can therefore assume that - // `Body` is a MIR body and unwrap. - let body = Body::from_mir(&state_with_id, body).s_unwrap(s); - kind = FullDefKind::PromotedConst { + let ty: Ty = body.local_decls[rustc_middle::mir::Local::ZERO].ty.sinto(s); + kind = FullDefKind::Const { param_env, ty, - body, + kind: ConstKind::PromotedConst, + body: Body::from_mir(s, body), }; // None of these make sense for a promoted constant. @@ -107,9 +115,18 @@ where let source_text = source_span .filter(|source_span| source_span.ctxt().is_root()) .and_then(|source_span| tcx.sess.source_map().span_to_snippet(source_span).ok()); + let this = if can_have_generics(tcx, rust_def_id) { + let args_or_default = + args.unwrap_or_else(|| ty::GenericArgs::identity_for_item(tcx, rust_def_id)); + let item = translate_item_ref(s, rust_def_id, args_or_default); + // Tricky: hax's DefId has more info (could be a promoted const), we must be careful to use + // the input DefId instead of the one derived from `rust_def_id`. + item.with_def_id(s, def_id) + } else { + ItemRef::dummy_without_generics(s, def_id.clone()) + }; FullDef { - def_id: def_id.clone(), - parent: def_id.parent.clone(), + this, span: def_id.def_span(s), source_span: source_span.sinto(s), source_text, @@ -137,33 +154,56 @@ impl DefId { /// Get the full definition of this item. pub fn full_def<'tcx, S, Body>(&self, s: &S) -> Arc> + where + Body: IsBody + TypeMappable, + S: BaseState<'tcx>, + { + self.full_def_maybe_instantiated(s, None) + } + + /// Get the full definition of this item, instantiated if `args` is `Some`. + pub fn full_def_maybe_instantiated<'tcx, S, Body>( + &self, + s: &S, + args: Option>, + ) -> Arc> where Body: IsBody + TypeMappable, S: BaseState<'tcx>, { let rust_def_id = self.underlying_rust_def_id(); - if let Some(def) = s.with_item_cache(rust_def_id, |cache| match self.promoted_id() { - None => cache.full_def.get().cloned(), - Some(promoted_id) => cache.promoteds.or_default().get(&promoted_id).cloned(), - }) { + let s = &s.with_owner_id(rust_def_id); + let cache_key = (self.promoted_id(), args); + if let Some(def) = + s.with_cache(|cache| cache.full_defs.entry(cache_key).or_default().get().cloned()) + { return def; } - let def = Arc::new(translate_full_def(s, self)); - s.with_item_cache(rust_def_id, |cache| match self.promoted_id() { - None => { - cache.full_def.insert(def.clone()); - } - Some(promoted_id) => { - cache - .promoteds - .or_default() - .insert(promoted_id, def.clone()); - } + let def = Arc::new(translate_full_def(s, self, args)); + s.with_cache(|cache| { + cache + .full_defs + .entry(cache_key) + .or_default() + .insert(def.clone()); }); def } } +#[cfg(feature = "rustc")] +impl ItemRef { + /// Get the full definition of the item, instantiated with the provided generics. + pub fn instantiated_full_def<'tcx, S, Body>(&self, s: &S) -> Arc> + where + Body: IsBody + TypeMappable, + S: BaseState<'tcx>, + { + let args = self.rustc_args(s); + self.def_id.full_def_maybe_instantiated(s, Some(args)) + } +} + /// The combination of type generics and related predicates. #[derive_group(Serializers)] #[derive(Clone, Debug, JsonSchema)] @@ -172,78 +212,53 @@ pub struct ParamEnv { pub generics: TyGenerics, /// Required predicates for the item (see `traits::utils::required_predicates`). pub predicates: GenericPredicates, + /// A reference to the parent of this item, with appropriate args. + pub parent: Option, +} + +/// The kind of a constant item. +#[derive_group(Serializers)] +#[derive(Clone, Debug, JsonSchema)] +pub enum ConstKind { + /// Top-level constant: `const CONST: usize = 42;` + TopLevel, + /// Anonymous constant, e.g. the `1 + 2` in `[u8; 1 + 2]` + AnonConst, + /// An inline constant, e.g. `const { 1 + 2 }` + InlineConst, + /// A promoted constant, e.g. the `1 + 2` in `&(1 + 2)` + PromotedConst, } /// Imbues [`rustc_hir::def::DefKind`] with a lot of extra information. -/// Important: the `owner_id()` must be the id of this definition. -#[derive(AdtInto)] -#[args(<'tcx, S: UnderOwnerState<'tcx>>, from: rustc_hir::def::DefKind, state: S as s, where Body: IsBody + TypeMappable)] #[derive_group(Serializers)] #[derive(Clone, Debug, JsonSchema)] pub enum FullDefKind { // Types - /// Refers to the struct definition, [`DefKind::Ctor`] refers to its constructor if it exists. - Struct { - #[value(get_param_env(s, s.owner_id()))] + /// ADts (`Struct`, `Enum` and `Union` map to this variant). + Adt { param_env: ParamEnv, - #[value(s.base().tcx.adt_def(s.owner_id()).sinto(s))] - def: AdtDef, + adt_kind: AdtKind, + variants: IndexVec, + flags: AdtFlags, + repr: ReprOptions, /// MIR body of the builtin `drop` impl. - #[value(drop_glue_shim(s.base().tcx, s.owner_id()).and_then(|body| Body::from_mir(s, body)))] - drop_glue: Option, - }, - Union { - #[value(get_param_env(s, s.owner_id()))] - param_env: ParamEnv, - #[value(s.base().tcx.adt_def(s.owner_id()).sinto(s))] - def: AdtDef, - /// MIR body of the builtin `drop` impl. - #[value(drop_glue_shim(s.base().tcx, s.owner_id()).and_then(|body| Body::from_mir(s, body)))] - drop_glue: Option, - }, - Enum { - #[value(get_param_env(s, s.owner_id()))] - param_env: ParamEnv, - #[value(s.base().tcx.adt_def(s.owner_id()).sinto(s))] - def: AdtDef, - /// MIR body of the builtin `drop` impl. - #[value(drop_glue_shim(s.base().tcx, s.owner_id()).and_then(|body| Body::from_mir(s, body)))] drop_glue: Option, + /// Info required to construct a virtual `Drop` impl for this adt. + drop_impl: Box, }, /// Type alias: `type Foo = Bar;` TyAlias { - #[value(get_param_env(s, s.owner_id()))] param_env: ParamEnv, - /// `Some` if the item is in the local crate. - #[value(s.base().tcx.hir_get_if_local(s.owner_id()).map(|node| { - let rustc_hir::Node::Item(item) = node else { unreachable!() }; - let rustc_hir::ItemKind::TyAlias(_, _generics, ty) = &item.kind else { unreachable!() }; - let mut s = State::from_under_owner(s); - s.base.ty_alias_mode = true; - ty.sinto(&s) - }))] - ty: Option, + ty: Ty, }, /// Type from an `extern` block. ForeignTy, /// Associated type: `trait MyTrait { type Assoc; }` AssocTy { - #[value(s.base().tcx.parent(s.owner_id()).sinto(s))] - parent: DefId, - #[value(get_param_env(s, s.owner_id()))] param_env: ParamEnv, - #[value(implied_predicates(s.base().tcx, s.owner_id(), s.base().options.bounds_options).sinto(s))] implied_predicates: GenericPredicates, - #[value(s.base().tcx.associated_item(s.owner_id()).sinto(s))] associated_item: AssocItem, - #[value({ - let tcx = s.base().tcx; - if tcx.defaultness(s.owner_id()).has_value() { - Some(tcx.type_of(s.owner_id()).instantiate_identity().sinto(s)) - } else { - None - } - })] value: Option, }, /// Opaque type, aka `impl Trait`. @@ -251,42 +266,20 @@ pub enum FullDefKind { // Traits Trait { - #[value(get_param_env(s, s.owner_id()))] param_env: ParamEnv, - #[value(implied_predicates(s.base().tcx, s.owner_id(), s.base().options.bounds_options).sinto(s))] implied_predicates: GenericPredicates, /// The special `Self: Trait` clause. - #[value(get_self_predicate(s))] self_predicate: TraitPredicate, /// Associated items, in definition order. - #[value( - s - .base() - .tcx - .associated_items(s.owner_id()) - .in_definition_order() - .map(|assoc| { - let def_id = assoc.def_id.sinto(s); - (assoc.sinto(s), def_id.full_def(s)) - }) - .collect::>() - )] - items: Vec<(AssocItem, Arc>)>, + items: Vec, }, /// Trait alias: `trait IntIterator = Iterator;` TraitAlias { - #[value(get_param_env(s, s.owner_id()))] param_env: ParamEnv, - #[value(implied_predicates(s.base().tcx, s.owner_id(), s.base().options.bounds_options).sinto(s))] implied_predicates: GenericPredicates, /// The special `Self: Trait` clause. - #[value(get_self_predicate(s))] self_predicate: TraitPredicate, }, - #[custom_arm( - // Returns `TraitImpl` or `InherentImpl`. - RDefKind::Impl { .. } => get_impl_contents(s), - )] TraitImpl { param_env: ParamEnv, /// The trait that is implemented by this impl block. @@ -298,46 +291,32 @@ pub enum FullDefKind { /// ``` implied_impl_exprs: Vec, /// Associated items, in the order of the trait declaration. Includes defaulted items. - items: Vec>, + items: Vec, }, - #[disable_mapping] InherentImpl { param_env: ParamEnv, /// The type to which this block applies. ty: Ty, /// Associated items, in definition order. - items: Vec<(AssocItem, Arc>)>, + items: Vec, }, // Functions Fn { - #[value(get_param_env(s, s.owner_id()))] param_env: ParamEnv, - #[value(s.base().tcx.codegen_fn_attrs(s.owner_id()).inline.sinto(s))] inline: InlineAttr, - #[value(s.base().tcx.constness(s.owner_id()) == rustc_hir::Constness::Const)] is_const: bool, - #[value(s.base().tcx.fn_sig(s.owner_id()).instantiate_identity().sinto(s))] sig: PolyFnSig, - #[value(Body::body(s.owner_id(), s))] body: Option, }, /// Associated function: `impl MyStruct { fn associated() {} }` or `trait Foo { fn associated() /// {} }` AssocFn { - #[value(s.base().tcx.parent(s.owner_id()).sinto(s))] - parent: DefId, - #[value(get_param_env(s, s.owner_id()))] param_env: ParamEnv, - #[value(s.base().tcx.associated_item(s.owner_id()).sinto(s))] associated_item: AssocItem, - #[value(s.base().tcx.codegen_fn_attrs(s.owner_id()).inline.sinto(s))] inline: InlineAttr, - #[value(s.base().tcx.constness(s.owner_id()) == rustc_hir::Constness::Const)] is_const: bool, - #[value(get_method_sig(s).sinto(s))] sig: PolyFnSig, - #[value(Body::body(s.owner_id(), s))] body: Option, }, /// A closure, coroutine, or coroutine-closure. @@ -345,82 +324,38 @@ pub enum FullDefKind { /// Note: the (early-bound) generics of a closure are the same as those of the item in which it /// is defined. Closure { - /// The enclosing item. Note: this item could itself be a closure; to get the generics, you - /// might have to recurse through several layers of parents until you find a function or - /// constant. - #[value(s.base().tcx.parent(s.owner_id()).sinto(s))] - parent: DefId, - #[value(s.base().tcx.constness(s.owner_id()) == rustc_hir::Constness::Const)] - is_const: bool, - #[value({ - let closure_ty = s.base().tcx.type_of(s.owner_id()).instantiate_identity(); - let ty::TyKind::Closure(_, args) = closure_ty.kind() else { unreachable!() }; - ClosureArgs::sfrom(s, s.owner_id(), args.as_closure()) - })] args: ClosureArgs, + is_const: bool, + /// Info required to construct a virtual `FnOnce` impl for this closure. + fn_once_impl: Box, + /// Info required to construct a virtual `FnMut` impl for this closure. + fn_mut_impl: Option>, + /// Info required to construct a virtual `Fn` impl for this closure. + fn_impl: Option>, /// For `FnMut`&`Fn` closures: the MIR for the `call_once` method; it simply calls /// `call_mut`. - #[value({ - let tcx = s.base().tcx; - let closure_ty = tcx.type_of(s.owner_id()).instantiate_identity(); - let opt_body = closure_once_shim(tcx, closure_ty); - opt_body.and_then(|body| Body::from_mir(s, body)) - })] once_shim: Option, /// MIR body of the builtin `drop` impl. - #[value(drop_glue_shim(s.base().tcx, s.owner_id()).and_then(|body| Body::from_mir(s, body)))] drop_glue: Option, + /// Info required to construct a virtual `Drop` impl for this closure. + drop_impl: Box, }, // Constants Const { - #[value(get_param_env(s, s.owner_id()))] param_env: ParamEnv, - #[value(s.base().tcx.type_of(s.owner_id()).instantiate_identity().sinto(s))] ty: Ty, - #[value(Body::body(s.owner_id(), s))] + kind: ConstKind, body: Option, }, /// Associated constant: `trait MyTrait { const ASSOC: usize; }` AssocConst { - #[value(s.base().tcx.parent(s.owner_id()).sinto(s))] - parent: DefId, - #[value(get_param_env(s, s.owner_id()))] param_env: ParamEnv, - #[value(s.base().tcx.associated_item(s.owner_id()).sinto(s))] associated_item: AssocItem, - #[value(s.base().tcx.type_of(s.owner_id()).instantiate_identity().sinto(s))] ty: Ty, - #[value(Body::body(s.owner_id(), s))] body: Option, }, - /// Anonymous constant, e.g. the `1 + 2` in `[u8; 1 + 2]` - AnonConst { - #[value(get_param_env(s, s.owner_id()))] - param_env: ParamEnv, - #[value(s.base().tcx.type_of(s.owner_id()).instantiate_identity().sinto(s))] - ty: Ty, - #[value(Body::body(s.owner_id(), s))] - body: Option, - }, - /// An inline constant, e.g. `const { 1 + 2 }` - InlineConst { - #[value(get_param_env(s, s.owner_id()))] - param_env: ParamEnv, - #[value(s.base().tcx.type_of(s.owner_id()).instantiate_identity().sinto(s))] - ty: Ty, - #[value(Body::body(s.owner_id(), s))] - body: Option, - }, - /// A promoted constant, e.g. `&(1 + 2)` - #[disable_mapping] - PromotedConst { - param_env: ParamEnv, - ty: Ty, - body: Body, - }, Static { - #[value(get_param_env(s, s.owner_id()))] param_env: ParamEnv, /// Whether it's a `unsafe static`, `safe static` (inside extern only) or just a `static`. safety: Safety, @@ -428,9 +363,7 @@ pub enum FullDefKind { mutability: Mutability, /// Whether it's an anonymous static generated for nested allocations. nested: bool, - #[value(s.base().tcx.type_of(s.owner_id()).instantiate_identity().sinto(s))] ty: Ty, - #[value(Body::body(s.owner_id(), s))] body: Option, }, @@ -438,12 +371,10 @@ pub enum FullDefKind { ExternCrate, Use, Mod { - #[value(get_mod_children(s.base().tcx, s.owner_id()).sinto(s))] items: Vec<(Option, DefId)>, }, /// An `extern` block. ForeignMod { - #[value(get_foreign_mod_children(s.base().tcx, s.owner_id()).sinto(s))] items: Vec, }, @@ -459,7 +390,6 @@ pub enum FullDefKind { /// Refers to the variant definition, [`DefKind::Ctor`] refers to its constructor if it exists. Variant, /// The constructor function of a tuple/unit struct or tuple/unit enum variant. - #[custom_arm(RDefKind::Ctor(ctor_of, _) => get_ctor_contents(s, ctor_of.sinto(s)),)] Ctor { adt_def_id: DefId, ctor_of: CtorOf, @@ -482,16 +412,366 @@ pub enum FullDefKind { SyntheticCoroutineBody, } +#[cfg(feature = "rustc")] +/// Construct the `FullDefKind` for this item. +/// +/// If `args` is `Some`, instantiate the whole definition with these generics; otherwise keep the +/// polymorphic definition. +// Note: this is tricky to get right, we have to make sure to isntantiate every single field that +// may contain a type/const/trait reference. +fn translate_full_def_kind<'tcx, S, Body>( + s: &S, + def_id: RDefId, + args: Option>, +) -> FullDefKind +where + S: BaseState<'tcx>, + Body: IsBody + TypeMappable, +{ + let s = &s.with_owner_id(def_id); + let tcx = s.base().tcx; + let type_of_self = || inst_binder(tcx, s.typing_env(), args, tcx.type_of(def_id)); + let args_or_default = + || args.unwrap_or_else(|| ty::GenericArgs::identity_for_item(tcx, def_id)); + match get_def_kind(tcx, def_id) { + RDefKind::Struct { .. } | RDefKind::Union { .. } | RDefKind::Enum { .. } => { + let def = tcx.adt_def(def_id); + let variants = def + .variants() + .iter_enumerated() + .map(|(variant_idx, variant)| { + let discr = if def.is_enum() { + def.discriminant_for_variant(tcx, variant_idx) + } else { + // Structs and unions have a single variant. + assert_eq!(variant_idx.index(), 0); + ty::util::Discr { + val: 0, + ty: tcx.types.isize, + } + }; + VariantDef::sfrom(s, variant, discr, args) + }) + .collect(); + + let drop_trait = tcx.lang_items().drop_trait().unwrap(); + FullDefKind::Adt { + param_env: get_param_env(s, args), + adt_kind: def.adt_kind().sinto(s), + variants, + flags: def.flags().sinto(s), + repr: def.repr().sinto(s), + drop_glue: get_drop_glue_shim(s, args), + drop_impl: virtual_impl_for( + s, + ty::TraitRef::new(tcx, drop_trait, [type_of_self()]), + ), + } + } + RDefKind::TyAlias { .. } => { + let s = &s.with_base(Base { + ty_alias_mode: true, + ..s.base() + }); + FullDefKind::TyAlias { + param_env: get_param_env(s, args), + ty: type_of_self().sinto(s), + } + } + RDefKind::ForeignTy => FullDefKind::ForeignTy, + RDefKind::AssocTy { .. } => FullDefKind::AssocTy { + param_env: get_param_env(s, args), + implied_predicates: get_implied_predicates(s, args), + associated_item: AssocItem::sfrom_instantiated(s, &tcx.associated_item(def_id), args), + value: if tcx.defaultness(def_id).has_value() { + Some(type_of_self().sinto(s)) + } else { + None + }, + }, + RDefKind::OpaqueTy => FullDefKind::OpaqueTy, + RDefKind::Trait { .. } => FullDefKind::Trait { + param_env: get_param_env(s, args), + implied_predicates: get_implied_predicates(s, args), + self_predicate: get_self_predicate(s, args), + items: tcx + .associated_items(def_id) + .in_definition_order() + .map(|assoc| { + let item_args = args.map(|args| { + let item_identity_args = + ty::GenericArgs::identity_for_item(tcx, assoc.def_id); + let item_args = item_identity_args.rebase_onto(tcx, def_id, args); + tcx.mk_args(item_args) + }); + AssocItem::sfrom_instantiated(s, assoc, item_args) + }) + .collect::>(), + }, + RDefKind::TraitAlias { .. } => FullDefKind::TraitAlias { + param_env: get_param_env(s, args), + implied_predicates: get_implied_predicates(s, args), + self_predicate: get_self_predicate(s, args), + }, + RDefKind::Impl { .. } => { + use std::collections::HashMap; + let param_env = get_param_env(s, args); + match inst_binder(tcx, s.typing_env(), args, tcx.impl_subject(def_id)) { + ty::ImplSubject::Inherent(ty) => { + let items = tcx + .associated_items(def_id) + .in_definition_order() + .map(|assoc| { + let item_args = args.map(|args| { + let item_identity_args = + ty::GenericArgs::identity_for_item(tcx, assoc.def_id); + let item_args = item_identity_args.rebase_onto(tcx, def_id, args); + tcx.mk_args(item_args) + }); + AssocItem::sfrom_instantiated(s, assoc, item_args) + }) + .collect::>(); + FullDefKind::InherentImpl { + param_env, + ty: ty.sinto(s), + items, + } + } + ty::ImplSubject::Trait(trait_ref) => { + // Also record the polarity. + let polarity = tcx.impl_polarity(def_id); + let trait_pred = TraitPredicate { + trait_ref: trait_ref.sinto(s), + is_positive: matches!(polarity, ty::ImplPolarity::Positive), + }; + // Impl exprs required by the trait. + let required_impl_exprs = + solve_item_implied_traits(s, trait_ref.def_id, trait_ref.args); + + let mut item_map: HashMap = tcx + .associated_items(def_id) + .in_definition_order() + .map(|assoc| (assoc.trait_item_def_id.unwrap(), assoc)) + .collect(); + let items = tcx + .associated_items(trait_ref.def_id) + .in_definition_order() + .map(|decl_assoc| { + let decl_def_id = decl_assoc.def_id; + // Impl exprs required by the item. + let required_impl_exprs; + let value = match item_map.remove(&decl_def_id) { + Some(impl_assoc) => { + required_impl_exprs = { + let item_args = ty::GenericArgs::identity_for_item( + tcx, + impl_assoc.def_id, + ); + // Subtlety: we have to add the GAT arguments (if any) to the trait ref arguments. + let args = + item_args.rebase_onto(tcx, def_id, trait_ref.args); + let state_with_id = s.with_owner_id(impl_assoc.def_id); + solve_item_implied_traits(&state_with_id, decl_def_id, args) + }; + + ImplAssocItemValue::Provided { + def_id: impl_assoc.def_id.sinto(s), + is_override: decl_assoc.defaultness(tcx).has_value(), + } + } + None => { + required_impl_exprs = if tcx + .generics_of(decl_def_id) + .is_own_empty() + { + // Non-GAT case. + let item_args = + ty::GenericArgs::identity_for_item(tcx, decl_def_id); + let args = + item_args.rebase_onto(tcx, def_id, trait_ref.args); + // TODO: is it the right `def_id`? + let state_with_id = s.with_owner_id(def_id); + solve_item_implied_traits(&state_with_id, decl_def_id, args) + } else { + // FIXME: For GATs, we need a param_env that has the arguments of + // the impl plus those of the associated type, but there's no + // def_id with that param_env. + vec![] + }; + match decl_assoc.kind { + ty::AssocKind::Type { .. } => { + let ty = tcx + .type_of(decl_def_id) + .instantiate(tcx, trait_ref.args) + .sinto(s); + ImplAssocItemValue::DefaultedTy { ty } + } + ty::AssocKind::Fn { .. } => { + ImplAssocItemValue::DefaultedFn {} + } + ty::AssocKind::Const { .. } => { + ImplAssocItemValue::DefaultedConst {} + } + } + } + }; + + ImplAssocItem { + name: decl_assoc.opt_name().sinto(s), + value, + required_impl_exprs, + decl_def_id: decl_def_id.sinto(s), + } + }) + .collect(); + assert!(item_map.is_empty()); + FullDefKind::TraitImpl { + param_env, + trait_pred, + implied_impl_exprs: required_impl_exprs, + items, + } + } + } + } + RDefKind::Fn { .. } => FullDefKind::Fn { + param_env: get_param_env(s, args), + inline: tcx.codegen_fn_attrs(def_id).inline.sinto(s), + is_const: tcx.constness(def_id) == rustc_hir::Constness::Const, + sig: inst_binder(tcx, s.typing_env(), args, tcx.fn_sig(def_id)).sinto(s), + body: get_body(s, args), + }, + RDefKind::AssocFn { .. } => FullDefKind::AssocFn { + param_env: get_param_env(s, args), + associated_item: AssocItem::sfrom_instantiated(s, &tcx.associated_item(def_id), args), + inline: tcx.codegen_fn_attrs(def_id).inline.sinto(s), + is_const: tcx.constness(def_id) == rustc_hir::Constness::Const, + sig: get_method_sig(tcx, s.typing_env(), def_id, args).sinto(s), + body: get_body(s, args), + }, + RDefKind::Closure { .. } => { + use ty::ClosureKind::{Fn, FnMut}; + let closure_ty = type_of_self(); + let ty::TyKind::Closure(_, closure_args) = closure_ty.kind() else { + unreachable!() + }; + let closure = closure_args.as_closure(); + // We lose lifetime information here. Eventually would be nice not to. + let input_ty = erase_free_regions(tcx, closure.sig().input(0).skip_binder()); + let trait_args = [closure_ty, input_ty]; + let fn_once_trait = tcx.lang_items().fn_once_trait().unwrap(); + let fn_mut_trait = tcx.lang_items().fn_mut_trait().unwrap(); + let fn_trait = tcx.lang_items().fn_trait().unwrap(); + let drop_trait = tcx.lang_items().drop_trait().unwrap(); + FullDefKind::Closure { + is_const: tcx.constness(def_id) == rustc_hir::Constness::Const, + args: ClosureArgs::sfrom(s, def_id, closure), + once_shim: get_closure_once_shim(s, closure_ty), + drop_glue: get_drop_glue_shim(s, args), + drop_impl: virtual_impl_for( + s, + ty::TraitRef::new(tcx, drop_trait, [type_of_self()]), + ), + fn_once_impl: virtual_impl_for( + s, + ty::TraitRef::new(tcx, fn_once_trait, trait_args), + ), + fn_mut_impl: matches!(closure.kind(), FnMut | Fn) + .then(|| virtual_impl_for(s, ty::TraitRef::new(tcx, fn_mut_trait, trait_args))), + fn_impl: matches!(closure.kind(), Fn) + .then(|| virtual_impl_for(s, ty::TraitRef::new(tcx, fn_trait, trait_args))), + } + } + kind @ (RDefKind::Const { .. } + | RDefKind::AnonConst { .. } + | RDefKind::InlineConst { .. }) => { + let kind = match kind { + RDefKind::Const { .. } => ConstKind::TopLevel, + RDefKind::AnonConst { .. } => ConstKind::AnonConst, + RDefKind::InlineConst { .. } => ConstKind::InlineConst, + _ => unreachable!(), + }; + FullDefKind::Const { + param_env: get_param_env(s, args), + ty: type_of_self().sinto(s), + kind, + body: get_body(s, args), + } + } + RDefKind::AssocConst { .. } => FullDefKind::AssocConst { + param_env: get_param_env(s, args), + associated_item: AssocItem::sfrom_instantiated(s, &tcx.associated_item(def_id), args), + ty: type_of_self().sinto(s), + body: get_body(s, args), + }, + RDefKind::Static { + safety, + mutability, + nested, + .. + } => FullDefKind::Static { + param_env: get_param_env(s, args), + safety: safety.sinto(s), + mutability: mutability.sinto(s), + nested: nested.sinto(s), + ty: type_of_self().sinto(s), + body: get_body(s, args), + }, + RDefKind::ExternCrate => FullDefKind::ExternCrate, + RDefKind::Use => FullDefKind::Use, + RDefKind::Mod { .. } => FullDefKind::Mod { + items: get_mod_children(tcx, def_id).sinto(s), + }, + RDefKind::ForeignMod { .. } => FullDefKind::ForeignMod { + items: get_foreign_mod_children(tcx, def_id).sinto(s), + }, + RDefKind::TyParam => FullDefKind::TyParam, + RDefKind::ConstParam => FullDefKind::ConstParam, + RDefKind::LifetimeParam => FullDefKind::LifetimeParam, + RDefKind::Variant => FullDefKind::Variant, + RDefKind::Ctor(ctor_of, _) => { + let args = args_or_default(); + let ctor_of = ctor_of.sinto(s); + + // The def_id of the adt this ctor belongs to. + let adt_def_id = match ctor_of { + CtorOf::Struct => tcx.parent(def_id), + CtorOf::Variant => tcx.parent(tcx.parent(def_id)), + }; + let adt_def = tcx.adt_def(adt_def_id); + let variant_id = adt_def.variant_index_with_ctor_id(def_id); + let fields = adt_def + .variant(variant_id) + .fields + .iter() + .map(|f| FieldDef::sfrom(s, f, args)) + .collect(); + let output_ty = ty::Ty::new_adt(tcx, adt_def, args).sinto(s); + FullDefKind::Ctor { + adt_def_id: adt_def_id.sinto(s), + ctor_of, + variant_id: variant_id.sinto(s), + fields, + output_ty, + } + } + RDefKind::Field => FullDefKind::Field, + RDefKind::Macro(kind) => FullDefKind::Macro(kind.sinto(s)), + RDefKind::GlobalAsm => FullDefKind::GlobalAsm, + RDefKind::SyntheticCoroutineBody => FullDefKind::SyntheticCoroutineBody, + } +} + /// An associated item in a trait impl. This can be an item provided by the trait impl, or an item /// that reuses the trait decl default value. #[derive_group(Serializers)] #[derive(Clone, Debug, JsonSchema)] -pub struct ImplAssocItem { +pub struct ImplAssocItem { /// This is `None` for RPTITs. pub name: Option, - /// The definition of the item from the trait declaration. This is `AssocTy`, `AssocFn` or + /// The definition of the item from the trait declaration. This is an `AssocTy`, `AssocFn` or /// `AssocConst`. - pub decl_def: Arc>, + pub decl_def_id: DefId, /// The `ImplExpr`s required to satisfy the predicates on the associated type. E.g.: /// ```ignore /// trait Foo { @@ -504,17 +784,17 @@ pub struct ImplAssocItem { /// Empty if this item is an associated const or fn. pub required_impl_exprs: Vec, /// The value of the implemented item. - pub value: ImplAssocItemValue, + pub value: ImplAssocItemValue, } #[derive_group(Serializers)] #[derive(Clone, Debug, JsonSchema)] -pub enum ImplAssocItemValue { +pub enum ImplAssocItemValue { /// The item is provided by the trait impl. Provided { - /// The definition of the item in the trait impl. This is `AssocTy`, `AssocFn` or + /// The definition of the item in the trait impl. This is an `AssocTy`, `AssocFn` or /// `AssocConst`. - def: Arc>, + def_id: DefId, /// Whether the trait had a default value for this item (which is therefore overriden). is_override: bool, }, @@ -532,9 +812,27 @@ pub enum ImplAssocItemValue { DefaultedConst, } +/// Partial data for a trait impl, used for fake trait impls that we generate ourselves such as +/// `FnOnce` and `Drop` impls. +#[derive_group(Serializers)] +#[derive(Clone, Debug, JsonSchema)] +pub struct VirtualTraitImpl { + /// The trait that is implemented by this impl block. + pub trait_pred: TraitPredicate, + /// The `ImplExpr`s required to satisfy the predicates on the trait declaration. + pub implied_impl_exprs: Vec, + /// The associated types and their predicates, in definition order. + pub types: Vec<(Ty, Vec)>, +} + impl FullDef { pub fn def_id(&self) -> &DefId { - &self.def_id + &self.this.def_id + } + + /// Reference to the item itself. + pub fn this(&self) -> &ItemRef { + &self.this } pub fn kind(&self) -> &FullDefKind { @@ -544,10 +842,8 @@ impl FullDef { /// Returns the generics and predicates for definitions that have those. pub fn param_env(&self) -> Option<&ParamEnv> { use FullDefKind::*; - match &self.kind { - Struct { param_env, .. } - | Union { param_env, .. } - | Enum { param_env, .. } + match self.kind() { + Adt { param_env, .. } | Trait { param_env, .. } | TraitAlias { param_env, .. } | TyAlias { param_env, .. } @@ -556,8 +852,6 @@ impl FullDef { | AssocFn { param_env, .. } | Const { param_env, .. } | AssocConst { param_env, .. } - | AnonConst { param_env, .. } - | InlineConst { param_env, .. } | Static { param_env, .. } | TraitImpl { param_env, .. } | InherentImpl { param_env, .. } => Some(param_env), @@ -565,6 +859,43 @@ impl FullDef { } } + /// Return the parent of this item if the item inherits the typing context from its parent. + #[cfg(feature = "rustc")] + pub fn typing_parent<'tcx>(&self, s: &impl BaseState<'tcx>) -> Option { + use FullDefKind::*; + match self.kind() { + AssocTy { .. } + | AssocFn { .. } + | AssocConst { .. } + | Const { + kind: ConstKind::AnonConst | ConstKind::InlineConst | ConstKind::PromotedConst, + .. + } => self.param_env().unwrap().parent.clone(), + Closure { .. } | Ctor { .. } | Variant { .. } => { + let parent = self.def_id().parent.as_ref().unwrap(); + // The parent has the same generics as this item. + Some(self.this().with_def_id(s, parent)) + } + _ => None, + } + } + + /// Whether the item has any generics at all (including parent generics). + pub fn has_any_generics(&self) -> bool { + match self.param_env() { + Some(p) => p.generics.parent_count != 0 || !p.generics.params.is_empty(), + None => false, + } + } + + /// Whether the item has any generics of its own (ignoring parent generics). + pub fn has_own_generics(&self) -> bool { + match self.param_env() { + Some(p) => !p.generics.params.is_empty(), + None => false, + } + } + /// Lists the children of this item that can be named, in the way of normal rust paths. For /// types, this includes inherent items. #[cfg(feature = "rustc")] @@ -576,24 +907,26 @@ impl FullDef { Some((opt_ident.as_ref()?.0.clone(), def_id.clone())) }) .collect(), - FullDefKind::Enum { def, .. } => def - .variants - .raw + FullDefKind::Adt { + adt_kind: AdtKind::Enum, + variants, + .. + } => variants .iter() .map(|variant| (variant.name.clone(), variant.def_id.clone())) .collect(), FullDefKind::InherentImpl { items, .. } | FullDefKind::Trait { items, .. } => items .iter() - .filter_map(|(item, _)| Some((item.name.clone()?, item.def_id.clone()))) + .filter_map(|item| Some((item.name.clone()?, item.def_id.clone()))) .collect(), FullDefKind::TraitImpl { items, .. } => items .iter() - .filter_map(|item| Some((item.name.clone()?, item.def().def_id.clone()))) + .filter_map(|item| Some((item.name.clone()?, item.def_id().clone()))) .collect(), _ => vec![], }; // Add inherent impl items if any. - if let Some(rust_def_id) = self.def_id.as_rust_def_id() { + if let Some(rust_def_id) = self.def_id().as_rust_def_id() { let tcx = s.base().tcx; for impl_def_id in tcx.inherent_impls(rust_def_id) { children.extend( @@ -607,399 +940,156 @@ impl FullDef { } } -impl ImplAssocItem { +impl ImplAssocItem { /// The relevant definition: the provided implementation if any, otherwise the default /// declaration from the trait declaration. - pub fn def(&self) -> &FullDef { + pub fn def_id(&self) -> &DefId { match &self.value { - ImplAssocItemValue::Provided { def, .. } => def.as_ref(), - _ => self.decl_def.as_ref(), - } - } - - /// The kind of item this is. - pub fn assoc_kind(&self) -> &AssocKind { - match self.def().kind() { - FullDefKind::AssocTy { - associated_item, .. - } - | FullDefKind::AssocFn { - associated_item, .. - } - | FullDefKind::AssocConst { - associated_item, .. - } => &associated_item.kind, - _ => unreachable!(), + ImplAssocItemValue::Provided { def_id, .. } => def_id, + _ => &self.decl_def_id, } } } -/// Gets the visibility (`pub` or not) of the definition. Returns `None` for defs that don't have a -/// meaningful visibility. #[cfg(feature = "rustc")] -fn get_def_visibility<'tcx>( - tcx: ty::TyCtxt<'tcx>, - def_id: RDefId, - def_kind: RDefKind, -) -> Option { - use RDefKind::*; - match def_kind { - AssocConst - | AssocFn - | Const - | Enum - | Field - | Fn - | ForeignTy - | Macro { .. } - | Mod - | Static { .. } - | Struct - | Trait - | TraitAlias - | TyAlias { .. } - | Union - | Use - | Variant => Some(tcx.visibility(def_id).is_public()), - // These kinds don't have visibility modifiers (which would cause `visibility` to panic). - AnonConst - | AssocTy - | Closure - | ConstParam - | Ctor { .. } - | ExternCrate - | ForeignMod - | GlobalAsm - | Impl { .. } - | InlineConst - | LifetimeParam - | OpaqueTy - | SyntheticCoroutineBody - | TyParam => None, - } -} - -/// Gets the attributes of the definition. -#[cfg(feature = "rustc")] -fn get_def_attrs<'tcx>( - tcx: ty::TyCtxt<'tcx>, - def_id: RDefId, - def_kind: RDefKind, -) -> &'tcx [rustc_hir::Attribute] { - use RDefKind::*; - match def_kind { - // These kinds cause `get_attrs_unchecked` to panic. - ConstParam | LifetimeParam | TyParam | ForeignMod => &[], - _ => tcx.get_attrs_unchecked(def_id), - } -} - -/// Gets the children of a module. -#[cfg(feature = "rustc")] -fn get_mod_children<'tcx>( - tcx: ty::TyCtxt<'tcx>, - def_id: RDefId, -) -> Vec<(Option, RDefId)> { - match def_id.as_local() { - Some(ldid) => match tcx.hir_node_by_def_id(ldid) { - rustc_hir::Node::Crate(m) - | rustc_hir::Node::Item(&rustc_hir::Item { - kind: rustc_hir::ItemKind::Mod(_, m), - .. - }) => m - .item_ids - .iter() - .map(|&item_id| { - let opt_ident = tcx.hir_item(item_id).kind.ident(); - let def_id = item_id.owner_id.to_def_id(); - (opt_ident, def_id) - }) - .collect(), - node => panic!("DefKind::Module is an unexpected node: {node:?}"), - }, - None => tcx - .module_children(def_id) - .iter() - .map(|child| (Some(child.ident), child.res.def_id())) - .collect(), - } -} - -/// Gets the children of an `extern` block. Empty if the block is not defined in the current crate. -#[cfg(feature = "rustc")] -fn get_foreign_mod_children<'tcx>(tcx: ty::TyCtxt<'tcx>, def_id: RDefId) -> Vec { - match def_id.as_local() { - Some(ldid) => tcx - .hir_node_by_def_id(ldid) - .expect_item() - .expect_foreign_mod() - .1 - .iter() - .map(|foreign_item_ref| foreign_item_ref.owner_id.to_def_id()) - .collect(), - None => vec![], - } -} - -#[cfg(feature = "rustc")] -fn get_self_predicate<'tcx, S: UnderOwnerState<'tcx>>(s: &S) -> TraitPredicate { +fn get_self_predicate<'tcx, S: UnderOwnerState<'tcx>>( + s: &S, + args: Option>, +) -> TraitPredicate { use ty::Upcast; let tcx = s.base().tcx; + let typing_env = s.typing_env(); let pred: ty::TraitPredicate = crate::traits::self_predicate(tcx, s.owner_id()) .no_bound_vars() .unwrap() .upcast(tcx); + let pred = substitute(tcx, typing_env, args, pred); pred.sinto(s) } +/// Do the trait resolution necessary to create a new impl for the given trait_ref. Used when we +/// generate fake trait impls e.g. for `FnOnce` and `Drop`. #[cfg(feature = "rustc")] -fn get_impl_contents<'tcx, S, Body>(s: &S) -> FullDefKind +fn virtual_impl_for<'tcx, S>(s: &S, trait_ref: ty::TraitRef<'tcx>) -> Box where S: UnderOwnerState<'tcx>, - Body: IsBody + TypeMappable, { - use std::collections::HashMap; let tcx = s.base().tcx; - let impl_def_id = s.owner_id(); - let param_env = get_param_env(s, impl_def_id); - match tcx.impl_subject(impl_def_id).instantiate_identity() { - ty::ImplSubject::Inherent(ty) => { - let items = tcx - .associated_items(impl_def_id) - .in_definition_order() - .map(|assoc| { - let def_id = assoc.def_id.sinto(s); - (assoc.sinto(s), def_id.full_def(s)) - }) - .collect::>(); - FullDefKind::InherentImpl { - param_env, - ty: ty.sinto(s), - items, - } - } - ty::ImplSubject::Trait(trait_ref) => { - // Also record the polarity. - let polarity = tcx.impl_polarity(impl_def_id); - let trait_pred = TraitPredicate { - trait_ref: trait_ref.sinto(s), - is_positive: matches!(polarity, ty::ImplPolarity::Positive), - }; - // Impl exprs required by the trait. - let required_impl_exprs = - solve_item_implied_traits(s, trait_ref.def_id, trait_ref.args); - - let mut item_map: HashMap = tcx - .associated_items(impl_def_id) - .in_definition_order() - .map(|assoc| (assoc.trait_item_def_id.unwrap(), assoc)) - .collect(); - let items = tcx - .associated_items(trait_ref.def_id) - .in_definition_order() - .map(|decl_assoc| { - let decl_def_id = decl_assoc.def_id; - let decl_def = decl_def_id.sinto(s).full_def(s); - // Impl exprs required by the item. - let required_impl_exprs; - let value = match item_map.remove(&decl_def_id) { - Some(impl_assoc) => { - required_impl_exprs = { - let item_args = - ty::GenericArgs::identity_for_item(tcx, impl_assoc.def_id); - // Subtlety: we have to add the GAT arguments (if any) to the trait ref arguments. - let args = item_args.rebase_onto(tcx, impl_def_id, trait_ref.args); - let state_with_id = - with_owner_id(s.base(), (), (), impl_assoc.def_id); - solve_item_implied_traits(&state_with_id, decl_def_id, args) - }; - - ImplAssocItemValue::Provided { - def: impl_assoc.def_id.sinto(s).full_def(s), - is_override: decl_assoc.defaultness(tcx).has_value(), - } - } - None => { - required_impl_exprs = if tcx.generics_of(decl_def_id).is_own_empty() { - // Non-GAT case. - let item_args = - ty::GenericArgs::identity_for_item(tcx, decl_def_id); - let args = item_args.rebase_onto(tcx, impl_def_id, trait_ref.args); - let state_with_id = with_owner_id(s.base(), (), (), impl_def_id); - solve_item_implied_traits(&state_with_id, decl_def_id, args) - } else { - // FIXME: For GATs, we need a param_env that has the arguments of - // the impl plus those of the associated type, but there's no - // def_id with that param_env. - vec![] - }; - match decl_assoc.kind { - ty::AssocKind::Type { .. } => { - let ty = tcx - .type_of(decl_def_id) - .instantiate(tcx, trait_ref.args) - .sinto(s); - ImplAssocItemValue::DefaultedTy { ty } - } - ty::AssocKind::Fn { .. } => ImplAssocItemValue::DefaultedFn {}, - ty::AssocKind::Const { .. } => { - ImplAssocItemValue::DefaultedConst {} - } - } - } - }; - - ImplAssocItem { - name: decl_assoc.opt_name().sinto(s), - value, - required_impl_exprs, - decl_def, - } - }) - .collect(); - assert!(item_map.is_empty()); - FullDefKind::TraitImpl { - param_env, - trait_pred, - implied_impl_exprs: required_impl_exprs, - items, - } - } - } + let trait_pred = TraitPredicate { + trait_ref: trait_ref.sinto(s), + is_positive: true, + }; + // Impl exprs required by the trait. + let required_impl_exprs = solve_item_implied_traits(s, trait_ref.def_id, trait_ref.args); + let types = tcx + .associated_items(trait_ref.def_id) + .in_definition_order() + .filter(|assoc| matches!(assoc.kind, ty::AssocKind::Type { .. })) + .map(|assoc| { + // This assumes non-GAT because this is for builtin-trait (that don't + // have GATs). + let ty = ty::Ty::new_projection(tcx, assoc.def_id, trait_ref.args).sinto(s); + // Impl exprs required by the type. + let required_impl_exprs = solve_item_implied_traits(s, assoc.def_id, trait_ref.args); + (ty, required_impl_exprs) + }) + .collect(); + Box::new(VirtualTraitImpl { + trait_pred, + implied_impl_exprs: required_impl_exprs, + types, + }) } -/// The signature of a method impl may be a subtype of the one expected from the trait decl, as in -/// the example below. For correctness, we must be able to map from the method generics declared in -/// the trait to the actual method generics. Because this would require type inference, we instead -/// simply return the declared signature. This will cause issues if it is possible to use such a -/// more-specific implementation with its more-specific type, but we have a few other issues with -/// lifetime-generic function pointers anyway so this is unlikely to cause problems. -/// -/// ```ignore -/// trait MyCompare: Sized { -/// fn compare(self, other: Other) -> bool; -/// } -/// impl<'a> MyCompare<&'a ()> for &'a () { -/// // This implementation is more general because it works for non-`'a` refs. Note that only -/// // late-bound vars may differ in this way. -/// // `<&'a () as MyCompare<&'a ()>>::compare` has type `fn<'b>(&'a (), &'b ()) -> bool`, -/// // but type `fn(&'a (), &'a ()) -> bool` was expected from the trait declaration. -/// fn compare<'b>(self, _other: &'b ()) -> bool { -/// true -/// } -/// } -/// ``` #[cfg(feature = "rustc")] -fn get_method_sig<'tcx, S>(s: &S) -> ty::PolyFnSig<'tcx> +fn get_body<'tcx, S, Body>(s: &S, args: Option>) -> Option where S: UnderOwnerState<'tcx>, + Body: IsBody + TypeMappable, { - let tcx = s.base().tcx; let def_id = s.owner_id(); - let real_sig = tcx.fn_sig(def_id).instantiate_identity(); - let item = tcx.associated_item(def_id); - if !matches!(item.container, ty::AssocItemContainer::Impl) { - return real_sig; - } - let Some(decl_method_id) = item.trait_item_def_id else { - return real_sig; - }; - let declared_sig = tcx.fn_sig(decl_method_id); - - // TODO(Nadrieril): Temporary hack: if the signatures have the same number of bound vars, we - // keep the real signature. While the declared signature is more correct, it is also less - // normalized and we can't normalize without erasing regions but regions are crucial in - // function signatures. Hence we cheat here, until charon gains proper normalization - // capabilities. - if declared_sig.skip_binder().bound_vars().len() == real_sig.bound_vars().len() { - return real_sig; - } - - let impl_def_id = item.container_id(tcx); - // The trait predicate that is implemented by the surrounding impl block. - let implemented_trait_ref = tcx - .impl_trait_ref(impl_def_id) - .unwrap() - .instantiate_identity(); - // Construct arguments for the declared method generics in the context of the implemented - // method generics. - let impl_args = ty::GenericArgs::identity_for_item(tcx, def_id); - let decl_args = impl_args.rebase_onto(tcx, impl_def_id, implemented_trait_ref.args); - let sig = declared_sig.instantiate(tcx, decl_args); - // Avoids accidentally using the same lifetime name twice in the same scope - // (once in impl parameters, second in the method declaration late-bound vars). - let sig = tcx.anonymize_bound_vars(sig); - sig + Body::body(s, def_id, args) } #[cfg(feature = "rustc")] -fn get_ctor_contents<'tcx, S, Body>(s: &S, ctor_of: CtorOf) -> FullDefKind +fn get_closure_once_shim<'tcx, S, Body>(s: &S, closure_ty: ty::Ty<'tcx>) -> Option where S: UnderOwnerState<'tcx>, Body: IsBody + TypeMappable, { let tcx = s.base().tcx; - let def_id = s.owner_id(); - - // The def_id of the adt this ctor belongs to. - let adt_def_id = match ctor_of { - CtorOf::Struct => tcx.parent(def_id), - CtorOf::Variant => tcx.parent(tcx.parent(def_id)), - }; - let adt_def = tcx.adt_def(adt_def_id); - let variant_id = adt_def.variant_index_with_ctor_id(def_id); - let fields = adt_def.variant(variant_id).fields.sinto(s); - let generic_args = ty::GenericArgs::identity_for_item(tcx, adt_def_id); - let output_ty = ty::Ty::new_adt(tcx, adt_def, generic_args).sinto(s); - FullDefKind::Ctor { - adt_def_id: adt_def_id.sinto(s), - ctor_of, - variant_id: variant_id.sinto(s), - fields, - output_ty, - } + let mir = crate::closure_once_shim(tcx, closure_ty)?; + let body = Body::from_mir(s, mir)?; + Some(body) } #[cfg(feature = "rustc")] -fn closure_once_shim<'tcx>( - tcx: ty::TyCtxt<'tcx>, - closure_ty: ty::Ty<'tcx>, -) -> Option> { - let ty::Closure(def_id, args) = closure_ty.kind() else { - unreachable!() - }; - let instance = match args.as_closure().kind() { - ty::ClosureKind::Fn | ty::ClosureKind::FnMut => { - ty::Instance::fn_once_adapter_instance(tcx, *def_id, args) - } - ty::ClosureKind::FnOnce => return None, - }; - let mir = tcx.instance_mir(instance.def).clone(); - let mir = ty::EarlyBinder::bind(mir).instantiate(tcx, instance.args); - Some(mir) +fn get_drop_glue_shim<'tcx, S, Body>(s: &S, args: Option>) -> Option +where + S: UnderOwnerState<'tcx>, + Body: IsBody + TypeMappable, +{ + let tcx = s.base().tcx; + let mir = crate::drop_glue_shim(tcx, s.owner_id(), args)?; + let body = Body::from_mir(s, mir)?; + Some(body) } #[cfg(feature = "rustc")] -fn drop_glue_shim<'tcx>(tcx: ty::TyCtxt<'tcx>, def_id: RDefId) -> Option> { - let drop_in_place = - tcx.require_lang_item(rustc_hir::LangItem::DropInPlace, rustc_span::DUMMY_SP); - if !tcx.generics_of(def_id).is_empty() { - // Hack: layout code panics if it can't fully normalize types, which can happen e.g. with a - // trait associated type. For now we only translate the glue for monomorphic types. - return None; +fn get_param_env<'tcx, S: UnderOwnerState<'tcx>>( + s: &S, + args: Option>, +) -> ParamEnv { + let tcx = s.base().tcx; + let def_id = s.owner_id(); + let generics = tcx.generics_of(def_id).sinto(s); + + let parent = generics.parent.as_ref().map(|parent| { + let parent = parent.underlying_rust_def_id(); + let args = args.unwrap_or_else(|| ty::GenericArgs::identity_for_item(tcx, def_id)); + let parent_args = args.truncate_to(tcx, tcx.generics_of(parent)); + translate_item_ref(s, parent, parent_args) + }); + match args { + None => ParamEnv { + generics, + predicates: required_predicates(tcx, def_id, s.base().options.bounds_options).sinto(s), + parent, + }, + // An instantiated item is monomorphic. + Some(_) => ParamEnv { + generics: TyGenerics { + parent_count: 0, + params: Default::default(), + ..generics + }, + predicates: GenericPredicates::default(), + parent, + }, } - let ty = tcx.type_of(def_id).instantiate_identity(); - let instance_kind = ty::InstanceKind::DropGlue(drop_in_place, Some(ty)); - let mir = tcx.instance_mir(instance_kind).clone(); - Some(mir) } #[cfg(feature = "rustc")] -fn get_param_env<'tcx, S: BaseState<'tcx>>(s: &S, def_id: RDefId) -> ParamEnv { +fn get_implied_predicates<'tcx, S: UnderOwnerState<'tcx>>( + s: &S, + args: Option>, +) -> GenericPredicates { + use std::borrow::Cow; let tcx = s.base().tcx; - let s = &with_owner_id(s.base(), (), (), def_id); - ParamEnv { - generics: tcx.generics_of(def_id).sinto(s), - predicates: required_predicates(tcx, def_id, s.base().options.bounds_options).sinto(s), + let def_id = s.owner_id(); + let typing_env = s.typing_env(); + let mut implied_predicates = implied_predicates(tcx, def_id, s.base().options.bounds_options); + if args.is_some() { + implied_predicates = Cow::Owned( + implied_predicates + .iter() + .copied() + .map(|(clause, span)| { + let clause = substitute(tcx, typing_env, args, clause); + (clause, span) + }) + .collect(), + ); } + implied_predicates.sinto(s) } diff --git a/frontend/exporter/src/types/thir.rs b/frontend/exporter/src/types/thir.rs index ceb61cdf4..c4435b1ff 100644 --- a/frontend/exporter/src/types/thir.rs +++ b/frontend/exporter/src/types/thir.rs @@ -190,6 +190,7 @@ impl<'tcx, S: ExprState<'tcx>> SInto for thir::StmtId { #[cfg(feature = "rustc")] impl<'tcx, S: ExprState<'tcx>> SInto for thir::Expr<'tcx> { fn sinto(&self, s: &S) -> Expr { + let s = &s.with_ty(self.ty); let (hir_id, attributes) = self.hir_id_and_attributes(s); let hir_id = hir_id.map(|hir_id| hir_id.index()); let unrolled = self.unroll_scope(s); @@ -589,7 +590,7 @@ pub type Expr = Decorated; /// Reflects [`thir::ExprKind`] #[derive(AdtInto)] -#[args(<'tcx, S: ExprState<'tcx>>, from: thir::ExprKind<'tcx>, state: S as gstate)] +#[args(<'tcx, S: ExprState<'tcx> + HasTy<'tcx>>, from: thir::ExprKind<'tcx>, state: S as gstate)] #[derive_group(Serializers)] #[derive(Clone, Debug, JsonSchema)] #[append( @@ -704,6 +705,17 @@ pub enum ExprKind { NeverToAny { source: Expr, }, + #[custom_arm( + &FROM_TYPE::PointerCoercion { cast, source, .. } => { + let source = &gstate.thir().exprs[source]; + let src_ty = source.ty; + let tgt_ty = gstate.ty(); + TO_TYPE::PointerCoercion { + cast: PointerCoercion::sfrom(gstate, cast, src_ty, tgt_ty), + source: source.sinto(gstate), + } + }, + )] PointerCoercion { cast: PointerCoercion, source: Expr, @@ -804,7 +816,7 @@ pub enum ExprKind { }, #[custom_arm(FROM_TYPE::Closure(e) => { let (thir, expr_entrypoint) = get_thir(e.closure_id, gstate); - let s = &State::from_thir(gstate.base(), gstate.owner_id(), thir.clone()); + let s = &gstate.with_thir(thir.clone()); TO_TYPE::Closure { params: thir.params.raw.sinto(s), body: expr_entrypoint.sinto(s), @@ -858,7 +870,7 @@ pub trait ExprKindExt<'tcx> { &self, s: &S, ) -> (Option, Vec); - fn unroll_scope + HasThir<'tcx>>(&self, s: &S) -> thir::Expr<'tcx>; + fn unroll_scope + HasThir<'tcx>>(&self, s: &S) -> thir::Expr<'tcx>; } #[cfg(feature = "rustc")] @@ -875,7 +887,7 @@ impl<'tcx> ExprKindExt<'tcx> for thir::Expr<'tcx> { _ => (None, vec![]), } } - fn unroll_scope + HasThir<'tcx>>(&self, s: &S) -> thir::Expr<'tcx> { + fn unroll_scope + HasThir<'tcx>>(&self, s: &S) -> thir::Expr<'tcx> { // TODO: when we see a loop, we should lookup its label! label is actually a scope id // we remove scopes here, whence the TODO match self.kind { diff --git a/frontend/exporter/src/types/ty.rs b/frontend/exporter/src/types/ty.rs index bf71fb173..8b4c87f9f 100644 --- a/frontend/exporter/src/types/ty.rs +++ b/frontend/exporter/src/types/ty.rs @@ -305,15 +305,16 @@ pub struct FieldDef { } #[cfg(feature = "rustc")] -impl<'tcx, S: UnderOwnerState<'tcx>> SInto for ty::FieldDef { - fn sinto(&self, s: &S) -> FieldDef { +impl FieldDef { + pub fn sfrom<'tcx, S: UnderOwnerState<'tcx>>( + s: &S, + fdef: &ty::FieldDef, + instantiate: ty::GenericArgsRef<'tcx>, + ) -> FieldDef { let tcx = s.base().tcx; - let ty = { - let generics = ty::GenericArgs::identity_for_item(tcx, self.did); - self.ty(tcx, generics).sinto(s) - }; + let ty = fdef.ty(tcx, instantiate).sinto(s); let name = { - let name = self.name.sinto(s); + let name = fdef.name.sinto(s); let is_user_provided = { // SH: Note that the only way I found of checking if the user wrote the name or if it // is just an integer generated by rustc is by checking if it is just made of @@ -324,11 +325,11 @@ impl<'tcx, S: UnderOwnerState<'tcx>> SInto for ty::FieldDef { }; FieldDef { - did: self.did.sinto(s), + did: fdef.did.sinto(s), name, - vis: self.vis.sinto(s), + vis: fdef.vis.sinto(s), ty, - span: tcx.def_span(self.did).sinto(s), + span: tcx.def_span(fdef.did).sinto(s), } } } @@ -352,18 +353,26 @@ pub struct VariantDef { #[cfg(feature = "rustc")] impl VariantDef { - fn sfrom<'tcx, S: UnderOwnerState<'tcx>>( + pub(crate) fn sfrom<'tcx, S: UnderOwnerState<'tcx>>( s: &S, def: &ty::VariantDef, discr_val: ty::util::Discr<'tcx>, + instantiate: Option>, ) -> Self { + let tcx = s.base().tcx; + let instantiate = + instantiate.unwrap_or_else(|| ty::GenericArgs::identity_for_item(tcx, def.def_id)); VariantDef { def_id: def.def_id.sinto(s), ctor: def.ctor.sinto(s), name: def.name.sinto(s), discr_def: def.discr.sinto(s), discr_val: discr_val.sinto(s), - fields: def.fields.sinto(s), + fields: def + .fields + .iter() + .map(|f| FieldDef::sfrom(s, f, instantiate)) + .collect(), span: s.base().tcx.def_span(def.def_id).sinto(s), } } @@ -453,6 +462,8 @@ pub struct ItemRefContents { /// If we're referring to a trait associated item, this gives the trait clause/impl we're /// referring to. pub in_trait: Option, + /// Whether this contains any reference to a type/lifetime/const parameter. + pub has_param: bool, } /// Reference to an item, with generics. Basically any mention of an item (function, type, etc) @@ -495,7 +506,7 @@ pub struct ItemRef { impl ItemRefContents { #[cfg(feature = "rustc")] - pub fn intern<'tcx, S: BaseState<'tcx>>(self, s: &S) -> ItemRef { + fn intern<'tcx, S: BaseState<'tcx>>(self, s: &S) -> ItemRef { s.with_global_cache(|cache| { let table_session = &mut cache.id_table_session; let contents = id_table::Node::new(self, table_session); @@ -505,36 +516,134 @@ impl ItemRefContents { } impl ItemRef { + /// The main way to obtain an `ItemRef`: from a `def_id` and generics. #[cfg(feature = "rustc")] - pub fn new<'tcx, S: BaseState<'tcx>>( + pub fn translate<'tcx, S: UnderOwnerState<'tcx>>( s: &S, - def_id: DefId, - generic_args: Vec, - impl_exprs: Vec, - in_trait: Option, + def_id: RDefId, + generics: ty::GenericArgsRef<'tcx>, ) -> ItemRef { - let contents = ItemRefContents { - def_id, + use rustc_infer::infer::canonical::ir::TypeVisitableExt; + let key = (def_id, generics); + if let Some(item) = s.with_cache(|cache| cache.item_refs.get(&key).cloned()) { + return item; + } + let mut impl_exprs = solve_item_required_traits(s, def_id, generics); + let mut generic_args = generics.sinto(s); + + // If this is an associated item, resolve the trait reference. + let trait_info = self_clause_for_item(s, def_id, generics); + // Fixup the generics. + if let Some(tinfo) = &trait_info { + // The generics are split in two: the arguments of the trait and the arguments of the + // method. + // + // For instance, if we have: + // ``` + // trait Foo { + // fn baz(...) { ... } + // } + // + // fn test(x: T) { + // x.baz(...); + // ... + // } + // ``` + // The generics for the call to `baz` will be the concatenation: ``, which we + // split into `` and ``. + let trait_ref = tinfo.r#trait.hax_skip_binder_ref(); + let num_trait_generics = trait_ref.generic_args.len(); + generic_args.drain(0..num_trait_generics); + let num_trait_trait_clauses = trait_ref.impl_exprs.len(); + // Associated items take a `Self` clause as first clause, we skip that one too. Note: that + // clause is the same as `tinfo`. + impl_exprs.drain(0..num_trait_trait_clauses + 1); + } + + let content = ItemRefContents { + def_id: def_id.sinto(s), generic_args, impl_exprs, - in_trait, + in_trait: trait_info, + has_param: generics.has_param() + || generics.has_escaping_bound_vars() + || generics.has_free_regions(), + }; + let item = content.intern(s); + s.with_cache(|cache| { + cache.item_refs.insert(key, item.clone()); + }); + s.with_global_cache(|cache| { + cache.reverse_item_refs_map.insert(item.id(), generics); + }); + item + } + + /// Construct an `ItemRef` for items that can't have generics (e.g. modules). + #[cfg(feature = "rustc")] + pub fn dummy_without_generics<'tcx, S: BaseState<'tcx>>(s: &S, def_id: DefId) -> ItemRef { + let content = ItemRefContents { + def_id, + generic_args: Default::default(), + impl_exprs: Default::default(), + in_trait: Default::default(), + has_param: false, }; - contents.intern(s) + let item = content.intern(s); + s.with_global_cache(|cache| { + cache + .reverse_item_refs_map + .insert(item.id(), ty::GenericArgsRef::default()); + }); + item + } + + /// Erase lifetimes from the generic arguments of this item. + #[cfg(feature = "rustc")] + pub fn erase<'tcx, S: UnderOwnerState<'tcx>>(&self, s: &S) -> Self { + let def_id = self.def_id.underlying_rust_def_id(); + let args = self.rustc_args(s); + let args = erase_and_norm(s.base().tcx, s.typing_env(), args); + Self::translate(s, def_id, args).with_def_id(s, &self.def_id) } pub fn contents(&self) -> &ItemRefContents { &self.contents } + /// Get a unique id identitying this `ItemRef`. + pub fn id(&self) -> id_table::Id { + self.contents.id() + } + + /// Recover the original rustc args that generated this `ItemRef`. Will panic if the `ItemRef` + /// was built by hand instead of using `translate_item_ref`. #[cfg(feature = "rustc")] - pub fn mutate<'tcx, S: BaseState<'tcx>>( - &mut self, + pub fn rustc_args<'tcx, S: BaseState<'tcx>>(&self, s: &S) -> ty::GenericArgsRef<'tcx> { + s.with_global_cache(|cache| *cache.reverse_item_refs_map.get(&self.id()).unwrap()) + } + + /// Mutate the `DefId`, keeping the same generic args. + #[cfg(feature = "rustc")] + pub fn mutate_def_id<'tcx, S: BaseState<'tcx>>( + &self, s: &S, - f: impl FnOnce(&mut ItemRefContents), - ) { + f: impl FnOnce(&mut DefId), + ) -> Self { + let args = self.rustc_args(s); let mut contents = self.contents().clone(); - f(&mut contents); - *self = contents.intern(s); + f(&mut contents.def_id); + let new = contents.intern(s); + s.with_global_cache(|cache| { + cache.reverse_item_refs_map.insert(new.id(), args); + }); + new + } + + /// Set the `DefId`, keeping the same generic args. + #[cfg(feature = "rustc")] + pub fn with_def_id<'tcx, S: BaseState<'tcx>>(&self, s: &S, def_id: &DefId) -> Self { + self.mutate_def_id(s, |d| *d = def_id.clone()) } } @@ -713,6 +822,18 @@ impl> SInto> for ty::List { } } +/// Reflects [`ty::Variance`] +#[derive(AdtInto)] +#[args(, from: ty::Variance, state: S as _s)] +#[derive_group(Serializers)] +#[derive(Clone, Debug, JsonSchema)] +pub enum Variance { + Covariant, + Invariant, + Contravariant, + Bivariant, +} + /// Reflects [`ty::GenericParamDef`] #[derive(AdtInto)] #[args(<'tcx, S: UnderOwnerState<'tcx>>, from: ty::GenericParamDef, state: S as s)] @@ -734,6 +855,19 @@ pub struct GenericParamDef { } )] pub kind: GenericParamDefKind, + /// Variance of this type parameter, if sensible. + #[value({ + use rustc_hir::def::DefKind::*; + let tcx = s.base().tcx; + let parent = tcx.parent(self.def_id); + match tcx.def_kind(parent) { + Fn | AssocFn | Enum | Struct | Union | Ctor(..) | OpaqueTy => { + Some(tcx.variances_of(parent)[self.index as usize].sinto(s)) + } + _ => None + } + })] + pub variance: Option, } /// Reflects [`ty::GenericParamDefKind`] @@ -808,7 +942,17 @@ impl Alias { alias_ty: &ty::AliasTy<'tcx>, ) -> TyKind { let tcx = s.base().tcx; + let typing_env = s.typing_env(); use rustc_type_ir::AliasTyKind as RustAliasKind; + + // Try to normalize the alias first. + let ty = ty::Ty::new_alias(tcx, *alias_kind, *alias_ty); + let ty = crate::traits::normalize(tcx, typing_env, ty); + let ty::Alias(alias_kind, alias_ty) = ty.kind() else { + let ty: Ty = ty.sinto(s); + return ty.kind().clone(); + }; + let kind = match alias_kind { RustAliasKind::Projection => { let trait_ref = alias_ty.trait_ref(tcx); @@ -824,9 +968,10 @@ impl Alias { // yet we dont have a binder around (could even be several). Binding this correctly // is therefore difficult. Since our trait resolution ignores lifetimes anyway, we // just erase them. See also https://github.com/hacspec/hax/issues/747. - let trait_ref = crate::traits::erase_and_norm(tcx, s.typing_env(), trait_ref); + let trait_ref = crate::traits::erase_free_regions(tcx, trait_ref); + let item = tcx.associated_item(alias_ty.def_id); AliasKind::Projection { - assoc_item: tcx.associated_item(alias_ty.def_id).sinto(s), + assoc_item: AssocItem::sfrom(s, &item), impl_expr: solve_trait(s, ty::Binder::dummy(trait_ref)), } } @@ -948,14 +1093,22 @@ pub enum TyKind { #[custom_arm(FROM_TYPE::Adt(adt_def, generics) => TO_TYPE::Adt(translate_item_ref(s, adt_def.did(), generics)),)] Adt(ItemRef), - #[custom_arm(FROM_TYPE::Foreign(def_id) => TO_TYPE::Foreign(ItemRef::new(s, def_id.sinto(s), vec![], vec![], None)),)] + #[custom_arm(FROM_TYPE::Foreign(def_id) => TO_TYPE::Foreign(translate_item_ref(s, *def_id, Default::default())),)] Foreign(ItemRef), Str, Array(Box, #[map(Box::new(x.sinto(s)))] Box), Slice(Box), RawPtr(Box, Mutability), Ref(Region, Box, Mutability), - Dynamic(Vec>, Region, DynKind), + #[custom_arm(FROM_TYPE::Dynamic(preds, region, _) => make_dyn(s, preds, region),)] + Dynamic( + /// Fresh type parameter that we use as the `Self` type in the prediates below. + ParamTy, + /// Clauses that define the trait object. These clauses use the fresh type parameter above + /// as `Self` type. + GenericPredicates, + Region, + ), #[custom_arm(FROM_TYPE::Coroutine(def_id, generics) => TO_TYPE::Coroutine(translate_item_ref(s, *def_id, generics)),)] Coroutine(ItemRef), Never, @@ -972,16 +1125,96 @@ pub enum TyKind { Todo(String), } -/// Reflects [`ty::Variance`] -#[derive(AdtInto)] -#[args(, from: ty::Variance, state: S as _s)] -#[derive_group(Serializers)] -#[derive(Clone, Debug, JsonSchema)] -pub enum Variance { - Covariant, - Invariant, - Contravariant, - Bivariant, +/// Transform existential predicates into properly resolved predicates. +#[cfg(feature = "rustc")] +fn make_dyn<'tcx, S: UnderOwnerState<'tcx>>( + s: &S, + epreds: &'tcx ty::List>>, + region: &ty::Region<'tcx>, +) -> TyKind { + let tcx = s.base().tcx; + let def_id = s.owner_id(); + let span = rustc_span::DUMMY_SP.sinto(s); + + // Pretend there's an extra type in the environment. + let new_param_ty = { + let generics = tcx.generics_of(def_id); + let param_count = generics.parent_count + generics.own_params.len(); + ty::ParamTy::new(param_count as u32 + 1, rustc_span::Symbol::intern("_dyn")) + }; + let new_ty = new_param_ty.to_ty(tcx); + + // Set the new type as the `Self` parameter of our predicates. + let clauses: Vec> = epreds + .iter() + .map(|epred| epred.with_self_ty(tcx, new_ty)) + .collect(); + + // Populate a predicate searcher that knows about the `dyn` clauses. + let mut predicate_searcher = s.with_predicate_searcher(|ps| ps.clone()); + predicate_searcher + .insert_bound_predicates(clauses.iter().filter_map(|clause| clause.as_trait_clause())); + predicate_searcher.set_param_env(rustc_trait_selection::traits::normalize_param_env_or_error( + tcx, + ty::ParamEnv::new( + tcx.mk_clauses_from_iter( + s.param_env() + .caller_bounds() + .iter() + .chain(clauses.iter().copied()), + ), + ), + rustc_trait_selection::traits::ObligationCause::dummy(), + )); + + // Using the predicate searcher, translate the predicates. Only the projection predicates need + // to be handled specially. + let predicates = clauses + .into_iter() + .map(|clause| { + let clause = match clause.as_projection_clause() { + // Translate normally + None => clause.sinto(s), + // Translate by hand using our predicate searcher. This does the same as + // `clause.sinto(s)` except that it uses our predicate searcher to resolve the + // projection `ImplExpr`. + Some(proj) => { + let bound_vars = proj.bound_vars().sinto(s); + let proj = { + let alias_ty = &proj.skip_binder().projection_term.expect_ty(tcx); + let impl_expr = { + let poly_trait_ref = proj.rebind(alias_ty.trait_ref(tcx)); + predicate_searcher + .resolve(&poly_trait_ref, &|_| {}) + .s_unwrap(s) + .sinto(s) + }; + let Term::Ty(ty) = proj.skip_binder().term.sinto(s) else { + unreachable!() + }; + let item = tcx.associated_item(alias_ty.def_id); + ProjectionPredicate { + impl_expr, + assoc_item: AssocItem::sfrom(s, &item), + ty, + } + }; + let kind = Binder { + value: ClauseKind::Projection(proj), + bound_vars, + }; + let id = kind.clone().map(PredicateKind::Clause).predicate_id(); + Clause { kind, id } + } + }; + (clause, span.clone()) + }) + .collect(); + + let predicates = GenericPredicates { predicates }; + let param_ty = new_param_ty.sinto(s); + let region = region.sinto(s); + TyKind::Dynamic(param_ty, predicates, region) } /// Reflects [`ty::CanonicalUserTypeAnnotation`] @@ -1005,19 +1238,6 @@ pub enum AdtKind { Enum, } -// This comes from MIR -// TODO: add the generics and the predicates -/// Reflects [`ty::AdtDef`] -#[derive_group(Serializers)] -#[derive(Clone, Debug, JsonSchema)] -pub struct AdtDef { - pub did: DefId, - pub adt_kind: AdtKind, - pub variants: IndexVec, - pub flags: AdtFlags, - pub repr: ReprOptions, -} - sinto_todo!(rustc_middle::ty, AdtFlags); /// Reflects [`ty::ReprOptions`] @@ -1041,39 +1261,7 @@ sinto_todo!(rustc_abi, IntegerType); sinto_todo!(rustc_abi, ReprFlags); sinto_todo!(rustc_abi, Align); -#[cfg(feature = "rustc")] -impl<'tcx, S: UnderOwnerState<'tcx>> SInto for ty::AdtDef<'tcx> { - fn sinto(&self, s: &S) -> AdtDef { - let variants = self - .variants() - .iter_enumerated() - .map(|(variant_idx, variant)| { - let discr = if self.is_enum() { - self.discriminant_for_variant(s.base().tcx, variant_idx) - } else { - // Structs and unions have a single variant. - assert_eq!(variant_idx.index(), 0); - ty::util::Discr { - val: 0, - ty: s.base().tcx.types.isize, - } - }; - VariantDef::sfrom(s, variant, discr) - }) - .collect(); - AdtDef { - did: self.did().sinto(s), - adt_kind: self.adt_kind().sinto(s), - variants, - flags: self.flags().sinto(s), - repr: self.repr().sinto(s), - } - } -} - /// Reflects [`ty::adjustment::PointerCoercion`] -#[derive(AdtInto)] -#[args(, from: ty::adjustment::PointerCoercion, state: S as gstate)] #[derive_group(Serializers)] #[derive(Clone, Debug, JsonSchema)] pub enum PointerCoercion { @@ -1082,7 +1270,73 @@ pub enum PointerCoercion { ClosureFnPointer(Safety), MutToConstPointer, ArrayToPointer, - Unsize, + Unsize(UnsizingMetadata), +} + +/// The metadata to attach to the newly-unsized ptr. +#[derive_group(Serializers)] +#[derive(Clone, Debug, JsonSchema)] +pub enum UnsizingMetadata { + Length(ConstantExpr), + VTablePtr(ImplExpr), + Unknown, +} + +#[cfg(feature = "rustc")] +impl PointerCoercion { + pub fn sfrom<'tcx, S: UnderOwnerState<'tcx>>( + s: &S, + coercion: ty::adjustment::PointerCoercion, + src_ty: ty::Ty<'tcx>, + tgt_ty: ty::Ty<'tcx>, + ) -> PointerCoercion { + match coercion { + ty::adjustment::PointerCoercion::ReifyFnPointer => PointerCoercion::ReifyFnPointer, + ty::adjustment::PointerCoercion::UnsafeFnPointer => PointerCoercion::UnsafeFnPointer, + ty::adjustment::PointerCoercion::ClosureFnPointer(x) => { + PointerCoercion::ClosureFnPointer(x.sinto(s)) + } + ty::adjustment::PointerCoercion::MutToConstPointer => { + PointerCoercion::MutToConstPointer + } + ty::adjustment::PointerCoercion::ArrayToPointer => PointerCoercion::ArrayToPointer, + ty::adjustment::PointerCoercion::Unsize => { + // We only support unsizing behind references, pointers and boxes for now. + let meta = match (src_ty.builtin_deref(true), tgt_ty.builtin_deref(true)) { + (Some(src_ty), Some(tgt_ty)) => { + let tcx = s.base().tcx; + let typing_env = s.typing_env(); + let (src_ty, tgt_ty) = + tcx.struct_lockstep_tails_raw(src_ty, tgt_ty, |ty| { + normalize(tcx, typing_env, ty) + }); + match tgt_ty.kind() { + ty::Slice(_) | ty::Str => match src_ty.kind() { + ty::Array(_, len) => { + let len = len.sinto(s); + UnsizingMetadata::Length(len) + } + _ => UnsizingMetadata::Unknown, + }, + ty::Dynamic(preds, ..) => { + let pred = preds[0].with_self_ty(tcx, src_ty); + let clause = pred.as_trait_clause().expect( + "the first `ExistentialPredicate` of `TyKind::Dynamic` \ + should be a trait clause", + ); + let tref = clause.rebind(clause.skip_binder().trait_ref); + let impl_expr = solve_trait(s, tref); + UnsizingMetadata::VTablePtr(impl_expr) + } + _ => UnsizingMetadata::Unknown, + } + } + _ => UnsizingMetadata::Unknown, + }; + PointerCoercion::Unsize(meta) + } + } + } } /// Reflects [`ty::FnSig`] @@ -1207,9 +1461,10 @@ impl<'tcx, S: UnderBinderState<'tcx>> SInto let Term::Ty(ty) = self.term.sinto(s) else { unreachable!() }; + let item = tcx.associated_item(alias_ty.def_id); ProjectionPredicate { impl_expr: solve_trait(s, poly_trait_ref), - assoc_item: tcx.associated_item(alias_ty.def_id).sinto(s), + assoc_item: AssocItem::sfrom(s, &item), ty, } } @@ -1333,12 +1588,23 @@ impl Binder { #[derive(AdtInto)] #[args(<'tcx, S: UnderOwnerState<'tcx>>, from: ty::GenericPredicates<'tcx>, state: S as s)] #[derive_group(Serializers)] -#[derive(Clone, Debug, JsonSchema, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Clone, Debug, Default, JsonSchema, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct GenericPredicates { #[value(self.predicates.iter().map(|x| x.sinto(s)).collect())] pub predicates: Vec<(Clause, Span)>, } +#[cfg(feature = "rustc")] +impl<'tcx, S: UnderOwnerState<'tcx>> SInto + for crate::traits::Predicates<'tcx> +{ + fn sinto(&self, s: &S) -> GenericPredicates { + GenericPredicates { + predicates: self.as_ref().sinto(s), + } + } +} + #[cfg(feature = "rustc")] impl<'tcx, S: UnderOwnerState<'tcx>, T1, T2> SInto> for ty::Binder<'tcx, T1> where @@ -1347,13 +1613,7 @@ where fn sinto(&self, s: &S) -> Binder { let bound_vars = self.bound_vars().sinto(s); let value = { - let under_binder_s = &State { - base: s.base(), - owner_id: s.owner_id(), - binder: self.as_ref().map_bound(|_| ()), - thir: (), - mir: (), - }; + let under_binder_s = &s.with_binder(self.as_ref().map_bound(|_| ())); self.as_ref().skip_binder().sinto(under_binder_s) }; Binder { value, bound_vars } @@ -1465,24 +1725,79 @@ pub enum PredicateKind { } /// Reflects [`ty::AssocItem`] -#[derive(AdtInto)] -#[args(<'tcx, S: BaseState<'tcx>>, from: ty::AssocItem, state: S as s)] #[derive_group(Serializers)] #[derive(Clone, Debug, JsonSchema, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct AssocItem { pub def_id: DefId, /// This is `None` for RPTITs. - #[value(self.opt_name().sinto(s))] pub name: Option, pub kind: AssocKind, - #[value(get_container_for_assoc_item(s, self))] pub container: AssocItemContainer, /// Whether this item has a value (e.g. this is `false` for trait methods without default /// implementations). - #[value(self.defaultness(s.base().tcx).has_value())] pub has_value: bool, } +#[cfg(feature = "rustc")] +impl AssocItem { + pub fn sfrom<'tcx, S: BaseState<'tcx>>(s: &S, item: &ty::AssocItem) -> AssocItem { + Self::sfrom_instantiated(s, item, None) + } + + /// Translate an `AssocItem` and optionally instantiate it with the provided arguments. + pub fn sfrom_instantiated<'tcx, S: BaseState<'tcx>>( + s: &S, + item: &ty::AssocItem, + item_args: Option>, + ) -> AssocItem { + let tcx = s.base().tcx; + // We want to solve traits in the context of this item. + let s = &s.with_owner_id(item.def_id); + let item_args = + item_args.unwrap_or_else(|| ty::GenericArgs::identity_for_item(tcx, item.def_id)); + let container_id = item.container_id(tcx); + let container_args = item_args.truncate_to(tcx, tcx.generics_of(container_id)); + let container = match item.container { + ty::AssocItemContainer::Trait => { + let trait_ref = + ty::TraitRef::new_from_args(tcx, container_id, container_args).sinto(s); + AssocItemContainer::TraitContainer { trait_ref } + } + ty::AssocItemContainer::Impl => { + if let Some(implemented_item_id) = item.trait_item_def_id { + let item = translate_item_ref(s, container_id, container_args); + let implemented_trait_ref = tcx + .impl_trait_ref(container_id) + .unwrap() + .instantiate(tcx, container_args); + let implemented_trait_item = translate_item_ref( + s, + implemented_item_id, + item_args.rebase_onto(tcx, container_id, implemented_trait_ref.args), + ); + AssocItemContainer::TraitImplContainer { + impl_: item, + implemented_trait_ref: implemented_trait_ref.sinto(s), + implemented_trait_item, + overrides_default: tcx.defaultness(implemented_item_id).has_value(), + } + } else { + AssocItemContainer::InherentImplContainer { + impl_id: container_id.sinto(s), + } + } + } + }; + AssocItem { + def_id: item.def_id.sinto(s), + name: item.opt_name().sinto(s), + kind: item.kind.sinto(s), + container, + has_value: item.defaultness(tcx).has_value(), + } + } +} + /// Reflects [`ty::AssocKind`] #[derive(AdtInto)] #[args(<'tcx, S: BaseState<'tcx>>, from: ty::AssocKind, state: S as _tcx)] @@ -1515,8 +1830,8 @@ pub enum AssocItemContainer { impl_: ItemRef, /// The trait ref implemented by the impl block. implemented_trait_ref: TraitRef, - /// The def_id of the associated item (in the trait declaration) that is being implemented. - implemented_trait_item: DefId, + /// The the associated item (in the trait declaration) that is being implemented. + implemented_trait_item: ItemRef, /// Whether the corresponding trait item had a default (and therefore this one overrides /// it). overrides_default: bool, @@ -1526,43 +1841,6 @@ pub enum AssocItemContainer { }, } -#[cfg(feature = "rustc")] -fn get_container_for_assoc_item<'tcx, S: BaseState<'tcx>>( - s: &S, - item: &ty::AssocItem, -) -> AssocItemContainer { - let tcx = s.base().tcx; - // We want to solve traits in the context of this item. - let state_with_id = &with_owner_id(s.base(), (), (), item.def_id); - let container_id = item.container_id(tcx); - match item.container { - ty::AssocItemContainer::Trait => { - let trait_ref = ty::TraitRef::identity(tcx, container_id).sinto(state_with_id); - AssocItemContainer::TraitContainer { trait_ref } - } - ty::AssocItemContainer::Impl => { - if let Some(implemented_trait_item) = item.trait_item_def_id { - let impl_generics = ty::GenericArgs::identity_for_item(tcx, container_id); - let item = translate_item_ref(state_with_id, container_id, impl_generics); - let implemented_trait_ref = tcx - .impl_trait_ref(container_id) - .unwrap() - .instantiate_identity(); - AssocItemContainer::TraitImplContainer { - impl_: item, - implemented_trait_ref: implemented_trait_ref.sinto(state_with_id), - implemented_trait_item: implemented_trait_item.sinto(s), - overrides_default: tcx.defaultness(implemented_trait_item).has_value(), - } - } else { - AssocItemContainer::InherentImplContainer { - impl_id: container_id.sinto(s), - } - } - } - } -} - /// Reflects [`ty::ImplTraitInTraitData`] #[derive(AdtInto)] #[args(<'tcx, S: BaseState<'tcx>>, from: ty::ImplTraitInTraitData, state: S as _s)] diff --git a/rust-engine/src/ocaml_engine.rs b/rust-engine/src/ocaml_engine.rs index 134d362bb..35bd00c1a 100644 --- a/rust-engine/src/ocaml_engine.rs +++ b/rust-engine/src/ocaml_engine.rs @@ -87,12 +87,13 @@ impl Query { }; } - let mut engine_subprocess = Command::new("hax-engine") - .arg("driver_rust_engine") - .stdin(std::process::Stdio::piped()) - .stdout(std::process::Stdio::piped()) - .spawn() - .unwrap(); + let mut engine_subprocess = + Command::new(std::env::var("HAX_ENGINE_BINARY").unwrap_or("hax-engine".into())) + .arg("driver_rust_engine") + .stdin(std::process::Stdio::piped()) + .stdout(std::process::Stdio::piped()) + .spawn() + .unwrap(); let mut stdin = std::io::BufWriter::new( engine_subprocess diff --git a/rustc-coverage-tests/snapshots/fstar/Coverage.No_spans.fst b/rustc-coverage-tests/snapshots/fstar/Coverage.No_spans.fst index 6ff9de18b..7f198d144 100644 --- a/rustc-coverage-tests/snapshots/fstar/Coverage.No_spans.fst +++ b/rustc-coverage-tests/snapshots/fstar/Coverage.No_spans.fst @@ -10,10 +10,9 @@ let affected_function (_: Prims.unit) : Prims.unit -> Prims.unit = let main (_: Prims.unit) : Prims.unit = let _:Prims.unit = - Core.Ops.Function.f_call #_ - #Prims.unit + Core.Ops.Function.f_call #Prims.unit #FStar.Tactics.Typeclasses.solve - (affected_function () <: _) + (affected_function () <: Prims.unit -> Prims.unit) (() <: Prims.unit) in () diff --git a/test-harness/src/snapshots/toolchain__attribute-opaque into-fstar.snap b/test-harness/src/snapshots/toolchain__attribute-opaque into-fstar.snap index 8e623da8b..6c822974e 100644 --- a/test-harness/src/snapshots/toolchain__attribute-opaque into-fstar.snap +++ b/test-harness/src/snapshots/toolchain__attribute-opaque into-fstar.snap @@ -133,14 +133,14 @@ class t_T (v_Self: Type0) = { val impl_T_for_u8:t_T u8 class t_TrGeneric (v_Self: Type0) (v_U: Type0) = { - [@@@ FStar.Tactics.Typeclasses.no_method]_super_17240578109911634293:Core.Clone.t_Clone v_U; + [@@@ FStar.Tactics.Typeclasses.no_method]_super_12400030861545157532:Core.Clone.t_Clone v_U; f_f_pre:v_U -> Type0; f_f_post:v_U -> v_Self -> Type0; f_f:x0: v_U -> Prims.Pure v_Self (f_f_pre x0) (fun result -> f_f_post x0 result) } [@@ FStar.Tactics.Typeclasses.tcinstance] -let _ = fun (v_Self:Type0) (v_U:Type0) {|i: t_TrGeneric v_Self v_U|} -> i._super_17240578109911634293 +let _ = fun (v_Self:Type0) (v_U:Type0) {|i: t_TrGeneric v_Self v_U|} -> i._super_12400030861545157532 [@@ FStar.Tactics.Typeclasses.tcinstance] val impl_2 (#v_U: Type0) {| i0: Core.Clone.t_Clone v_U |} : t_TrGeneric i32 v_U diff --git a/test-harness/src/snapshots/toolchain__traits into-fstar.snap b/test-harness/src/snapshots/toolchain__traits into-fstar.snap index 5ec562d9b..297b54b07 100644 --- a/test-harness/src/snapshots/toolchain__traits into-fstar.snap +++ b/test-harness/src/snapshots/toolchain__traits into-fstar.snap @@ -35,14 +35,14 @@ open FStar.Mul class t_BlockSizeUser (v_Self: Type0) = { f_BlockSize:Type0 } class t_ParBlocksSizeUser (v_Self: Type0) = { - [@@@ FStar.Tactics.Typeclasses.no_method]_super_5884559561557426095:t_BlockSizeUser v_Self + [@@@ FStar.Tactics.Typeclasses.no_method]_super_6049459820240371831:t_BlockSizeUser v_Self } [@@ FStar.Tactics.Typeclasses.tcinstance] -let _ = fun (v_Self:Type0) {|i: t_ParBlocksSizeUser v_Self|} -> i._super_5884559561557426095 +let _ = fun (v_Self:Type0) {|i: t_ParBlocksSizeUser v_Self|} -> i._super_6049459820240371831 class t_BlockBackend (v_Self: Type0) = { - [@@@ FStar.Tactics.Typeclasses.no_method]_super_17422710415653782164:t_ParBlocksSizeUser v_Self; + [@@@ FStar.Tactics.Typeclasses.no_method]_super_4483031398080819618:t_ParBlocksSizeUser v_Self; f_proc_block_pre:Alloc.Vec.t_Vec _ Alloc.Alloc.t_Global -> Type0; f_proc_block_post:Alloc.Vec.t_Vec _ Alloc.Alloc.t_Global -> Prims.unit -> Type0; f_proc_block:x0: Alloc.Vec.t_Vec _ Alloc.Alloc.t_Global @@ -50,7 +50,7 @@ class t_BlockBackend (v_Self: Type0) = { } [@@ FStar.Tactics.Typeclasses.tcinstance] -let _ = fun (v_Self:Type0) {|i: t_BlockBackend v_Self|} -> i._super_17422710415653782164 +let _ = fun (v_Self:Type0) {|i: t_BlockBackend v_Self|} -> i._super_4483031398080819618 ''' "Traits.Default_traits_parameters.fst" = ''' module Traits.Default_traits_parameters @@ -61,12 +61,12 @@ open FStar.Mul class t_Bar (v_Self: Type0) (v_T: Type0) = { __marker_trait_t_Bar:Prims.unit } class t_Foo (v_Self: Type0) = { - [@@@ FStar.Tactics.Typeclasses.no_method]_super_7275791365833186636:t_Bar v_Self f_U; + [@@@ FStar.Tactics.Typeclasses.no_method]_super_6177652678586986918:t_Bar v_Self f_U; f_U:Type0 } [@@ FStar.Tactics.Typeclasses.tcinstance] -let _ = fun (v_Self:Type0) {|i: t_Foo v_Self|} -> i._super_7275791365833186636 +let _ = fun (v_Self:Type0) {|i: t_Foo v_Self|} -> i._super_6177652678586986918 ''' "Traits.For_clauses.Issue_495_.Minimized_3_.fst" = ''' module Traits.For_clauses.Issue_495_.Minimized_3_ @@ -434,15 +434,15 @@ let associated_function_caller () class t_SubTrait (v_Self: Type0) (v_TypeArg: Type0) (v_ConstArg: usize) = { - [@@@ FStar.Tactics.Typeclasses.no_method]_super_15145771689388873921:t_Trait v_Self + [@@@ FStar.Tactics.Typeclasses.no_method]_super_18031117442777819101:t_Trait v_Self v_TypeArg v_ConstArg; f_AssocType:Type0; - f_AssocType_2317355564376578742:t_Trait f_AssocType v_TypeArg v_ConstArg + f_AssocType_5661342272396313504:t_Trait f_AssocType v_TypeArg v_ConstArg } [@@ FStar.Tactics.Typeclasses.tcinstance] -let _ = fun (v_Self:Type0) (v_TypeArg:Type0) (v_ConstArg:usize) {|i: t_SubTrait v_Self v_TypeArg v_ConstArg|} -> i._super_15145771689388873921 +let _ = fun (v_Self:Type0) (v_TypeArg:Type0) (v_ConstArg:usize) {|i: t_SubTrait v_Self v_TypeArg v_ConstArg|} -> i._super_18031117442777819101 ''' "Traits.Interlaced_consts_types.fst" = ''' module Traits.Interlaced_consts_types @@ -518,16 +518,16 @@ open FStar.Mul class t_Trait1 (v_Self: Type0) = { f_T:Type0; - f_T_7969211799618487585:t_Trait1 f_T + f_T_5587126706995160828:t_Trait1 f_T } class t_Trait2 (v_Self: Type0) = { - [@@@ FStar.Tactics.Typeclasses.no_method]_super_3259985701465885527:t_Trait1 v_Self; + [@@@ FStar.Tactics.Typeclasses.no_method]_super_6380933412563503442:t_Trait1 v_Self; f_U:Type0 } [@@ FStar.Tactics.Typeclasses.tcinstance] -let _ = fun (v_Self:Type0) {|i: t_Trait2 v_Self|} -> i._super_3259985701465885527 +let _ = fun (v_Self:Type0) {|i: t_Trait2 v_Self|} -> i._super_6380933412563503442 ''' "Traits.Type_alias_bounds_issue_707_.fst" = ''' module Traits.Type_alias_bounds_issue_707_ @@ -637,7 +637,7 @@ open Core open FStar.Mul class t_SuperTrait (v_Self: Type0) = { - [@@@ FStar.Tactics.Typeclasses.no_method]_super_15837849249852401974:Core.Clone.t_Clone v_Self; + [@@@ FStar.Tactics.Typeclasses.no_method]_super_14156401398203956914:Core.Clone.t_Clone v_Self; f_function_of_super_trait_pre:v_Self -> Type0; f_function_of_super_trait_post:v_Self -> u32 -> Type0; f_function_of_super_trait:x0: v_Self @@ -647,12 +647,12 @@ class t_SuperTrait (v_Self: Type0) = { } [@@ FStar.Tactics.Typeclasses.tcinstance] -let _ = fun (v_Self:Type0) {|i: t_SuperTrait v_Self|} -> i._super_15837849249852401974 +let _ = fun (v_Self:Type0) {|i: t_SuperTrait v_Self|} -> i._super_14156401398203956914 [@@ FStar.Tactics.Typeclasses.tcinstance] let impl: t_SuperTrait i32 = { - _super_15837849249852401974 = FStar.Tactics.Typeclasses.solve; + _super_14156401398203956914 = FStar.Tactics.Typeclasses.solve; f_function_of_super_trait_pre = (fun (self: i32) -> true); f_function_of_super_trait_post = (fun (self: i32) (out: u32) -> true); f_function_of_super_trait = fun (self: i32) -> cast (Core.Num.impl_i32__abs self <: i32) <: u32 @@ -714,17 +714,21 @@ let iter_option (#v_T: Type0) (x: Core.Option.t_Option v_T) : Core.Option.t_Into (Core.Option.impl__as_ref #v_T x <: Core.Option.t_Option v_T) let uuse_iimpl_trait (_: Prims.unit) : Prims.unit = - let iter:_ = iter_option #bool (Core.Option.Option_Some false <: Core.Option.t_Option bool) in - let tmp0, out:(_ & Core.Option.t_Option bool) = - Core.Iter.Traits.Iterator.f_next #_ #FStar.Tactics.Typeclasses.solve iter + let iter:Core.Option.t_IntoIter bool = + iter_option #bool (Core.Option.Option_Some false <: Core.Option.t_Option bool) in - let iter:_ = tmp0 in + let tmp0, out:(Core.Option.t_IntoIter bool & Core.Option.t_Option bool) = + Core.Iter.Traits.Iterator.f_next #(Core.Option.t_IntoIter bool) + #FStar.Tactics.Typeclasses.solve + iter + in + let iter:Core.Option.t_IntoIter bool = tmp0 in let _:Core.Option.t_Option bool = out in () class t_Foo (v_Self: Type0) = { f_AssocType:Type0; - f_AssocType_1162045947544099865:t_SuperTrait f_AssocType; + f_AssocType_3062811437891594152:t_SuperTrait f_AssocType; f_N:usize; f_assoc_f_pre:Prims.unit -> Type0; f_assoc_f_post:Prims.unit -> Prims.unit -> Type0; @@ -761,7 +765,7 @@ let g (#v_T: Type0) (#[FStar.Tactics.Typeclasses.tcresolve ()] i0: t_Foo v_T) (x let impl_Foo_for_tuple_: t_Foo Prims.unit = { f_AssocType = i32; - f_AssocType_1162045947544099865 = FStar.Tactics.Typeclasses.solve; + f_AssocType_3062811437891594152 = FStar.Tactics.Typeclasses.solve; f_N = mk_usize 32; f_assoc_f_pre = (fun (_: Prims.unit) -> true); f_assoc_f_post = (fun (_: Prims.unit) (out: Prims.unit) -> true); diff --git a/tests/Cargo.lock b/tests/Cargo.lock index 71e90a8ef..72f5bad1b 100644 --- a/tests/Cargo.lock +++ b/tests/Cargo.lock @@ -354,7 +354,7 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hax-bounded-integers" -version = "0.3.2" +version = "0.3.4" dependencies = [ "duplicate", "hax-lib", @@ -363,7 +363,7 @@ dependencies = [ [[package]] name = "hax-lib" -version = "0.3.2" +version = "0.3.4" dependencies = [ "hax-lib-macros", "num-bigint", @@ -372,7 +372,7 @@ dependencies = [ [[package]] name = "hax-lib-macros" -version = "0.3.2" +version = "0.3.4" dependencies = [ "hax-lib-macros-types", "proc-macro-error2", @@ -383,7 +383,7 @@ dependencies = [ [[package]] name = "hax-lib-macros-types" -version = "0.3.2" +version = "0.3.4" dependencies = [ "proc-macro2", "quote", @@ -394,14 +394,14 @@ dependencies = [ [[package]] name = "hax-lib-protocol" -version = "0.3.2" +version = "0.3.4" dependencies = [ "libcrux", ] [[package]] name = "hax-lib-protocol-macros" -version = "0.3.2" +version = "0.3.4" dependencies = [ "proc-macro-error2", "proc-macro2",