Skip to content

Commit

Permalink
build abstract consts without typeck for QPath::Resolveds
Browse files Browse the repository at this point in the history
  • Loading branch information
BoxyUwU committed Sep 28, 2021
1 parent 98c8619 commit 5b99704
Show file tree
Hide file tree
Showing 9 changed files with 163 additions and 21 deletions.
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() {}

0 comments on commit 5b99704

Please sign in to comment.