Skip to content

Commit

Permalink
Implementing basic type aliases (FuelLabs#4151)
Browse files Browse the repository at this point in the history
  • Loading branch information
mohammadfawaz authored Mar 16, 2023
1 parent 707bf2e commit 2ffb034
Show file tree
Hide file tree
Showing 110 changed files with 1,703 additions and 73 deletions.
1 change: 1 addition & 0 deletions docs/book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
- [Access Control](./blockchain-development/access_control.md)
- [Calling Contracts](./blockchain-development/calling_contracts.md)
- [Advanced Concepts](./advanced/index.md)
- [Advanced Types](./advanced/advanced_types.md)
- [Generic Types](./advanced/generic_types.md)
- [Traits](./advanced/traits.md)
- [Assembly](./advanced/assembly.md)
Expand Down
37 changes: 37 additions & 0 deletions docs/book/src/advanced/advanced_types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Advanced Types

## Creating Type Synonyms with Type Aliases

Sway provides the ability to declare a type alias to give an existing type another name. For this we use the `type` keyword. For example, we can create the alias `Kilometers` to `u64` like so:

```sway
{{#include ../../../../examples/type_aliases/src/main.sw:type_alias}}
```

Now, the alias `Kilometers` is a _synonym_ for `u64`. Note that `Kilometers` is **not** a separate new type. Values that have the type `Kilometers` will be treated the same as values of type `u64`:

```sway
{{#include ../../../../examples/type_aliases/src/main.sw:addition}}
```

Because `Kilometers` and `u64` are the same type, we can add values of both types and we can pass `Kilometers` values to functions that take `u64` parameters. However, using this method, we don’t get the type checking benefits that we get from introducing a _separate_ new type called `Kilometers`. In other words, if we mix up `Kilometers` and `i32` values somewhere, the compiler will not give us an error.

The main use case for type synonyms is to reduce repetition. For example, we might have a lengthy array type like this:

```sway
[MyStruct<u64, b256>; 5]
```

Writing this lengthy type in function signatures and as type annotations all over the code can be tiresome and error prone. Imagine having a project full of code like this:

```sway
{{#include ../../../../examples/type_aliases/src/main.sw:long_type_use}}
```

A type alias makes this code more manageable by reducing the repetition. Below, we’ve introduced an alias named `MyArray` for the verbose type and can replace all uses of the type with the shorter alias `MyArray`:

```sway
{{#include ../../../../examples/type_aliases/src/main.sw:long_type_use_shorter}}
```

This code is much easier to read and write! Choosing a meaningful name for a type alias can help communicate your intent as well.
2 changes: 1 addition & 1 deletion docs/book/src/advanced/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Advanced concepts.

- [Advanced Types](./advanced_types.md)
- [Generic Types](./generic_types.md)
- [Traits](./traits.md)
- [Trait Constraints](./trait_constraints.md)
- [Assembly](./assembly.md)
5 changes: 5 additions & 0 deletions examples/Forc.lock
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ dependencies = ['std']
name = 'tuples'
source = 'member'

[[package]]
name = 'type_aliases'
source = 'member'
dependencies = ['std']

[[package]]
name = 'vec'
source = 'member'
Expand Down
1 change: 1 addition & 0 deletions examples/Forc.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ members = [
"structs",
"subcurrency",
"tuples",
"type_aliases",
"vec",
"wallet_abi",
"wallet_contract_caller_script",
Expand Down
2 changes: 2 additions & 0 deletions examples/type_aliases/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
out
target
8 changes: 8 additions & 0 deletions examples/type_aliases/Forc.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
license = "Apache-2.0"
name = "type_aliases"

[dependencies]
std = { path = "../../sway-lib-std" }
31 changes: 31 additions & 0 deletions examples/type_aliases/src/main.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
script;

// ANCHOR: type_alias
type Kilometers = u64;
// ANCHOR_END: type_alias

struct MyStruct<T, U> {
x: T,
y: U,
}
// ANCHOR: long_type_use
fn foo_long(array: [MyStruct<u64, b256>; 5]) -> [MyStruct<u64, b256>; 5] {
array
}
// ANCHOR_END: long_type_use

// ANCHOR: long_type_use_shorter
type MyArray = [MyStruct<u64, b256>; 5];

fn foo_shorter(array: MyArray) -> MyArray {
array
}
// ANCHOR_END: long_type_use_shorter

fn main() {
// ANCHOR: addition
let x: u64 = 5;
let y: Kilometers = 5;
assert(x + y == 10);
// ANCHOR_END: addition
}
19 changes: 19 additions & 0 deletions sway-ast/src/item/item_type_alias.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use crate::priv_prelude::*;

#[derive(Clone, Debug, Serialize)]
pub struct ItemTypeAlias {
pub visibility: Option<PubToken>,
pub name: Ident,
pub type_token: TypeToken,
pub eq_token: EqToken,
pub ty: Ty,
pub semicolon_token: SemicolonToken,
}

impl Spanned for ItemTypeAlias {
fn span(&self) -> Span {
let start = self.type_token.span();
let end = self.semicolon_token.span();
Span::join(start, end)
}
}
3 changes: 3 additions & 0 deletions sway-ast/src/item/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub mod item_impl;
pub mod item_storage;
pub mod item_struct;
pub mod item_trait;
pub mod item_type_alias;
pub mod item_use;

pub type Item = Annotated<ItemKind>;
Expand Down Expand Up @@ -36,6 +37,7 @@ pub enum ItemKind {
Const(ItemConst),
Storage(ItemStorage),
Configurable(ItemConfigurable),
TypeAlias(ItemTypeAlias),
}

impl Spanned for ItemKind {
Expand All @@ -52,6 +54,7 @@ impl Spanned for ItemKind {
ItemKind::Const(item_const) => item_const.span(),
ItemKind::Storage(item_storage) => item_storage.span(),
ItemKind::Configurable(item_configurable) => item_configurable.span(),
ItemKind::TypeAlias(item_type_alias) => item_type_alias.span(),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions sway-ast/src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ define_keyword!(FalseToken, "false");
define_keyword!(BreakToken, "break");
define_keyword!(ContinueToken, "continue");
define_keyword!(ConfigurableToken, "configurable");
define_keyword!(TypeToken, "type");

/// The type is a keyword.
pub trait Token: Spanned + Sized {
Expand Down
1 change: 1 addition & 0 deletions sway-ast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub use crate::{
item_storage::{ItemStorage, StorageField},
item_struct::ItemStruct,
item_trait::{ItemTrait, ItemTraitItem, Traits},
item_type_alias::ItemTypeAlias,
item_use::{ItemUse, UseTree},
FnArg, FnArgs, FnSignature, Item, ItemKind, TypeField,
},
Expand Down
1 change: 1 addition & 0 deletions sway-ast/src/priv_prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub use {
item_storage::ItemStorage,
item_struct::ItemStruct,
item_trait::{ItemTrait, Traits},
item_type_alias::ItemTypeAlias,
item_use::ItemUse,
FnSignature, Item, ItemKind, TypeField,
},
Expand Down
1 change: 1 addition & 0 deletions sway-core/src/abi_generation/evm_json_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ pub fn json_abi_str(
Storage { .. } => "contract storage".into(),
RawUntypedPtr => "raw untyped ptr".into(),
RawUntypedSlice => "raw untyped slice".into(),
Alias { ty, .. } => json_abi_str_type_arg(ty, type_engine, decl_engine),
}
}

Expand Down
18 changes: 18 additions & 0 deletions sway-core/src/abi_generation/fuel_json_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,9 @@ impl TypeId {
(TypeInfo::Custom { .. }, TypeInfo::Enum { .. }) => type_engine
.get(resolved_type_id)
.json_abi_str(ctx, type_engine, decl_engine),
(TypeInfo::Custom { .. }, TypeInfo::Alias { .. }) => type_engine
.get(resolved_type_id)
.json_abi_str(ctx, type_engine, decl_engine),
(TypeInfo::Tuple(fields), TypeInfo::Tuple(resolved_fields)) => {
assert_eq!(fields.len(), resolved_fields.len());
let field_strs = fields
Expand Down Expand Up @@ -593,6 +596,20 @@ impl TypeId {
None
}
}
TypeInfo::Alias { .. } => {
if let TypeInfo::Alias { ty, .. } = type_engine.get(resolved_type_id) {
ty.initial_type_id.get_json_type_components(
ctx,
type_engine,
decl_engine,
types,
ty.type_id,
)
} else {
None
}
}

_ => None,
}
}
Expand Down Expand Up @@ -820,6 +837,7 @@ impl TypeInfo {
Storage { .. } => "contract storage".into(),
RawUntypedPtr => "raw untyped ptr".into(),
RawUntypedSlice => "raw untyped slice".into(),
Alias { ty, .. } => ty.json_abi_str(ctx, type_engine, decl_engine),
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ fn connect_declaration<'eng: 'cfg, 'cfg>(
| StructDeclaration { .. }
| EnumDeclaration { .. }
| StorageDeclaration { .. }
| TypeAliasDeclaration { .. }
| GenericTypeForFunctionScope { .. } => Ok(leaves.to_vec()),
VariableDeclaration(_) | ConstantDeclaration { .. } => {
let entry_node = graph.add_node(ControlFlowGraphNode::from_node(node));
Expand Down
24 changes: 20 additions & 4 deletions sway-core/src/control_flow_analysis/dead_code_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,10 @@ fn connect_declaration<'eng: 'cfg, 'cfg>(
connect_storage_declaration(&storage, graph, entry_node, tree_type);
Ok(leaves.to_vec())
}
TypeAliasDeclaration { .. } => {
// TODO - handle type aliases properly. For now, always skip DCA for them.
Ok(leaves.to_vec())
}
ErrorRecovery(_) | GenericTypeForFunctionScope { .. } => Ok(leaves.to_vec()),
}
}
Expand Down Expand Up @@ -1236,11 +1240,19 @@ fn connect_expression<'eng: 'cfg, 'cfg>(
.to_typeinfo(*resolved_type_of_parent, &field_to_access.span)
.unwrap_or_else(|_| TypeInfo::Tuple(Vec::new()));

assert!(matches!(resolved_type_of_parent, TypeInfo::Struct { .. }));
let resolved_type_of_parent = match resolved_type_of_parent {
TypeInfo::Struct(decl_ref) => decl_engine.get_struct(&decl_ref).call_path,
_ => panic!("Called subfield on a non-struct"),
let resolved_type_of_parent = match resolved_type_of_parent
.expect_struct(engines, field_instantiation_span)
.value
{
Some(struct_decl_ref) => decl_engine.get_struct(&struct_decl_ref).call_path,
None => {
return Err(CompileError::Internal(
"Called subfield on a non-struct",
field_instantiation_span.clone(),
))
}
};

let field_name = &field_to_access.name;
// find the struct field index in the namespace
let field_ix = match graph
Expand Down Expand Up @@ -1877,6 +1889,10 @@ fn allow_dead_code_ast_node(decl_engine: &DeclEngine, node: &ty::TyAstNode) -> b
ty::TyDeclaration::EnumDeclaration { decl_id, .. } => {
allow_dead_code(decl_engine.get_enum(decl_id).attributes)
}
ty::TyDeclaration::TypeAliasDeclaration { .. } => {
// TODO - handle type aliases properly. For now, always skip DCA for them.
true
}
ty::TyDeclaration::ImplTrait { .. } => false,
ty::TyDeclaration::AbiDeclaration { .. } => false,
ty::TyDeclaration::GenericTypeForFunctionScope { .. } => false,
Expand Down
10 changes: 10 additions & 0 deletions sway-core/src/decl_engine/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::{
language::ty::{
self, TyAbiDeclaration, TyConstantDeclaration, TyEnumDeclaration, TyFunctionDeclaration,
TyImplTrait, TyStorageDeclaration, TyStructDeclaration, TyTraitDeclaration, TyTraitFn,
TyTypeAliasDeclaration,
},
};

Expand All @@ -27,6 +28,7 @@ pub struct DeclEngine {
abi_slab: ConcurrentSlab<TyAbiDeclaration>,
constant_slab: ConcurrentSlab<TyConstantDeclaration>,
enum_slab: ConcurrentSlab<TyEnumDeclaration>,
type_alias_slab: ConcurrentSlab<TyTypeAliasDeclaration>,

parents: RwLock<HashMap<FunctionalDeclId, Vec<FunctionalDeclId>>>,
}
Expand Down Expand Up @@ -71,6 +73,7 @@ decl_engine_index!(storage_slab, ty::TyStorageDeclaration);
decl_engine_index!(abi_slab, ty::TyAbiDeclaration);
decl_engine_index!(constant_slab, ty::TyConstantDeclaration);
decl_engine_index!(enum_slab, ty::TyEnumDeclaration);
decl_engine_index!(type_alias_slab, ty::TyTypeAliasDeclaration);

impl DeclEngine {
/// Given a [DeclRef] `index`, finds all the parents of `index` and all the
Expand Down Expand Up @@ -192,4 +195,11 @@ impl DeclEngine {
{
self.enum_slab.get(DeclId::from(index).inner())
}

pub fn get_type_alias<'a, T, I>(&self, index: &'a T) -> ty::TyTypeAliasDeclaration
where
DeclId<I>: From<&'a T>,
{
self.type_alias_slab.get(DeclId::from(index).inner())
}
}
18 changes: 17 additions & 1 deletion sway-core/src/decl_engine/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
engine_threading::*,
language::ty::{
TyEnumDeclaration, TyFunctionDeclaration, TyImplTrait, TyStructDeclaration,
TyTraitDeclaration, TyTraitFn,
TyTraitDeclaration, TyTraitFn, TyTypeAliasDeclaration,
},
type_system::*,
};
Expand Down Expand Up @@ -135,6 +135,14 @@ impl SubstTypes for DeclId<TyEnumDeclaration> {
decl_engine.replace(*self, decl);
}
}
impl SubstTypes for DeclId<TyTypeAliasDeclaration> {
fn subst_inner(&mut self, type_mapping: &TypeSubstMap, engines: Engines<'_>) {
let decl_engine = engines.de();
let mut decl = decl_engine.get(*self);
decl.subst(type_mapping, engines);
decl_engine.replace(*self, decl);
}
}

impl ReplaceSelfType for DeclId<TyFunctionDeclaration> {
fn replace_self_type(&mut self, engines: Engines<'_>, self_type: TypeId) {
Expand Down Expand Up @@ -184,3 +192,11 @@ impl ReplaceSelfType for DeclId<TyEnumDeclaration> {
decl_engine.replace(*self, decl);
}
}
impl ReplaceSelfType for DeclId<TyTypeAliasDeclaration> {
fn replace_self_type(&mut self, engines: Engines<'_>, self_type: TypeId) {
let decl_engine = engines.de();
let mut decl = decl_engine.get(*self);
decl.replace_self_type(engines, self_type);
decl_engine.replace(*self, decl);
}
}
1 change: 1 addition & 0 deletions sway-core/src/ir_generation/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ fn compile_declarations(
| ty::TyDeclaration::AbiDeclaration { .. }
| ty::TyDeclaration::GenericTypeForFunctionScope { .. }
| ty::TyDeclaration::StorageDeclaration { .. }
| ty::TyDeclaration::TypeAliasDeclaration { .. }
| ty::TyDeclaration::ErrorRecovery(_) => (),
}
}
Expand Down
3 changes: 3 additions & 0 deletions sway-core/src/ir_generation/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ fn convert_resolved_type(
}
TypeInfo::RawUntypedPtr => Type::get_uint64(context),
TypeInfo::RawUntypedSlice => Type::get_slice(context),
TypeInfo::Alias { ty, .. } => {
convert_resolved_typeid(type_engine, decl_engine, context, &ty.type_id, span)?
}

// Unsupported types which shouldn't exist in the AST after type checking and
// monomorphisation.
Expand Down
6 changes: 6 additions & 0 deletions sway-core/src/ir_generation/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,12 @@ impl<'eng> FnCompiler<'eng> {
span: ast_node.span.clone(),
})
}
ty::TyDeclaration::TypeAliasDeclaration { .. } => {
Err(CompileError::UnexpectedDeclaration {
decl_type: "type alias",
span: ast_node.span.clone(),
})
}
},
ty::TyAstNodeContent::Expression(te) => {
// An expression with an ignored return value... I assume.
Expand Down
Loading

0 comments on commit 2ffb034

Please sign in to comment.