-
Notifications
You must be signed in to change notification settings - Fork 17
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
Preliminary support for Const Generics in refinements #637
Conversation
hang on, let me merge first... |
@nilehmann -- the above is ready for review. One annoying thing is the addition of the |
Added the stuff for arrays too, so this works: pub fn test01<const N: usize>(arr: &[i32; N]) -> i32 {
arr[0] //~ ERROR assertion might fail
}
pub fn test02<const N: usize>(arr: &[i32; N]) -> i32 {
if N > 0 {
arr[0]
} else {
99
}
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ranjitjhala First round of comments.
About the proliferation of ConstGenericArgs::empty()
, I'm not entirely sure what's the best strategy, but here is my thinking.
Right now we have two functions to eliminate an EarlyBinder
, instantiate
and instantiate_identity
.
instantiate
: This one is meant to be used when calling/using an item external to the function being checked.instantiate_identity
: This one is meant to be used when going inside an item (function, adt, ...) to signal that all parameters are now "free" (i.e., universally quantified). In rustc, this is a no-op because (early) bound parameters are represented the same as free parameters.
Since we need to make the "freeing" of refinement parameters explict, I propose we separate this into three functions
instantiate
: This remains the sameenter_forall
(not sure about the name): This is used when going inside an item. We should use this in all the places where we are currently callinginstantiate_identity
withrefine_params
andconst_generic_args
.instantiate_identity
: This becomes a no-op and should be used in places where we enter an item (and we are technically freeing parameters) but the thing we are doing doesn't depend on refinements. I think the only place that qualifies for this iscompare_impl_item
where we manipulate sorts, which should not depend on refinements. The worst that can happen if we call this function by mistake instead ofenter_forall
is that we free parameters implicitly causing a crash when encoding into fixpoint.
Next, I'd make instantiate
take the ConstGenericArgs
and make the computation in call_const_generic_args
inside instantiate
. This will make it evident that some places are missing the instantiation of const generics (e.g. place_ty
). To make this more manageable, we could put refine_params
and geneneric_arg_consts
in the same struct (refine_params
is already being passed around)
This implementation is trickier than I thought because you need to pass the list of generated vars to every call to |
We modify |
@nilehmann -- I made the changes so all the stuff is in |
The one tedious thing is you have to pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ranjitjhala Looks good. There are still some comments from the first review that need attention. I also added a couple of extra small comments.
It is unfortunate that we have to pass tcx
to instantiate
, but we almost always have access to it so it shouldn't be too bad.
Have made all the changes except the |
ok I believe I addressed all comments -- in particular made all the Will merge if CI is green! |
The following works now, but I'd like to get some feedback on simplifying etc. before moving to the actual use case of array lengths.
The main changes are these:
Checking Definitions
a0
)TypeEnv
to track a mappub struct ConstGenericArgs(FxHashMap<u32, Expr>)
that maps eachN
to the singleton expr,TypeEnv
to give occurrences of GenericParam the corresponding singleton-indexed type (e.g.usize[a0]
)So given
fn foo<N: usize>() -> usize{v: N < v} { N + 1 }
STEP 1. Get a name
a0
STEP 2. Substitute the sig to get
fn() -> usize{v: a0 < v}
STEP 3. Extend the
TypeEnv
to trackN -> a0
STEP 4. Type check using
TypeEnv
to get the constraintforall a0. a0 < a0 + 1
Checking Calls
Similar to the above, except that when we
instantiate
thegeneric_args
we again build up aConstGenericArgs
to substitute all the generic params in the refinements with the appropriate constants e.g.3
or the correspondinga0
singleton name at the callsite (using the caller'sTypeEnv
), as done incall_const_generic_args
.