Skip to content

Commit

Permalink
Implement Display for TypeIds; Add type parameters to the definit…
Browse files Browse the repository at this point in the history
…ion of custom types (FuelLabs#1534)

Include type_parameters in the definition of a struct or enum in the type engine. This allows for handling of generic types where the type is not necessarily used in the fields or variants.
Change TypeId from a type alias to a newtype. This provides greater type safety but also allowed me to implement Display for it. Now when you print a TypeId, instead of the numeric value in the type engine, you'll see the actual type it represents.
Do a bit of refactoring/code cleanup in places.
  • Loading branch information
sezna authored May 13, 2022
1 parent 2b6e938 commit 197e640
Show file tree
Hide file tree
Showing 13 changed files with 295 additions and 173 deletions.
2 changes: 1 addition & 1 deletion sway-core/src/asm_generation/expression/contract_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ pub(crate) fn convert_contract_call_to_asm(
struct_name: Ident::new_with_override("bundled_arguments", span.clone()),
fields: typed_fields_buf,
},
return_type: 0,
return_type: 0.into(),
is_constant: IsConstant::No,
span: span.clone(),
})
Expand Down
1 change: 1 addition & 0 deletions sway-core/src/asm_generation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1307,6 +1307,7 @@ fn compile_contract_to_selectors(
// create a register for it and load it using some utilities from expression::subfield.
let bundled_arguments_type = crate::type_engine::insert_type(TypeInfo::Struct {
name: Ident::new(bundled_arguments_span),
type_parameters: Default::default(),
fields: decl
.parameters
.iter()
Expand Down
15 changes: 8 additions & 7 deletions sway-core/src/concurrent_slab.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::type_engine::TypeId;
use std::sync::RwLock;

#[derive(Debug, Default)]
Expand All @@ -9,25 +10,25 @@ impl<T> ConcurrentSlab<T>
where
T: Clone + PartialEq,
{
pub fn insert(&self, value: T) -> usize {
pub fn insert(&self, value: T) -> TypeId {
let mut inner = self.inner.write().unwrap();
let ret = inner.len();
inner.push(value);
ret
ret.into()
}

pub fn get(&self, index: usize) -> T {
pub fn get(&self, index: TypeId) -> T {
let inner = self.inner.read().unwrap();
inner[index].clone()
inner[*index].clone()
}

pub fn replace(&self, index: usize, prev_value: &T, new_value: T) -> Option<T> {
pub fn replace(&self, index: TypeId, prev_value: &T, new_value: T) -> Option<T> {
let mut inner = self.inner.write().unwrap();
let actual_prev_value = &inner[index];
let actual_prev_value = &inner[*index];
if actual_prev_value != prev_value {
return Some(actual_prev_value.clone());
}
inner[index] = new_value;
inner[*index] = new_value;
None
}
}
2 changes: 1 addition & 1 deletion sway-core/src/optimize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2270,7 +2270,7 @@ fn get_struct_name_and_field_index(
resolve_type(field_type, field_name.span())
.ok()
.and_then(|ty_info| match ty_info {
TypeInfo::Struct { name, fields } => Some((
TypeInfo::Struct { name, fields, .. } => Some((
name.as_str().to_owned(),
fields
.iter()
Expand Down
4 changes: 4 additions & 0 deletions sway-core/src/parse_tree/declaration/type_parameter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ impl TypeParameter {
ok(params, warnings, errors)
}

pub fn span(&self) -> Span {
self.name_ident.span().clone()
}

fn parse_from_type_params(
type_params_pair: Pair<Rule>,
config: Option<&BuildConfig>,
Expand Down
172 changes: 21 additions & 151 deletions sway-core/src/semantic_analysis/ast_node/declaration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ use sway_types::Span;

mod function;
mod storage;
mod r#struct;
mod variable;
pub use function::*;
pub use r#struct::*;
pub use storage::*;
pub use variable::*;

Expand Down Expand Up @@ -258,155 +260,6 @@ impl TypedAbiDeclaration {
}
}

#[derive(Clone, Debug, Eq)]
pub struct TypedStructDeclaration {
pub(crate) name: Ident,
pub(crate) fields: Vec<TypedStructField>,
pub(crate) type_parameters: Vec<TypeParameter>,
pub(crate) visibility: Visibility,
pub(crate) span: Span,
}

// NOTE: Hash and PartialEq must uphold the invariant:
// k1 == k2 -> hash(k1) == hash(k2)
// https://doc.rust-lang.org/std/collections/struct.HashMap.html
impl PartialEq for TypedStructDeclaration {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
&& self.fields == other.fields
&& self.type_parameters == other.type_parameters
&& self.visibility == other.visibility
}
}

impl TypedStructDeclaration {
pub(crate) fn monomorphize(
&self,
namespace: &mut namespace::Items,
type_arguments: &[TypeArgument],
self_type: Option<TypeId>,
) -> CompileResult<Self> {
let mut warnings = vec![];
let mut errors = vec![];
let type_mapping = insert_type_parameters(&self.type_parameters);
let new_decl = Self::monomorphize_inner(self, namespace, &type_mapping);
let type_arguments_span = type_arguments
.iter()
.map(|x| x.span.clone())
.reduce(Span::join)
.unwrap_or_else(|| self.span.clone());
if !type_arguments.is_empty() {
if type_mapping.len() != type_arguments.len() {
errors.push(CompileError::IncorrectNumberOfTypeArguments {
given: type_arguments.len(),
expected: type_mapping.len(),
span: type_arguments_span,
});
return err(warnings, errors);
}
for ((_, interim_type), type_argument) in type_mapping.iter().zip(type_arguments.iter())
{
match self_type {
Some(self_type) => {
let (mut new_warnings, new_errors) = unify_with_self(
*interim_type,
type_argument.type_id,
self_type,
&type_argument.span,
"Type argument is not assignable to generic type parameter.",
);
warnings.append(&mut new_warnings);
errors.append(&mut new_errors.into_iter().map(|x| x.into()).collect());
}
None => {
let (mut new_warnings, new_errors) = unify(
*interim_type,
type_argument.type_id,
&type_argument.span,
"Type argument is not assignable to generic type parameter.",
);
warnings.append(&mut new_warnings);
errors.append(&mut new_errors.into_iter().map(|x| x.into()).collect());
}
}
}
}
ok(new_decl, warnings, errors)
}

fn monomorphize_inner(
&self,
namespace: &mut namespace::Items,
type_mapping: &[(TypeParameter, usize)],
) -> Self {
let old_type_id = self.type_id();
let mut new_decl = self.clone();
new_decl.copy_types(type_mapping);
namespace.copy_methods_to_type(
look_up_type_id(old_type_id),
look_up_type_id(new_decl.type_id()),
type_mapping,
);
new_decl
}

pub(crate) fn copy_types(&mut self, type_mapping: &[(TypeParameter, TypeId)]) {
self.fields
.iter_mut()
.for_each(|x| x.copy_types(type_mapping));
}

pub(crate) fn type_id(&self) -> TypeId {
insert_type(TypeInfo::Struct {
name: self.name.clone(),
fields: self.fields.clone(),
})
}
}

#[derive(Debug, Clone, Eq)]
pub struct TypedStructField {
pub(crate) name: Ident,
pub(crate) r#type: TypeId,
pub(crate) span: Span,
}

// NOTE: Hash and PartialEq must uphold the invariant:
// k1 == k2 -> hash(k1) == hash(k2)
// https://doc.rust-lang.org/std/collections/struct.HashMap.html
impl Hash for TypedStructField {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name.hash(state);
look_up_type_id(self.r#type).hash(state);
}
}

// NOTE: Hash and PartialEq must uphold the invariant:
// k1 == k2 -> hash(k1) == hash(k2)
// https://doc.rust-lang.org/std/collections/struct.HashMap.html
impl PartialEq for TypedStructField {
fn eq(&self, other: &Self) -> bool {
self.name == other.name && look_up_type_id(self.r#type) == look_up_type_id(other.r#type)
}
}

impl TypedStructField {
pub fn generate_json_abi(&self) -> Property {
Property {
name: self.name.to_string(),
type_field: self.r#type.json_abi_str(),
components: self.r#type.generate_json_abi(),
}
}

pub(crate) fn copy_types(&mut self, type_mapping: &[(TypeParameter, TypeId)]) {
self.r#type = match look_up_type_id(self.r#type).matches_type_parameter(type_mapping) {
Some(matching_id) => insert_type(TypeInfo::Ref(matching_id)),
None => insert_type(look_up_type_id_raw(self.r#type)),
};
}
}

#[derive(Clone, Debug, Eq)]
pub struct TypedEnumDeclaration {
pub(crate) name: Ident,
Expand Down Expand Up @@ -443,7 +296,7 @@ impl TypedEnumDeclaration {
let mut warnings = vec![];
let mut errors = vec![];
let type_mapping = insert_type_parameters(&self.type_parameters);
let new_decl = Self::monomorphize_inner(self, namespace, &type_mapping);
let mut new_decl = Self::monomorphize_inner(self, namespace, &type_mapping);
let type_arguments_span = type_arguments
.iter()
.map(|x| x.span.clone())
Expand Down Expand Up @@ -482,13 +335,29 @@ impl TypedEnumDeclaration {
}
}
}
// associate the type arguments with the parameters in the struct decl
new_decl
.type_parameters
.iter_mut()
.zip(type_arguments.iter())
.for_each(
|(
TypeParameter {
ref mut type_id, ..
},
arg,
)| {
*type_id = arg.type_id;
},
);
// perform the monomorphization
ok(new_decl, warnings, errors)
}

fn monomorphize_inner(
&self,
namespace: &mut namespace::Items,
type_mapping: &[(TypeParameter, usize)],
type_mapping: &[(TypeParameter, TypeId)],
) -> Self {
let old_type_id = self.type_id();
let mut new_decl = self.clone();
Expand All @@ -511,6 +380,7 @@ impl TypedEnumDeclaration {
insert_type(TypeInfo::Enum {
name: self.name.clone(),
variant_types: self.variants.clone(),
type_parameters: self.type_parameters.clone(),
})
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ fn test_function_selector_behavior() {
},
parameters: vec![],
span: Span::dummy(),
return_type: 0,
return_type: 0.into(),
type_parameters: vec![],
return_type_span: Span::dummy(),
visibility: Visibility::Public,
Expand Down Expand Up @@ -475,7 +475,7 @@ fn test_function_selector_behavior() {
},
],
span: Span::dummy(),
return_type: 0,
return_type: 0.into(),
type_parameters: vec![],
return_type_span: Span::dummy(),
visibility: Visibility::Public,
Expand Down
Loading

0 comments on commit 197e640

Please sign in to comment.