Skip to content

Refactor ty_is_int #200

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

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open

Refactor ty_is_int #200

wants to merge 16 commits into from

Conversation

tiif
Copy link
Member

@tiif tiif commented Aug 12, 2025

Ignore the first commit, it is just UPDATE_EXPECT=1 for the change in main branch

Fixed #198

if !ty_is_int(value_ty) {
bail!("The value used for switch must be an int.");
}
self.prove_goal(&typeck_env.env, Wcs::default(), value_ty.is_int())?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally don't like introducing a new judgement for this. I think if we want this check to be a proper judgement maybe add a builtin trait and prove value_ty: BuiltinInteger here 🤔

Even if it's a proper judegement:tm: but instead be just something else you can prove. Predicates feel core to the type system in a way in which "is this an integer" does not. That's just my personal vibe here though

What's the reasoning behind changing an assertion to an actual goal?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason of changing this to a goal is I feel we should have a way to say "this type is an int" somewhere instead of having this function https://github.com/rust-lang/a-mir-formality/pull/200/files#diff-b58482e54c5aa3c63282fc2a3a0aa88194e826004773bd7bc9e385f29b5c3e0d

But I do agree that adding a predicate for this might be an overkill.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we instead just use a "non-predicate" judgement here. Same as we do for trait_ref_is_knowable or however it's called?

Copy link
Member Author

@tiif tiif Aug 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I forgot to mention that I was using a Relation instead of Predicate.

From my understanding, we can only use a judgment_fn! through prove_wc, and when we call prove_wc, we need to pick one of the below as the Wc type:

pub enum WcData {
    /// Means the built-in relation holds.
    #[cast]
    Relation(Relation),

    /// Means the predicate holds.
    #[cast]
    Predicate(Predicate),

    // Equivalent to `for<'a>` except that it can also express `for<ty T>` and so forth:
    // means `$v0` is true for any value of the bound variables (e.g., `'a` or `T`).
    #[grammar(for $v0)]
    ForAll(Binder<Wc>),

    #[grammar(if $v0 $v1)]
    Implies(Wcs, Wc),
}

Relation looks the most reasonable to me among all of them.

(
(is_int(decl, env, assumptions, ty) => c)
----------------------------- ("ty is int")
(prove_wc(decl, env, assumptions, Relation::IsInt(ty)) => c)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how does a-mir-formality handle normalization when proving predicates/relations?

Because right now <u32 as Id>::This fails to satisfy IsInt

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's true, I did an experiment in 331c737#diff-2ca1246445028c80247cd13d93fd973d22ca55cfb7a76c0903a04c09055d706c and it indeed failed.

The place where normalization is done should be here


It is possible to use prove_normalize when proving a goal, for example:
(
(prove_normalize(&decls, env, &assumptions, goal) => (c1, p))
(let assumptions = c1.substitution().apply(&assumptions))
(is_local_parameter(&decls, c1.env(), assumptions, p) => c2)
--- ("local parameter")
(is_local_parameter(decls, env, assumptions, goal) => c1.seq(c2))
)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But with the change in c0fbd8b, it is possible to make IsInt work with normalization.

(let assumptions = c1.substitution().apply(&assumptions))
(is_int(&decl, &env, assumptions, p) => c2)
----------------------------- ("ty is int")
(prove_wc(decl, env, assumptions, Relation::IsInt(ty)) => c2)
Copy link
Contributor

@lcnr lcnr Aug 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, maybe also as a question for @nikomatsakis

This currently means there are 2 leaf judegements for proving is_int, one with normalization and one without.

What does prove_normalize actually do 😅 is it "normalize until rigid [but at least 1 step]" or "normalize 1 step"?

I think regardless, I personally prefer the setup of prove_wc(Relation::IsInt(ty)) -> normalize(ty) -> prove_wc(Relation::IsInt(normalized_ty)) so that there's only one "leaf" judgement here

Copy link
Member Author

@tiif tiif Aug 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I write two judgements because prove_normalize will fail if we pass an int type to it. I slightly changed the rule for ty_is_int in 1f601cf so it always normalize the type to rigid if possible, and if it can't be normalize to rigid, it should just fail.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Answer: prove_normalize does a single step. I think this logic is good, because the base mir-formality infra should handle cycles -- we should add a test for fun, though.

tiif and others added 3 commits August 15, 2025 13:33
The idea is that judgment functions correspond
to "Random Rust Code" in the compiler
and Predicates/Relations are limited to
things that can appear in where-clauses
(explicitly or implicitly).

The `formality_prove` module then roughly
corresponds to the trait system + type
inference logic in rustc.
@nikomatsakis
Copy link
Contributor

@lcnr -- @tiif and I discussed it and did a bit of refactoring. Now there is no Relation::TyIsInt and instead we are writing judgments in the mini-rust-check, which I think corresponds a bit more to the compiler (judgment fn is basically "Random Rust Code" and predicates/relations are reserved for things you will find in where-clause lists, implicitly or explicitly).

// For the rest of the case, it should fail.

(
(prove_normalize(&decl, &env, &assumptions, ty) => (c1, p))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
(prove_normalize(&decl, &env, &assumptions, ty) => (c1, p))
(prove_normalize(&decl, &env, &assumptions, ty) => (c1, p))!

I'd guess we don't care about this candidate if stuff is unnormalizeable? or well, we currently use this judegement even for non alias types 🤔 I feel like maybe limiting prove_normalize to require an AliasTerm (or whatever we've got in a-mir-formality) would make avoid this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I debated about that. I think this is a good addition, the only downside would be if you expect it to be normalizable and it's not... you won't readily know why... might be that we can do better with these annotations.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like maybe limiting prove_normalize to require an AliasTerm (or whatever we've got in a-mir-formality) would make avoid this?

Thinking about this, this seems generally desirable esp if it's literally shallow one-step normalization, using it for non-alias types seems weird imo

clearly not something that has to happen in this PR 😁

Copy link
Contributor

@lcnr lcnr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the impl seems nice now 😊 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Refactor ty_is_int
3 participants