Implement const generic bounds in order to check if they are well formed

Hello everyone, I have some free time and thought I could contribute a bit to the rust compiler. I have a bit of experience implementing stuff and already know what I want to try (proposal 2 from this comment I know that it might be a bit early to start implementing such a vague proposal, but it sparked my interest and I mostly am doing it in order to get a bit familiar with the internals of rustc (so please forgive me if there are some obvious missuses of types).

TL;DR of the proposal: The success of compiling the following code depends on the concrete value of C, resulting in post-monomorphisation errors, which are undesired.

fn foo<const C: usize>() {
    let x = [u8; C - 2];

Proposal: Add a type of where bound that exposes all const-generic expressions inside the function so that the call site can check them pre-monomorphisation:

fn foo<const C: usize>() where (C-2) {
    let x = [u8; C - 2];

Obviously all just hypothetical syntax.

I'm working based on this rustc commit: 0f5c76951327b912c8e92e83235430ebd9b349d9

What I have done already:

  • added some required variants and types to rustc_ast in order parse the syntax (WherePredicate::ConstPredicate basically a wrapper around AnonConst)
  • added the corresponding types to rustc_ast_lowering
  • added and stubbed the doc types
  • The following code gets parsed into an AST (contrary to the example above I used ={} as the syntax, as it was easier to implement and could obviously be changed later on. (For the interested, the problem with where (<const expr>) was that their exist types that start with an ( and as such parsing it would require more logic.
fn foo<const C: usize>() where ={C-2} {
    let x = [u8; C - 2];

Where I'm currently unsure if it is the correct approach is the following (in rustc_typecheck/src/collectstarting at line 2042 ):

&hir::WherePredicate::ConstPredicate(ref const_pred) => {
    let const_def_id = icx.tcx.hir().local_def_id(const_pred.const_expr.hir_id);
    let c = ty::Const::from_anon_const(icx.tcx, const_def_id);
    let pred = ty::PredicateAtom::WellFormed(c.into());
    predicates.push((pred.to_predicate(icx.tcx), const_pred.span));

What happens here is that I take the anonymous const expression from the where bound and put it inside a WellFormed predicate.

Now I currently have two questions:

Is it correct to use WellFormed predicate? There is also a ConstEvaluatable predicate, however I could not find out how to correctly set the SubstRef field.

Currently the code fails to compile with an error in rustc_typecheck/src/collect/type_of where it tries to find out what type the AnonConst is. This happens due to the code encountering an unexpected parent node variant (in this case a Fn). My guess would be that I should be able to treat it as if the expression was found inside the function body and was wondering if the lines 312-316 are the ones responsible for this case? (I don't really understand line 304-310 as they just return the type usize, maybe someone could explain to me what case this is?).

I already have a rough idea what the next step should be after this:

  • Check for every const expr inside a function (including where bound on called functions) that it can be evaluated pre-monomorph or if not, that it exists as a where bound

In case this is the wrong place for these questions I would appreciate a hint for the correct place :slight_smile:

Thanks in advance, Raidwas

1 Like

Hi @Raidwas, it's good to see someone investigating this issue in more depth! I would suggest asking on the #t-compiler channel on Zulip, which is a good place to discuss questions about the compiler like this one. If you start a new thread, and post a link to your branch, it'll also be easier to take a look and give suggestions.

It's worth leaving a comment on to mention that you're working on this, in case anyone has some comments there (some people won't be keeping up with IRLO).

I think solution for this problem should go via RFC first (or the lang team should make an official desicion on the matter). As argued in the linked thread, personally I think that adding such explicit bounds will be a mistake and I strongly prefer the third option (compiler will automatically infer and track such bounds implicitly), but which would work globally, not limited to a function body.

Note, that I do want to be able to add bounds like "C should be in this range" or "C should be element of the given set", but I do not want the mandatory "dumb" bounds.

I did not say any of the solution should be preferred over another (while I have a preference). Currently I'm just doing this in order to understand a bit how rustc works.

And yes, I agree with you that anything in this direction would first have to go trough some RFC process :slight_smile:

Just a note that there's already an extensive Zulip thread about the implementation of proposal 2, which would be worth checking out and commenting on. (I had forgotten about this previously, apologies.)

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.