Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build abstract consts without typeck for fully qualified associated consts #89231

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
23 changes: 23 additions & 0 deletions compiler/rustc_middle/src/hir/map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<LocalDefId>
) -> Option<Option<&'tcx [thir::abstract_const::Node<'tcx>]>> {
desc {
"building an abstract representation for the const argument {}",
tcx.def_path_str(key.did.to_def_id()),
}
}
}
48 changes: 28 additions & 20 deletions compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,29 +433,37 @@ pub(super) fn thir_abstract_const<'tcx>(
tcx: TyCtxt<'tcx>,
def: ty::WithOptConstParam<LocalDefId>,
) -> Result<Option<&'tcx [thir::abstract_const::Node<'tcx>]>, 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. `{ <T as Trait<U>>::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>(
Expand Down
49 changes: 49 additions & 0 deletions compiler/rustc_typeck/src/abstract_const_build.rs
Original file line number Diff line number Diff line change
@@ -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<LocalDefId>,
) -> Option<Option<&'tcx [ACNode<'tcx>]>> {
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 = <dyn crate::astconv::AstConv<'_>>::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 `<T as Trait<U>>::ASSOC::<N>`
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)])
})
})
}
2 changes: 1 addition & 1 deletion compiler/rustc_typeck/src/astconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_typeck/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_typeck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// check-pass
#![feature(generic_const_exprs)]
#![allow(incomplete_features)]

trait Trait<const N: usize> {
const ASSOC: usize;
type Foo;
}

fn no_cycle<const N: usize>()
where
u8: Trait<N>,
(): Trait<{ <u8 as Trait<N>>::ASSOC }>,
[(); <() as Trait<{ <u8 as Trait<N>>::ASSOC }>>::ASSOC]: ,
{
}

fn foo<const N: usize>(_: [(); <<() as Trait<N>>::Foo as Trait<N>>::ASSOC])
where
(): Trait<N>,
<() as Trait<N>>::Foo: Trait<N>,
{
}

trait Trait2<T> {
type Foo;
}

struct Inherent;
impl Inherent {
const ASSOC: usize = 10;
}

fn bar<T>()
where
(): Trait2<T, Foo = Inherent>,
[(); <() as Trait2<T>>::Foo::ASSOC]: ,
{
}

fn main() {}