Skip to content

Commit 5b99704

Browse files
committed
build abstract consts without typeck for QPath::Resolveds
1 parent 98c8619 commit 5b99704

File tree

9 files changed

+163
-21
lines changed

9 files changed

+163
-21
lines changed

compiler/rustc_hir/src/hir.rs

+7
Original file line numberDiff line numberDiff line change
@@ -3253,6 +3253,13 @@ impl<'hir> Node<'hir> {
32533253
_ => None,
32543254
}
32553255
}
3256+
3257+
pub fn is_anon_const(&self) -> Option<&'hir AnonConst> {
3258+
match self {
3259+
Self::AnonConst(ct) => Some(ct),
3260+
_ => None,
3261+
}
3262+
}
32563263
}
32573264

32583265
// Some nodes are used a lot. Make sure they don't unintentionally get bigger.

compiler/rustc_middle/src/hir/map/mod.rs

+23
Original file line numberDiff line numberDiff line change
@@ -938,6 +938,29 @@ impl<'hir> Map<'hir> {
938938
_ => None,
939939
}
940940
}
941+
942+
/// Returns Some(..) if the body only contains a fully qualified associated constant
943+
/// (optionally surrounded in a block)
944+
pub fn is_fully_qualif_assoc_const_proj(
945+
&self,
946+
body: BodyId,
947+
) -> Option<(&'hir Ty<'hir>, &'hir Path<'hir>)> {
948+
let body = self.body(body);
949+
// get rid of an optional outer level of `{}` so that this can return `Some` for
950+
// the anon const in: `foo::<{ <() as Trait>::ASSOC }>();`
951+
let expr = match &body.value.kind {
952+
ExprKind::Block(Block { stmts: [], expr: Some(e), .. }, _) => &e.kind,
953+
e => e,
954+
};
955+
956+
match expr {
957+
ExprKind::Path(QPath::Resolved(
958+
Some(this),
959+
path @ Path { res: Res::Def(DefKind::AssocConst, _), segments: [_, _], .. },
960+
)) => Some((this, path)),
961+
_ => None,
962+
}
963+
}
941964
}
942965

943966
impl<'hir> intravisit::Map<'hir> for Map<'hir> {

compiler/rustc_middle/src/query/mod.rs

+10
Original file line numberDiff line numberDiff line change
@@ -1792,4 +1792,14 @@ rustc_queries! {
17921792
no_hash
17931793
desc { "performing HIR wf-checking for predicate {:?} at item {:?}", key.0, key.1 }
17941794
}
1795+
1796+
/// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead.
1797+
query abstract_const_from_fully_qualif_assoc(
1798+
key: ty::WithOptConstParam<LocalDefId>
1799+
) -> Option<Option<&'tcx [thir::abstract_const::Node<'tcx>]>> {
1800+
desc {
1801+
"building an abstract representation for the const argument {}",
1802+
tcx.def_path_str(key.did.to_def_id()),
1803+
}
1804+
}
17951805
}

compiler/rustc_trait_selection/src/traits/const_evaluatable.rs

+28-20
Original file line numberDiff line numberDiff line change
@@ -433,29 +433,37 @@ pub(super) fn thir_abstract_const<'tcx>(
433433
tcx: TyCtxt<'tcx>,
434434
def: ty::WithOptConstParam<LocalDefId>,
435435
) -> Result<Option<&'tcx [thir::abstract_const::Node<'tcx>]>, ErrorReported> {
436-
if tcx.features().generic_const_exprs {
437-
match tcx.def_kind(def.did) {
438-
// FIXME(generic_const_exprs): We currently only do this for anonymous constants,
439-
// meaning that we do not look into associated constants. I(@lcnr) am not yet sure whether
440-
// we want to look into them or treat them as opaque projections.
441-
//
442-
// Right now we do neither of that and simply always fail to unify them.
443-
DefKind::AnonConst => (),
444-
_ => return Ok(None),
445-
}
436+
if tcx.lazy_normalization() == false {
437+
return Ok(None);
438+
}
446439

447-
let body = tcx.thir_body(def);
448-
if body.0.borrow().exprs.is_empty() {
449-
// type error in constant, there is no thir
450-
return Err(ErrorReported);
451-
}
440+
match tcx.def_kind(def.did) {
441+
// FIXME(generic_const_exprs): We currently only do this for anonymous constants,
442+
// meaning that we do not look into associated constants. I(@lcnr) am not yet sure whether
443+
// we want to look into them or treat them as opaque projections.
444+
//
445+
// Right now we do neither of that and simply always fail to unify them.
446+
DefKind::AnonConst => (),
447+
_ => return Ok(None),
448+
}
449+
debug!("thir_abstract_const: def={:?}", def.did);
450+
451+
// If the anon const is a fully qualified assoc const i.e. `{ <T as Trait<U>>::ASSOC }`
452+
// we lower it to an abstract const without typeck'ing which helps to avoid cycles when
453+
// equating consts in where clauses
454+
if let Some(opt_unevaluated) = tcx.abstract_const_from_fully_qualif_assoc(def) {
455+
return Ok(opt_unevaluated);
456+
}
452457

453-
AbstractConstBuilder::new(tcx, (&*body.0.borrow(), body.1))?
454-
.map(AbstractConstBuilder::build)
455-
.transpose()
456-
} else {
457-
Ok(None)
458+
let body = tcx.thir_body(def);
459+
if body.0.borrow().exprs.is_empty() {
460+
// type error in constant, there is no thir
461+
return Err(ErrorReported);
458462
}
463+
464+
AbstractConstBuilder::new(tcx, (&*body.0.borrow(), body.1))?
465+
.map(AbstractConstBuilder::build)
466+
.transpose()
459467
}
460468

461469
pub(super) fn try_unify_abstract_consts<'tcx>(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use rustc_middle::thir::abstract_const::Node as ACNode;
2+
use rustc_middle::ty::{self, DefIdTree, TyCtxt, TypeFoldable};
3+
use rustc_span::def_id::LocalDefId;
4+
5+
/// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead.
6+
pub(super) fn abstract_const_from_fully_qualif_assoc<'tcx>(
7+
tcx: TyCtxt<'tcx>,
8+
def: ty::WithOptConstParam<LocalDefId>,
9+
) -> Option<Option<&'tcx [ACNode<'tcx>]>> {
10+
let anon_ct_hir_id = tcx.hir().local_def_id_to_hir_id(def.did);
11+
tcx.hir()
12+
.get(anon_ct_hir_id)
13+
.is_anon_const()
14+
.and_then(|ct| tcx.hir().is_fully_qualif_assoc_const_proj(ct.body))
15+
.map(|(this, path)| {
16+
let trait_did = tcx.parent(path.res.def_id()).unwrap();
17+
debug!("trait_did: {:?}", trait_did);
18+
let item_ctxt: &dyn crate::astconv::AstConv<'_> =
19+
&crate::collect::ItemCtxt::new(tcx, trait_did);
20+
let self_ty = item_ctxt.ast_ty_to_ty(this);
21+
let trait_ref_substs = <dyn crate::astconv::AstConv<'_>>::ast_path_to_mono_trait_ref(
22+
item_ctxt,
23+
path.span,
24+
trait_did,
25+
self_ty,
26+
&path.segments[0],
27+
)
28+
.substs;
29+
debug!("trait_ref_substs: {:?}", trait_ref_substs);
30+
31+
// there is no such thing as `feature(generic_associated_consts)` yet so we dont need
32+
// to handle substs for the const on the trait i.e. `N` in `<T as Trait<U>>::ASSOC::<N>`
33+
assert!(path.segments[1].args.is_none());
34+
35+
trait_ref_substs.definitely_has_param_types_or_consts(tcx).then(|| {
36+
let ct = tcx.mk_const(ty::Const {
37+
val: ty::ConstKind::Unevaluated(ty::Unevaluated::new(
38+
ty::WithOptConstParam {
39+
did: path.res.def_id(),
40+
const_param_did: def.const_param_did,
41+
},
42+
trait_ref_substs,
43+
)),
44+
ty: tcx.type_of(path.res.def_id()),
45+
});
46+
&*tcx.arena.alloc_from_iter([ACNode::Leaf(ct)])
47+
})
48+
})
49+
}

compiler/rustc_typeck/src/astconv/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -837,7 +837,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
837837
);
838838
}
839839

840-
fn ast_path_to_mono_trait_ref(
840+
pub(crate) fn ast_path_to_mono_trait_ref(
841841
&self,
842842
span: Span,
843843
trait_def_id: DefId,

compiler/rustc_typeck/src/collect.rs

+3
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ pub fn provide(providers: &mut Providers) {
9191
codegen_fn_attrs,
9292
collect_mod_item_types,
9393
should_inherit_track_caller,
94+
// TODO: find a proper place for this
95+
abstract_const_from_fully_qualif_assoc:
96+
crate::abstract_const_build::abstract_const_from_fully_qualif_assoc,
9497
..*providers
9598
};
9699
}

compiler/rustc_typeck/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ extern crate rustc_middle;
8080
pub mod check;
8181
pub mod expr_use_visitor;
8282

83+
mod abstract_const_build;
8384
mod astconv;
8485
mod bounds;
8586
mod check_unused;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// check-pass
2+
#![feature(generic_const_exprs)]
3+
#![allow(incomplete_features)]
4+
5+
trait Trait<const N: usize> {
6+
const ASSOC: usize;
7+
type Foo;
8+
}
9+
10+
fn no_cycle<const N: usize>()
11+
where
12+
u8: Trait<N>,
13+
(): Trait<{ <u8 as Trait<N>>::ASSOC }>,
14+
[(); <() as Trait<{ <u8 as Trait<N>>::ASSOC }>>::ASSOC]: ,
15+
{
16+
}
17+
18+
fn foo<const N: usize>(_: [(); <<() as Trait<N>>::Foo as Trait<N>>::ASSOC])
19+
where
20+
(): Trait<N>,
21+
<() as Trait<N>>::Foo: Trait<N>,
22+
{
23+
}
24+
25+
trait Trait2<T> {
26+
type Foo;
27+
}
28+
29+
struct Inherent;
30+
impl Inherent {
31+
const ASSOC: usize = 10;
32+
}
33+
34+
fn bar<T>()
35+
where
36+
(): Trait2<T, Foo = Inherent>,
37+
[(); <() as Trait2<T>>::Foo::ASSOC]: ,
38+
{
39+
}
40+
41+
fn main() {}

0 commit comments

Comments
 (0)