Skip to content

Commit

Permalink
Merge cfb293c into 65c1927
Browse files Browse the repository at this point in the history
  • Loading branch information
esdrubal authored Feb 16, 2024
2 parents 65c1927 + cfb293c commit 057388c
Show file tree
Hide file tree
Showing 43 changed files with 269 additions and 430 deletions.
1 change: 1 addition & 0 deletions docs/book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
- [Associated Types](./advanced/associated_types.md)
- [Generics and Trait Constraints](./advanced/generics_and_trait_constraints.md)
- [Assembly](./advanced/assembly.md)
- [Never Type](./advanced/never_type.md)
- [Common Collections](./common-collections/index.md)
- [Vectors on the Heap](./common-collections/vec.md)
- [Storage Vectors](./common-collections/storage_vec.md)
Expand Down
1 change: 1 addition & 0 deletions docs/book/src/advanced/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ Advanced concepts.
- [Associated Types](./associated_types.md)
- [Generics and Trait Constraints](./generics_and_trait_constraints.md)
- [Assembly](./assembly.md)
- [Never Type](./never_type.md)
53 changes: 53 additions & 0 deletions docs/book/src/advanced/never_type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Never Type

The Never type `!` represents the type of computations which never resolve to any value at all.

## Additional Information

`break`, `continue` and `return` expressions also have type `!`. For example we are allowed to
write:

```sway
let x: ! = {
return 123
};
```

Although the `let` is pointless here, it illustrates the meaning of `!`. Since `x` is never
assigned a value (because `return` returns from the entire function), `x` can be given type
`Never`. We could also replace `return 123` with a `revert()` or a never-ending `loop` and this code
would still be valid.

A more realistic usage of `Never` is in this code:

```sway
let num: u32 = match get_a_number() {
Some(num) => num,
None => break,
};
```

Both match arms must produce values of type [`u32`], but since `break` never produces a value
at all we know it can never produce a value which isn't a [`u32`]. This illustrates another
behaviour of the `!` type - expressions with type `!` will coerce into any other type.

Note that `!` type coerces into any other type, another example of this would be:

```sway
let x: u32 = {
return 123
};
```

Regardless of the type of `x`, the return block of type `Never` will always coerce into `x` type.

## Examples

```sway
fn foo() {
let num: u64 = match Option::None::<u64> {
Some(num) => num,
None => return,
};
}
```
4 changes: 1 addition & 3 deletions forc-plugins/forc-doc/src/tests/expects/impl_trait/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ fn test_impl_traits_default() {
assert_search_js(
&doc_path,
&expect![[r#"
var SEARCH_INDEX={"core":[{"html_filename":"trait.AsRawSlice.html","module_info":["core","raw_slice"],"name":"AsRawSlice","preview":"Trait to return a type as a <code>raw_slice</code>.\n","type_name":"trait"},{"html_filename":"fn.from_str_array.html","module_info":["core","str"],"name":"from_str_array","preview":"","type_name":"function"},{"html_filename":"trait.Add.html","module_info":["core","ops"],"name":"Add","preview":"Trait for the addition of two values.\n","type_name":"trait"},{"html_filename":"trait.Subtract.html","module_info":["core","ops"],"name":"Subtract","preview":"Trait for the subtraction of two values.\n","type_name":"trait"},{"html_filename":"trait.Multiply.html","module_info":["core","ops"],"name":"Multiply","preview":"Trait for the multiplication of two values.\n","type_name":"trait"},{"html_filename":"trait.Divide.html","module_info":["core","ops"],"name":"Divide","preview":"Trait for the division of two values.\n","type_name":"trait"},{"html_filename":"trait.Mod.html","module_info":["core","ops"],"name":"Mod","preview":"Trait for the modulo of two values.\n","type_name":"trait"},{"html_filename":"trait.Not.html","module_info":["core","ops"],"name":"Not","preview":"Trait to invert a type.\n","type_name":"trait"},{"html_filename":"trait.Eq.html","module_info":["core","ops"],"name":"Eq","preview":"Trait to evaluate if two types are equal.\n","type_name":"trait"},{"html_filename":"trait.Ord.html","module_info":["core","ops"],"name":"Ord","preview":"Trait to evaluate if one value is greater or less than another of the same type.\n","type_name":"trait"},{"html_filename":"trait.BitwiseAnd.html","module_info":["core","ops"],"name":"BitwiseAnd","preview":"Trait to bitwise AND two values of the same type.\n","type_name":"trait"},{"html_filename":"trait.BitwiseOr.html","module_info":["core","ops"],"name":"BitwiseOr","preview":"Trait to bitwise OR two values of the same type.\n","type_name":"trait"},{"html_filename":"trait.BitwiseXor.html","module_info":["core","ops"],"name":"BitwiseXor","preview":"Trait to bitwise XOR two values of the same type.\n","type_name":"trait"},{"html_filename":"trait.Shift.html","module_info":["core","ops"],"name":"Shift","preview":"Trait to bit shift a value.\n","type_name":"trait"},{"html_filename":"enum.Never.html","module_info":["core","never"],"name":"Never","preview":"<code>Never</code> represents the type of computations which never resolve to any value at all.","type_name":"enum"},{"html_filename":"struct.StorageKey.html","module_info":["core","storage"],"name":"StorageKey","preview":"Describes a location in storage.\n","type_name":"struct"},{"html_filename":"struct.Buffer.html","module_info":["core","codec"],"name":"Buffer","preview":"","type_name":"struct"},{"html_filename":"trait.AbiEncode.html","module_info":["core","codec"],"name":"AbiEncode","preview":"","type_name":"trait"},{"html_filename":"fn.encode.html","module_info":["core","codec"],"name":"encode","preview":"","type_name":"function"}],"impl_traits":[{"html_filename":"trait.Foo.html","module_info":["impl_traits","foo"],"name":"Foo","preview":"","type_name":"trait"},{"html_filename":"trait.Baz.html","module_info":["impl_traits","foo"],"name":"Baz","preview":"","type_name":"trait"},{"html_filename":"struct.Bar.html","module_info":["impl_traits","bar"],"name":"Bar","preview":"","type_name":"struct"}]};
var SEARCH_INDEX={"core":[{"html_filename":"trait.AsRawSlice.html","module_info":["core","raw_slice"],"name":"AsRawSlice","preview":"Trait to return a type as a <code>raw_slice</code>.\n","type_name":"trait"},{"html_filename":"fn.from_str_array.html","module_info":["core","str"],"name":"from_str_array","preview":"","type_name":"function"},{"html_filename":"trait.Add.html","module_info":["core","ops"],"name":"Add","preview":"Trait for the addition of two values.\n","type_name":"trait"},{"html_filename":"trait.Subtract.html","module_info":["core","ops"],"name":"Subtract","preview":"Trait for the subtraction of two values.\n","type_name":"trait"},{"html_filename":"trait.Multiply.html","module_info":["core","ops"],"name":"Multiply","preview":"Trait for the multiplication of two values.\n","type_name":"trait"},{"html_filename":"trait.Divide.html","module_info":["core","ops"],"name":"Divide","preview":"Trait for the division of two values.\n","type_name":"trait"},{"html_filename":"trait.Mod.html","module_info":["core","ops"],"name":"Mod","preview":"Trait for the modulo of two values.\n","type_name":"trait"},{"html_filename":"trait.Not.html","module_info":["core","ops"],"name":"Not","preview":"Trait to invert a type.\n","type_name":"trait"},{"html_filename":"trait.Eq.html","module_info":["core","ops"],"name":"Eq","preview":"Trait to evaluate if two types are equal.\n","type_name":"trait"},{"html_filename":"trait.Ord.html","module_info":["core","ops"],"name":"Ord","preview":"Trait to evaluate if one value is greater or less than another of the same type.\n","type_name":"trait"},{"html_filename":"trait.BitwiseAnd.html","module_info":["core","ops"],"name":"BitwiseAnd","preview":"Trait to bitwise AND two values of the same type.\n","type_name":"trait"},{"html_filename":"trait.BitwiseOr.html","module_info":["core","ops"],"name":"BitwiseOr","preview":"Trait to bitwise OR two values of the same type.\n","type_name":"trait"},{"html_filename":"trait.BitwiseXor.html","module_info":["core","ops"],"name":"BitwiseXor","preview":"Trait to bitwise XOR two values of the same type.\n","type_name":"trait"},{"html_filename":"trait.Shift.html","module_info":["core","ops"],"name":"Shift","preview":"Trait to bit shift a value.\n","type_name":"trait"},{"html_filename":"struct.StorageKey.html","module_info":["core","storage"],"name":"StorageKey","preview":"Describes a location in storage.\n","type_name":"struct"},{"html_filename":"struct.Buffer.html","module_info":["core","codec"],"name":"Buffer","preview":"","type_name":"struct"},{"html_filename":"trait.AbiEncode.html","module_info":["core","codec"],"name":"AbiEncode","preview":"","type_name":"trait"},{"html_filename":"fn.encode.html","module_info":["core","codec"],"name":"encode","preview":"","type_name":"function"}],"impl_traits":[{"html_filename":"trait.Foo.html","module_info":["impl_traits","foo"],"name":"Foo","preview":"","type_name":"trait"},{"html_filename":"trait.Baz.html","module_info":["impl_traits","foo"],"name":"Baz","preview":"","type_name":"trait"},{"html_filename":"struct.Bar.html","module_info":["impl_traits","bar"],"name":"Bar","preview":"","type_name":"struct"}]};
"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=SEARCH_INDEX);"#]],
);
assert_file_tree(
Expand All @@ -50,8 +50,6 @@ fn test_impl_traits_default() {
"core/codec/struct.Buffer.html",
"core/codec/trait.AbiEncode.html",
"core/index.html",
"core/never/enum.Never.html",
"core/never/index.html",
"core/ops/index.html",
"core/ops/trait.Add.html",
"core/ops/trait.BitwiseAnd.html",
Expand Down
4 changes: 4 additions & 0 deletions sway-ast/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ pub enum Ty {
ampersand_token: AmpersandToken,
ty: Box<Ty>,
},
Never {
bang_token: BangToken,
},
}

impl Spanned for Ty {
Expand All @@ -43,6 +46,7 @@ impl Spanned for Ty {
ampersand_token,
ty,
} => Span::join(ampersand_token.span(), ty.span()),
Ty::Never { bang_token } => bang_token.span(),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions sway-core/src/abi_generation/evm_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ pub fn abi_str(type_info: &TypeInfo, type_engine: &TypeEngine, decl_engine: &Dec
use TypeInfo::*;
match type_info {
Unknown => "unknown".into(),
Never => "never".into(),
UnknownGeneric { name, .. } => name.to_string(),
Placeholder(_) => "_".to_string(),
TypeParam(n) => format!("typeparam({n})"),
Expand Down
1 change: 1 addition & 0 deletions sway-core/src/abi_generation/fuel_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,7 @@ impl TypeInfo {
use TypeInfo::*;
match self {
Unknown => "unknown".into(),
Never => "never".into(),
UnknownGeneric { name, .. } => name.to_string(),
Placeholder(_) => "_".to_string(),
TypeParam(n) => format!("typeparam({n})"),
Expand Down
1 change: 1 addition & 0 deletions sway-core/src/asm_generation/fuel/fuel_asm_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1728,6 +1728,7 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> {
// XXX reassess all the places we use this
pub(crate) fn is_copy_type(&self, ty: &Type) -> bool {
ty.is_unit(self.context)
|| ty.is_never(self.context)
|| ty.is_bool(self.context)
|| ty
.get_uint_width(self.context)
Expand Down
1 change: 1 addition & 0 deletions sway-core/src/ir_generation/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ fn convert_resolved_type(
convert_resolved_typeid(type_engine, decl_engine, context, &ty.type_id, span)?
}
TypeInfo::Ref(_) => Type::get_uint64(context),
TypeInfo::Never => Type::get_never(context),

// Unsupported types which shouldn't exist in the AST after type checking and
// monomorphisation.
Expand Down
12 changes: 0 additions & 12 deletions sway-core/src/language/ty/ast_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,18 +135,6 @@ impl CollectTypesMetadata for TyAstNode {
}
}

impl DeterministicallyAborts for TyAstNode {
fn deterministically_aborts(&self, decl_engine: &DeclEngine, check_call_body: bool) -> bool {
use TyAstNodeContent::*;
match &self.content {
Declaration(_) => false,
Expression(exp) => exp.deterministically_aborts(decl_engine, check_call_body),
SideEffect(_) => false,
Error(_, _) => false,
}
}
}

impl GetDeclIdent for TyAstNode {
fn get_decl_ident(&self) -> Option<Ident> {
self.content.get_decl_ident()
Expand Down
10 changes: 1 addition & 9 deletions sway-core/src/language/ty/code_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use sway_types::Span;

use crate::{
decl_engine::*, engine_threading::*, language::ty::*, semantic_analysis::TypeCheckContext,
type_system::*, types::DeterministicallyAborts,
type_system::*,
};

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -74,11 +74,3 @@ impl UpdateConstantExpression for TyCodeBlock {
.for_each(|x| x.update_constant_expression(engines, implementing_type));
}
}

impl DeterministicallyAborts for TyCodeBlock {
fn deterministically_aborts(&self, decl_engine: &DeclEngine, check_call_body: bool) -> bool {
self.contents
.iter()
.any(|x| x.deterministically_aborts(decl_engine, check_call_body))
}
}
103 changes: 0 additions & 103 deletions sway-core/src/language/ty/expression/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,109 +317,6 @@ impl CollectTypesMetadata for TyExpression {
}
}

impl DeterministicallyAborts for TyExpression {
fn deterministically_aborts(&self, decl_engine: &DeclEngine, check_call_body: bool) -> bool {
use TyExpressionVariant::*;
match &self.expression {
FunctionApplication {
fn_ref, arguments, ..
} => {
if !check_call_body {
return false;
}
let function_decl = decl_engine.get_function(fn_ref);
function_decl
.body
.deterministically_aborts(decl_engine, check_call_body)
|| arguments
.iter()
.any(|(_, x)| x.deterministically_aborts(decl_engine, check_call_body))
}
Tuple { fields, .. } => fields
.iter()
.any(|x| x.deterministically_aborts(decl_engine, check_call_body)),
Array { contents, .. } => contents
.iter()
.any(|x| x.deterministically_aborts(decl_engine, check_call_body)),
CodeBlock(contents) => contents.deterministically_aborts(decl_engine, check_call_body),
LazyOperator { lhs, .. } => lhs.deterministically_aborts(decl_engine, check_call_body),
StructExpression { fields, .. } => fields.iter().any(|x| {
x.value
.deterministically_aborts(decl_engine, check_call_body)
}),
EnumInstantiation { contents, .. } => contents
.as_ref()
.map(|x| x.deterministically_aborts(decl_engine, check_call_body))
.unwrap_or(false),
AbiCast { address, .. } => {
address.deterministically_aborts(decl_engine, check_call_body)
}
StructFieldAccess { .. }
| Literal(_)
| StorageAccess { .. }
| VariableExpression { .. }
| ConstantExpression { .. }
| FunctionParameter
| TupleElemAccess { .. } => false,
IntrinsicFunction(kind) => kind.deterministically_aborts(decl_engine, check_call_body),
ArrayIndex { prefix, index } => {
prefix.deterministically_aborts(decl_engine, check_call_body)
|| index.deterministically_aborts(decl_engine, check_call_body)
}
AsmExpression { registers, .. } => registers.iter().any(|x| {
x.initializer
.as_ref()
.map(|x| x.deterministically_aborts(decl_engine, check_call_body))
.unwrap_or(false)
}),
MatchExp { desugared, .. } => {
desugared.deterministically_aborts(decl_engine, check_call_body)
}
IfExp {
condition,
then,
r#else,
..
} => {
condition.deterministically_aborts(decl_engine, check_call_body)
|| (then.deterministically_aborts(decl_engine, check_call_body)
&& r#else
.as_ref()
.map(|x| x.deterministically_aborts(decl_engine, check_call_body))
.unwrap_or(false))
}
AbiName(_) => false,
EnumTag { exp } => exp.deterministically_aborts(decl_engine, check_call_body),
UnsafeDowncast { exp, .. } => {
exp.deterministically_aborts(decl_engine, check_call_body)
}
WhileLoop { condition, body } => {
condition.deterministically_aborts(decl_engine, check_call_body)
|| body.deterministically_aborts(decl_engine, check_call_body)
}
ForLoop { desugared } => {
desugared.deterministically_aborts(decl_engine, check_call_body)
}
Break => false,
Continue => false,
Reassignment(reassignment) => reassignment
.rhs
.deterministically_aborts(decl_engine, check_call_body),
ImplicitReturn(exp) => exp.deterministically_aborts(decl_engine, check_call_body),
// TODO: Is this correct?
// I'm not sure what this function is supposed to do exactly. It's called
// "deterministically_aborts" which I thought meant it checks for an abort/panic, but
// it's actually checking for returns.
//
// Also, is it necessary to check the expression to see if avoids the return? eg.
// someone could write `return break;` in a loop, which would mean the return never
// gets executed.
Return(_) => true,
Ref(exp) | Deref(exp) => exp.deterministically_aborts(decl_engine, check_call_body),
}
}
}

impl TyExpression {
pub(crate) fn error(err: ErrorEmitted, span: Span, engines: &Engines) -> TyExpression {
let type_engine = engines.te();
Expand Down
14 changes: 1 addition & 13 deletions sway-core/src/language/ty/expression/intrinsic_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ use std::{
hash::{Hash, Hasher},
};

use crate::{
decl_engine::DeclEngine, engine_threading::*, language::ty::*, type_system::*, types::*,
};
use crate::{engine_threading::*, language::ty::*, type_system::*, types::*};
use itertools::Itertools;
use sway_ast::Intrinsic;
use sway_error::handler::{ErrorEmitted, Handler};
Expand Down Expand Up @@ -99,16 +97,6 @@ impl DebugWithEngines for TyIntrinsicFunctionKind {
}
}

impl DeterministicallyAborts for TyIntrinsicFunctionKind {
fn deterministically_aborts(&self, decl_engine: &DeclEngine, check_call_body: bool) -> bool {
matches!(self.kind, Intrinsic::Revert)
|| self
.arguments
.iter()
.any(|x| x.deterministically_aborts(decl_engine, check_call_body))
}
}

impl CollectTypesMetadata for TyIntrinsicFunctionKind {
fn collect_types_metadata(
&self,
Expand Down
2 changes: 0 additions & 2 deletions sway-core/src/semantic_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ mod program;
mod type_check_analysis;
pub(crate) mod type_check_context;
mod type_check_finalization;
mod type_check_unification;
pub use ast_node::*;
pub use namespace::Namespace;
pub(crate) use type_check_analysis::*;
pub(crate) use type_check_context::TypeCheckContext;
pub(crate) use type_check_finalization::*;
pub(crate) use type_check_unification::*;
Loading

0 comments on commit 057388c

Please sign in to comment.