Skip to content

Commit

Permalink
Implements the Never ! types as a TypeInfo bottom type.
Browse files Browse the repository at this point in the history
We now parse the `!` as a TypeInfo::Never, and remove the usage of empty enums as Never type in our code.

This commit removes completely the DeterministicallyAborts and TypeCheckUnificationContext.

The DeterministicallyAborts can be removed because the Never TypeInfo is now propagated using the type checker.
Code blocks that return, break, continue, or call an expression that returns Never, are marked as Never.

Partially fixes #5562.
  • Loading branch information
esdrubal committed Feb 12, 2024
1 parent 76e407a commit f8ca1c7
Show file tree
Hide file tree
Showing 38 changed files with 203 additions and 430 deletions.
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/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 f8ca1c7

Please sign in to comment.