From 5b99704e9678cf3f4c14fac81fbddc08c7e4248e Mon Sep 17 00:00:00 2001 From: Ellen Date: Tue, 28 Sep 2021 01:07:16 +0100 Subject: [PATCH] build abstract consts without typeck for `QPath::Resolved`s --- compiler/rustc_hir/src/hir.rs | 7 +++ compiler/rustc_middle/src/hir/map/mod.rs | 23 +++++++++ compiler/rustc_middle/src/query/mod.rs | 10 ++++ .../src/traits/const_evaluatable.rs | 48 ++++++++++-------- .../rustc_typeck/src/abstract_const_build.rs | 49 +++++++++++++++++++ compiler/rustc_typeck/src/astconv/mod.rs | 2 +- compiler/rustc_typeck/src/collect.rs | 3 ++ compiler/rustc_typeck/src/lib.rs | 1 + .../assoc_const_no_typeck.rs | 41 ++++++++++++++++ 9 files changed, 163 insertions(+), 21 deletions(-) create mode 100644 compiler/rustc_typeck/src/abstract_const_build.rs create mode 100644 src/test/ui/const-generics/generic_const_exprs/assoc_const_no_typeck.rs diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 05b652fd5af2c..04ab92d551066 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3253,6 +3253,13 @@ impl<'hir> Node<'hir> { _ => None, } } + + pub fn is_anon_const(&self) -> Option<&'hir AnonConst> { + match self { + Self::AnonConst(ct) => Some(ct), + _ => None, + } + } } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 3707fadadac20..3d91d78c5639a 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -938,6 +938,29 @@ impl<'hir> Map<'hir> { _ => None, } } + + /// Returns Some(..) if the body only contains a fully qualified associated constant + /// (optionally surrounded in a block) + pub fn is_fully_qualif_assoc_const_proj( + &self, + body: BodyId, + ) -> Option<(&'hir Ty<'hir>, &'hir Path<'hir>)> { + let body = self.body(body); + // get rid of an optional outer level of `{}` so that this can return `Some` for + // the anon const in: `foo::<{ <() as Trait>::ASSOC }>();` + let expr = match &body.value.kind { + ExprKind::Block(Block { stmts: [], expr: Some(e), .. }, _) => &e.kind, + e => e, + }; + + match expr { + ExprKind::Path(QPath::Resolved( + Some(this), + path @ Path { res: Res::Def(DefKind::AssocConst, _), segments: [_, _], .. }, + )) => Some((this, path)), + _ => None, + } + } } impl<'hir> intravisit::Map<'hir> for Map<'hir> { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index b4f7a9fa8e9d6..2d4b57a71bab9 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1792,4 +1792,14 @@ rustc_queries! { no_hash desc { "performing HIR wf-checking for predicate {:?} at item {:?}", key.0, key.1 } } + + /// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead. + query abstract_const_from_fully_qualif_assoc( + key: ty::WithOptConstParam + ) -> Option]>> { + desc { + "building an abstract representation for the const argument {}", + tcx.def_path_str(key.did.to_def_id()), + } + } } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 24fa5007f1ecd..351b3fe73991e 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -433,29 +433,37 @@ pub(super) fn thir_abstract_const<'tcx>( tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam, ) -> Result]>, ErrorReported> { - if tcx.features().generic_const_exprs { - match tcx.def_kind(def.did) { - // FIXME(generic_const_exprs): We currently only do this for anonymous constants, - // meaning that we do not look into associated constants. I(@lcnr) am not yet sure whether - // we want to look into them or treat them as opaque projections. - // - // Right now we do neither of that and simply always fail to unify them. - DefKind::AnonConst => (), - _ => return Ok(None), - } + if tcx.lazy_normalization() == false { + return Ok(None); + } - let body = tcx.thir_body(def); - if body.0.borrow().exprs.is_empty() { - // type error in constant, there is no thir - return Err(ErrorReported); - } + match tcx.def_kind(def.did) { + // FIXME(generic_const_exprs): We currently only do this for anonymous constants, + // meaning that we do not look into associated constants. I(@lcnr) am not yet sure whether + // we want to look into them or treat them as opaque projections. + // + // Right now we do neither of that and simply always fail to unify them. + DefKind::AnonConst => (), + _ => return Ok(None), + } + debug!("thir_abstract_const: def={:?}", def.did); + + // If the anon const is a fully qualified assoc const i.e. `{ >::ASSOC }` + // we lower it to an abstract const without typeck'ing which helps to avoid cycles when + // equating consts in where clauses + if let Some(opt_unevaluated) = tcx.abstract_const_from_fully_qualif_assoc(def) { + return Ok(opt_unevaluated); + } - AbstractConstBuilder::new(tcx, (&*body.0.borrow(), body.1))? - .map(AbstractConstBuilder::build) - .transpose() - } else { - Ok(None) + let body = tcx.thir_body(def); + if body.0.borrow().exprs.is_empty() { + // type error in constant, there is no thir + return Err(ErrorReported); } + + AbstractConstBuilder::new(tcx, (&*body.0.borrow(), body.1))? + .map(AbstractConstBuilder::build) + .transpose() } pub(super) fn try_unify_abstract_consts<'tcx>( diff --git a/compiler/rustc_typeck/src/abstract_const_build.rs b/compiler/rustc_typeck/src/abstract_const_build.rs new file mode 100644 index 0000000000000..88480d1e2f33d --- /dev/null +++ b/compiler/rustc_typeck/src/abstract_const_build.rs @@ -0,0 +1,49 @@ +use rustc_middle::thir::abstract_const::Node as ACNode; +use rustc_middle::ty::{self, DefIdTree, TyCtxt, TypeFoldable}; +use rustc_span::def_id::LocalDefId; + +/// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead. +pub(super) fn abstract_const_from_fully_qualif_assoc<'tcx>( + tcx: TyCtxt<'tcx>, + def: ty::WithOptConstParam, +) -> Option]>> { + let anon_ct_hir_id = tcx.hir().local_def_id_to_hir_id(def.did); + tcx.hir() + .get(anon_ct_hir_id) + .is_anon_const() + .and_then(|ct| tcx.hir().is_fully_qualif_assoc_const_proj(ct.body)) + .map(|(this, path)| { + let trait_did = tcx.parent(path.res.def_id()).unwrap(); + debug!("trait_did: {:?}", trait_did); + let item_ctxt: &dyn crate::astconv::AstConv<'_> = + &crate::collect::ItemCtxt::new(tcx, trait_did); + let self_ty = item_ctxt.ast_ty_to_ty(this); + let trait_ref_substs = >::ast_path_to_mono_trait_ref( + item_ctxt, + path.span, + trait_did, + self_ty, + &path.segments[0], + ) + .substs; + debug!("trait_ref_substs: {:?}", trait_ref_substs); + + // there is no such thing as `feature(generic_associated_consts)` yet so we dont need + // to handle substs for the const on the trait i.e. `N` in `>::ASSOC::` + assert!(path.segments[1].args.is_none()); + + trait_ref_substs.definitely_has_param_types_or_consts(tcx).then(|| { + let ct = tcx.mk_const(ty::Const { + val: ty::ConstKind::Unevaluated(ty::Unevaluated::new( + ty::WithOptConstParam { + did: path.res.def_id(), + const_param_did: def.const_param_did, + }, + trait_ref_substs, + )), + ty: tcx.type_of(path.res.def_id()), + }); + &*tcx.arena.alloc_from_iter([ACNode::Leaf(ct)]) + }) + }) +} diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 33df541eb2ba8..58aa5ae41c30f 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -837,7 +837,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ); } - fn ast_path_to_mono_trait_ref( + pub(crate) fn ast_path_to_mono_trait_ref( &self, span: Span, trait_def_id: DefId, diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 51f9f459af1c5..a00a11b6ac929 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -91,6 +91,9 @@ pub fn provide(providers: &mut Providers) { codegen_fn_attrs, collect_mod_item_types, should_inherit_track_caller, + // TODO: find a proper place for this + abstract_const_from_fully_qualif_assoc: + crate::abstract_const_build::abstract_const_from_fully_qualif_assoc, ..*providers }; } diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index f8714cdc70c19..ea0209de64961 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -80,6 +80,7 @@ extern crate rustc_middle; pub mod check; pub mod expr_use_visitor; +mod abstract_const_build; mod astconv; mod bounds; mod check_unused; diff --git a/src/test/ui/const-generics/generic_const_exprs/assoc_const_no_typeck.rs b/src/test/ui/const-generics/generic_const_exprs/assoc_const_no_typeck.rs new file mode 100644 index 0000000000000..6a0b621b9f1af --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/assoc_const_no_typeck.rs @@ -0,0 +1,41 @@ +// check-pass +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +trait Trait { + const ASSOC: usize; + type Foo; +} + +fn no_cycle() +where + u8: Trait, + (): Trait<{ >::ASSOC }>, + [(); <() as Trait<{ >::ASSOC }>>::ASSOC]: , +{ +} + +fn foo(_: [(); <<() as Trait>::Foo as Trait>::ASSOC]) +where + (): Trait, + <() as Trait>::Foo: Trait, +{ +} + +trait Trait2 { + type Foo; +} + +struct Inherent; +impl Inherent { + const ASSOC: usize = 10; +} + +fn bar() +where + (): Trait2, + [(); <() as Trait2>::Foo::ASSOC]: , +{ +} + +fn main() {}