-
Notifications
You must be signed in to change notification settings - Fork 6
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
feat: Emulate TypeBound
s on parameters via constraints.
#1624
base: main
Are you sure you want to change the base?
Changes from 5 commits
b9e75fc
0293340
1cba000
f22581e
d377fcc
7d0f344
719af15
a05ae29
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 |
---|---|---|
|
@@ -116,7 +116,7 @@ struct Context<'a> { | |
nodes: FxHashMap<model::NodeId, Node>, | ||
|
||
/// The types of the local variables that are currently in scope. | ||
local_variables: FxIndexMap<&'a str, model::TermId>, | ||
local_variables: FxIndexMap<&'a str, LocalVar>, | ||
|
||
custom_name_cache: FxHashMap<&'a str, (ExtensionId, SmolStr)>, | ||
} | ||
|
@@ -159,16 +159,16 @@ impl<'a> Context<'a> { | |
fn resolve_local_ref( | ||
&self, | ||
local_ref: &model::LocalRef, | ||
) -> Result<(usize, model::TermId), ImportError> { | ||
) -> Result<(usize, LocalVar), ImportError> { | ||
let term = match local_ref { | ||
model::LocalRef::Index(_, index) => self | ||
.local_variables | ||
.get_index(*index as usize) | ||
.map(|(_, term)| (*index as usize, *term)), | ||
.map(|(_, v)| (*index as usize, *v)), | ||
model::LocalRef::Named(name) => self | ||
.local_variables | ||
.get_full(name) | ||
.map(|(index, _, term)| (index, *term)), | ||
.map(|(index, _, v)| (index, *v)), | ||
}; | ||
|
||
term.ok_or_else(|| model::ModelError::InvalidLocal(local_ref.to_string()).into()) | ||
|
@@ -892,41 +892,59 @@ impl<'a> Context<'a> { | |
self.with_local_socpe(|ctx| { | ||
let mut imported_params = Vec::with_capacity(decl.params.len()); | ||
|
||
for param in decl.params { | ||
// TODO: `PolyFuncType` should be able to handle constraints | ||
// and distinguish between implicit and explicit parameters. | ||
match param { | ||
model::Param::Implicit { name, r#type } => { | ||
imported_params.push(ctx.import_type_param(*r#type)?); | ||
ctx.local_variables.insert(name, *r#type); | ||
} | ||
model::Param::Explicit { name, r#type } => { | ||
imported_params.push(ctx.import_type_param(*r#type)?); | ||
ctx.local_variables.insert(name, *r#type); | ||
ctx.local_variables.extend( | ||
decl.params | ||
.iter() | ||
.map(|param| (param.name, LocalVar::new(param.r#type))), | ||
); | ||
|
||
for constraint in decl.constraints { | ||
match ctx.get_term(*constraint)? { | ||
model::Term::CopyConstraint { term } => { | ||
let model::Term::Var(var) = ctx.get_term(*term)? else { | ||
return Err(error_unsupported!( | ||
"constraint on term that is not a variable" | ||
)); | ||
}; | ||
|
||
let var = ctx.resolve_local_ref(var)?.0; | ||
ctx.local_variables.get_index_mut(var).unwrap().1.copy = true; | ||
} | ||
model::Param::Constraint { constraint: _ } => { | ||
return Err(error_unsupported!("constraints")); | ||
model::Term::DiscardConstraint { term } => { | ||
let model::Term::Var(var) = ctx.get_term(*term)? else { | ||
return Err(error_unsupported!( | ||
"constraint on term that is not a variable" | ||
)); | ||
}; | ||
|
||
let var = ctx.resolve_local_ref(var)?.0; | ||
ctx.local_variables.get_index_mut(var).unwrap().1.discard = true; | ||
} | ||
_ => return Err(error_unsupported!("constraint other than copy or discard")), | ||
} | ||
} | ||
|
||
for (index, param) in decl.params.iter().enumerate() { | ||
// TODO: `PolyFuncType` should be able to distinguish between implicit and explicit parameters. | ||
let bound = ctx.local_variables.get_index(index).unwrap().1.bound()?; | ||
imported_params.push(ctx.import_type_param(param.r#type, bound)?); | ||
} | ||
|
||
let body = ctx.import_func_type::<RV>(decl.signature)?; | ||
in_scope(ctx, PolyFuncTypeBase::new(imported_params, body)) | ||
}) | ||
} | ||
|
||
/// Import a [`TypeParam`] from a term that represents a static type. | ||
fn import_type_param(&mut self, term_id: model::TermId) -> Result<TypeParam, ImportError> { | ||
fn import_type_param( | ||
&mut self, | ||
term_id: model::TermId, | ||
bound: TypeBound, | ||
) -> Result<TypeParam, ImportError> { | ||
match self.get_term(term_id)? { | ||
model::Term::Wildcard => Err(error_uninferred!("wildcard")), | ||
|
||
model::Term::Type => { | ||
// As part of the migration from `TypeBound`s to constraints, we pretend that all | ||
// `TypeBound`s are copyable. | ||
Ok(TypeParam::Type { | ||
b: TypeBound::Copyable, | ||
}) | ||
} | ||
model::Term::Type => Ok(TypeParam::Type { b: bound }), | ||
|
||
model::Term::StaticType => Err(error_unsupported!("`type` as `TypeParam`")), | ||
model::Term::Constraint => Err(error_unsupported!("`constraint` as `TypeParam`")), | ||
|
@@ -938,7 +956,7 @@ impl<'a> Context<'a> { | |
model::Term::FuncType { .. } => Err(error_unsupported!("`(fn ...)` as `TypeParam`")), | ||
|
||
model::Term::ListType { item_type } => { | ||
let param = Box::new(self.import_type_param(*item_type)?); | ||
let param = Box::new(self.import_type_param(*item_type, TypeBound::Any)?); | ||
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. Not sure why this is any (also not covered by tests), is this a todo waiting for a list constraint? 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. The item type is nested within the list and since we only constrain parameters directly here, we can not express that the items of the list should be copyable. Therefore the |
||
Ok(TypeParam::List { param }) | ||
} | ||
|
||
|
@@ -952,15 +970,19 @@ impl<'a> Context<'a> { | |
| model::Term::List { .. } | ||
| model::Term::ExtSet { .. } | ||
| model::Term::Adt { .. } | ||
| model::Term::Control { .. } => Err(model::ModelError::TypeError(term_id).into()), | ||
| model::Term::Control { .. } | ||
| model::Term::CopyConstraint { .. } | ||
| model::Term::DiscardConstraint { .. } => { | ||
Err(model::ModelError::TypeError(term_id).into()) | ||
} | ||
|
||
model::Term::ControlType => { | ||
Err(error_unsupported!("type of control types as `TypeParam`")) | ||
} | ||
} | ||
} | ||
|
||
/// Import a `TypeArg` froma term that represents a static type or value. | ||
/// Import a `TypeArg` from a term that represents a static type or value. | ||
fn import_type_arg(&mut self, term_id: model::TermId) -> Result<TypeArg, ImportError> { | ||
match self.get_term(term_id)? { | ||
model::Term::Wildcard => Err(error_uninferred!("wildcard")), | ||
|
@@ -969,8 +991,9 @@ impl<'a> Context<'a> { | |
} | ||
|
||
model::Term::Var(var) => { | ||
let (index, var_type) = self.resolve_local_ref(var)?; | ||
let decl = self.import_type_param(var_type)?; | ||
let (index, var) = self.resolve_local_ref(var)?; | ||
let bound = var.bound()?; | ||
let decl = self.import_type_param(var.r#type, bound)?; | ||
Ok(TypeArg::new_var_use(index, decl)) | ||
} | ||
|
||
|
@@ -1008,7 +1031,11 @@ impl<'a> Context<'a> { | |
|
||
model::Term::FuncType { .. } | ||
| model::Term::Adt { .. } | ||
| model::Term::Control { .. } => Err(model::ModelError::TypeError(term_id).into()), | ||
| model::Term::Control { .. } | ||
| model::Term::CopyConstraint { .. } | ||
| model::Term::DiscardConstraint { .. } => { | ||
Err(model::ModelError::TypeError(term_id).into()) | ||
} | ||
} | ||
} | ||
|
||
|
@@ -1109,7 +1136,11 @@ impl<'a> Context<'a> { | |
| model::Term::List { .. } | ||
| model::Term::Control { .. } | ||
| model::Term::ControlType | ||
| model::Term::Nat(_) => Err(model::ModelError::TypeError(term_id).into()), | ||
| model::Term::Nat(_) | ||
| model::Term::DiscardConstraint { .. } | ||
| model::Term::CopyConstraint { .. } => { | ||
Err(model::ModelError::TypeError(term_id).into()) | ||
} | ||
} | ||
} | ||
|
||
|
@@ -1285,3 +1316,33 @@ impl<'a> Names<'a> { | |
Ok(Self { items }) | ||
} | ||
} | ||
|
||
#[derive(Debug, Clone, Copy)] | ||
struct LocalVar { | ||
r#type: model::TermId, | ||
copy: bool, | ||
discard: bool, | ||
} | ||
|
||
impl LocalVar { | ||
pub fn new(r#type: model::TermId) -> Self { | ||
Self { | ||
r#type, | ||
copy: false, | ||
discard: false, | ||
} | ||
} | ||
|
||
pub fn bound(&self) -> Result<TypeBound, ImportError> { | ||
match (self.copy, self.discard) { | ||
(true, true) => Ok(TypeBound::Copyable), | ||
(false, false) => Ok(TypeBound::Any), | ||
(true, false) => Err(error_unsupported!( | ||
"type that is copyable but not discardable" | ||
)), | ||
(false, true) => Err(error_unsupported!( | ||
"type that is discardable but not copyable" | ||
)), | ||
} | ||
} | ||
} |
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.
Is
local scope
==[Self::local_scope]
? Do we actually plan to modify the local scope by adding these? Or are these "Constraints to be added to those from the local scope" (i.e., extra constraints from somewhere else)? "Constraints in addition to those from the local scope" would be clearer still that we don't intend to mutate some list.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.
When exporting a
PolyFuncType
, we gather constraints inlocal_constraints
.PolyFuncType
itself can not express constraints, but if one of the parameters takes a copyable type,local_constraints
is where we record the corresponding constraint for that parameter.