-
Notifications
You must be signed in to change notification settings - Fork 182
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
Support const evaulation #596
Changes from 1 commit
eea70d6
58fb4ff
e0748ad
69623ba
b4684d0
f4cd784
f2ca1db
d767d7e
6a6a68f
3389719
bce9291
0b603f9
d4ee9a3
d05b66c
60da39c
1c38d7c
e048d93
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,7 +33,7 @@ use crate::Ty; | |
use crate::TyData; | ||
use crate::VariableKind; | ||
use crate::VariableKinds; | ||
use crate::{Const, ConstData}; | ||
use crate::{Const, ConstData, ConstEvalError}; | ||
use std::fmt::{self, Debug}; | ||
use std::hash::Hash; | ||
use std::marker::PhantomData; | ||
|
@@ -93,6 +93,15 @@ pub trait Interner: Debug + Copy + Eq + Ord + Hash { | |
/// evaluated consts. | ||
type InternedConcreteConst: Debug + Clone + Eq + Hash; | ||
|
||
/// "Interned" representation of an unevaluated const expression. In normal user code, | ||
/// `Self::InternedUnevaluatedConst` is not referenced. Instead, | ||
/// we refer to `UnevaluatedConst<Self>`, which wraps this type. | ||
/// | ||
/// `InternedUnevaluatedConst` instances are not created by chalk, | ||
/// it can only evaluate them with the [`try_eval_const`] method | ||
/// and check for (syntactic for now) equality between them. | ||
type InternedUnevaluatedConst: Debug + Clone + Eq + Hash; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we anticipate that this will carry a "substitutions"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I expect not, right? |
||
|
||
/// "Interned" representation of a "generic parameter", which can | ||
/// be either a type or a lifetime. In normal user code, | ||
/// `Self::InternedGenericArg` is not referenced. Instead, we refer to | ||
|
@@ -456,14 +465,25 @@ pub trait Interner: Debug + Copy + Eq + Ord + Hash { | |
/// Lookup the `ConstData` that was interned to create a `InternedConst`. | ||
fn const_data<'a>(&self, constant: &'a Self::InternedConst) -> &'a ConstData<Self>; | ||
|
||
/// Deterermine whether two concrete const values are equal. | ||
/// Determine whether two concrete const values are equal. | ||
fn const_eq( | ||
&self, | ||
ty: &Self::InternedType, | ||
c1: &Self::InternedConcreteConst, | ||
c2: &Self::InternedConcreteConst, | ||
) -> bool; | ||
|
||
/// Determine whether two unevaluated const expressions are equal. | ||
fn unevaluated_const_eq( | ||
hayashi-stl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
&self, | ||
ty: &Self::InternedType, | ||
c1: &Self::InternedUnevaluatedConst, | ||
c2: &Self::InternedUnevaluatedConst, | ||
) -> bool; | ||
|
||
/// Attempt to evaluate a const to a concrete const. | ||
fn try_eval_const(&self, ty: &Self::InternedType, constant: &Self::InternedUnevaluatedConst) -> Result<Self::InternedConcreteConst, ConstEvalError>; | ||
|
||
/// Create an "interned" parameter from `data`. This is not | ||
/// normally invoked directly; instead, you invoke | ||
/// `GenericArgData::intern` (which will ultimately call this | ||
|
@@ -634,6 +654,12 @@ pub trait TargetInterner<I: Interner>: Interner { | |
const_evaluated: &I::InternedConcreteConst, | ||
) -> Self::InternedConcreteConst; | ||
|
||
/// Transfer unevaluated constant expressions to the target interner. | ||
fn transfer_unevaluated_const( | ||
&self, | ||
const_unevaluated: &I::InternedUnevaluatedConst, | ||
) -> Self::InternedUnevaluatedConst; | ||
|
||
/// Transfer function ABI to the target interner. | ||
fn transfer_abi(abi: I::FnAbi) -> Self::FnAbi; | ||
} | ||
|
@@ -666,6 +692,13 @@ impl<I: Interner> TargetInterner<I> for I { | |
const_evaluated.clone() | ||
} | ||
|
||
fn transfer_unevaluated_const( | ||
&self, | ||
const_unevaluated: &I::InternedUnevaluatedConst, | ||
) -> Self::InternedUnevaluatedConst { | ||
const_unevaluated.clone() | ||
} | ||
|
||
fn transfer_abi(abi: I::FnAbi) -> Self::FnAbi { | ||
abi | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -929,8 +929,15 @@ impl<I: Interner> Const<I> { | |
ConstValue::InferenceVar(_) => false, | ||
ConstValue::Placeholder(_) => false, | ||
ConstValue::Concrete(_) => false, | ||
ConstValue::Unevaluated(_) => todo!("needs_shift for ConstValue::Unevaluated"), | ||
} | ||
} | ||
|
||
/// Tries to evaluate the constant. | ||
/// This is guaranteed to return a concrete constant or an error. | ||
pub fn try_eval(&self, interner: &I) -> Result<Const<I>, ConstEvalError> { | ||
Ok(Self::new(interner, self.data(interner).try_eval(interner)?)) | ||
} | ||
} | ||
|
||
/// Constant data, containing the constant's type and value. | ||
|
@@ -942,6 +949,21 @@ pub struct ConstData<I: Interner> { | |
pub value: ConstValue<I>, | ||
} | ||
|
||
impl<I: Interner> ConstData<I> { | ||
/// Tries to evaluate the constant. | ||
/// This is guaranteed to return a concrete constant or an error. | ||
pub fn try_eval(&self, interner: &I) -> Result<ConstData<I>, ConstEvalError> { | ||
hayashi-stl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
match &self.value { | ||
ConstValue::BoundVar(_) | ConstValue::InferenceVar(_) | ConstValue::Placeholder(_) => Err(ConstEvalError::TooGeneric), | ||
ConstValue::Concrete(_) => Ok(self.clone()), | ||
ConstValue::Unevaluated(expr) => Ok(ConstData { | ||
ty: self.ty.clone(), | ||
value: ConstValue::Concrete(expr.try_eval(&self.ty, interner)?) | ||
}), | ||
} | ||
} | ||
} | ||
|
||
/// A constant value, not necessarily concrete. | ||
#[derive(Clone, PartialEq, Eq, Hash, HasInterner)] | ||
pub enum ConstValue<I: Interner> { | ||
|
@@ -953,9 +975,14 @@ pub enum ConstValue<I: Interner> { | |
Placeholder(PlaceholderIndex), | ||
/// Concrete constant value. | ||
Concrete(ConcreteConst<I>), | ||
/// Unevaluated constant expression. | ||
Unevaluated(UnevaluatedConst<I>), | ||
} | ||
|
||
impl<I: Interner> Copy for ConstValue<I> where I::InternedConcreteConst: Copy {} | ||
impl<I: Interner> Copy for ConstValue<I> | ||
where | ||
I::InternedConcreteConst: Copy, | ||
I::InternedUnevaluatedConst: Copy, {} | ||
|
||
impl<I: Interner> ConstData<I> { | ||
/// Wraps the constant data in a `Const`. | ||
|
@@ -979,6 +1006,42 @@ impl<I: Interner> ConcreteConst<I> { | |
} | ||
} | ||
|
||
/// Unevaluated const expression, which can be evaluated | ||
#[derive(Copy, Clone, PartialEq, Eq, Hash, HasInterner)] | ||
pub struct UnevaluatedConst<I: Interner> { | ||
/// The interned expression | ||
pub interned: I::InternedUnevaluatedConst, | ||
} | ||
|
||
impl<I: Interner> UnevaluatedConst<I> { | ||
/// Attempts to evaluate the const expression. | ||
pub fn try_eval(&self, ty: &Ty<I>, interner: &I) -> Result<ConcreteConst<I>, ConstEvalError> { | ||
Ok(ConcreteConst {interned: interner.try_eval_const(&ty.interned, &self.interned)?}) | ||
} | ||
|
||
/// Checks whether two unevaluated constants are equal. | ||
pub fn const_eq(&self, ty: &Ty<I>, other: &UnevaluatedConst<I>, interner: &I) -> bool { | ||
hayashi-stl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
interner.unevaluated_const_eq(&ty.interned, &self.interned, &other.interned) | ||
} | ||
} | ||
|
||
/// An error that can occur when evaluating a const expression. | ||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] | ||
pub enum ConstEvalError { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably also want an |
||
/// A generic argument's value was not known. | ||
TooGeneric, | ||
} | ||
|
||
impl std::error::Error for ConstEvalError {} | ||
|
||
impl std::fmt::Display for ConstEvalError { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { | ||
match self { | ||
ConstEvalError::TooGeneric => write!(f, "Const expression was too generic"), | ||
} | ||
} | ||
} | ||
|
||
/// A Rust lifetime. | ||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, HasInterner)] | ||
pub struct Lifetime<I: Interner> { | ||
|
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.
Similarly here...feels like we should have already have caught this before and the answer itself (or the goal) shouldn't have unevaluated consts. (Well...maybe the goal would?)
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.
Turns out that unevaluated const expressions can appear in the answer and the goal in the resolvent if we so much as write
impl Foo<3?> for Baz {}
.