diff --git a/sway-core/src/control_flow_analysis/dead_code_analysis.rs b/sway-core/src/control_flow_analysis/dead_code_analysis.rs index e09ccecca87..c1e4b8fca80 100644 --- a/sway-core/src/control_flow_analysis/dead_code_analysis.rs +++ b/sway-core/src/control_flow_analysis/dead_code_analysis.rs @@ -1033,44 +1033,32 @@ fn connect_expression( } fn connect_intrinsic_function( - kind: &TypedIntrinsicFunctionKind, + TypedIntrinsicFunctionKind { + kind, arguments, .. + }: &TypedIntrinsicFunctionKind, graph: &mut ControlFlowGraph, leaves: &[NodeIndex], exit_node: Option, tree_type: &TreeType, ) -> Result, CompileError> { - let result = match kind { - TypedIntrinsicFunctionKind::SizeOfVal { exp } => connect_expression( + let node = graph.add_node(kind.to_string().into()); + for leaf in leaves { + graph.add_edge(*leaf, node, "".into()); + } + let mut result = vec![node]; + let _ = arguments.iter().try_fold(&mut result, |accum, exp| { + let mut res = connect_expression( &(*exp).expression, graph, leaves, exit_node, - "size_of", + "intrinsic", tree_type, exp.span.clone(), - )?, - TypedIntrinsicFunctionKind::SizeOfType { .. } => { - let node = graph.add_node("size of type".into()); - for leaf in leaves { - graph.add_edge(*leaf, node, "".into()); - } - vec![node] - } - TypedIntrinsicFunctionKind::IsRefType { .. } => { - let node = graph.add_node("is ref type".into()); - for leaf in leaves { - graph.add_edge(*leaf, node, "".into()); - } - vec![node] - } - TypedIntrinsicFunctionKind::GetStorageKey => { - let node = graph.add_node("Get storage key".into()); - for leaf in leaves { - graph.add_edge(*leaf, node, "".into()); - } - vec![node] - } - }; + )?; + accum.append(&mut res); + Ok::<_, CompileError>(accum) + })?; Ok(result) } diff --git a/sway-core/src/convert_parse_tree.rs b/sway-core/src/convert_parse_tree.rs index 2d6d621773f..d7874f2ee3b 100644 --- a/sway-core/src/convert_parse_tree.rs +++ b/sway-core/src/convert_parse_tree.rs @@ -10,11 +10,11 @@ use { AbiDeclaration, AsmExpression, AsmOp, AsmRegister, AsmRegisterDeclaration, AstNode, AstNodeContent, CallPath, CodeBlock, ConstantDeclaration, Declaration, EnumDeclaration, EnumVariant, Expression, FunctionDeclaration, FunctionParameter, ImplSelf, ImplTrait, - ImportType, IncludeStatement, IntrinsicFunctionKind, LazyOp, Literal, MatchBranch, - MethodName, ParseTree, Purity, Reassignment, ReassignmentTarget, ReturnStatement, - Scrutinee, StorageDeclaration, StorageField, StructDeclaration, StructExpressionField, - StructField, StructScrutineeField, Supertrait, TraitDeclaration, TraitFn, TreeType, - TypeInfo, UseStatement, VariableDeclaration, Visibility, WhileLoop, + ImportType, IncludeStatement, LazyOp, Literal, MatchBranch, MethodName, ParseTree, Purity, + Reassignment, ReassignmentTarget, ReturnStatement, Scrutinee, StorageDeclaration, + StorageField, StructDeclaration, StructExpressionField, StructField, StructScrutineeField, + Supertrait, TraitDeclaration, TraitFn, TreeType, TypeInfo, UseStatement, + VariableDeclaration, Visibility, WhileLoop, }, std::{ collections::HashMap, @@ -102,16 +102,6 @@ pub enum ConvertParseTreeError { GenericsNotSupportedHere { span: Span }, #[error("fully qualified paths are not supported here")] FullyQualifiedPathsNotSupportedHere { span: Span }, - #[error("__size_of does not take arguments")] - SizeOfTooManyArgs { span: Span }, - #[error("__size_of requires exactly one generic argument")] - SizeOfOneGenericArg { span: Span }, - #[error("__is_reference_type does not take arguments")] - IsReferenceTypeTooManyArgs { span: Span }, - #[error("__is_reference_type requires exactly one generic argument")] - IsReferenceTypeOneGenericArg { span: Span }, - #[error("__size_of_val requires exactly one argument")] - SizeOfValOneArg { span: Span }, #[error("tuple index out of range")] TupleIndexOutOfRange { span: Span }, #[error("shift-left expressions are not implemented")] @@ -199,11 +189,6 @@ impl Spanned for ConvertParseTreeError { ConvertParseTreeError::FunctionArbitraryExpression { span } => span.clone(), ConvertParseTreeError::GenericsNotSupportedHere { span } => span.clone(), ConvertParseTreeError::FullyQualifiedPathsNotSupportedHere { span } => span.clone(), - ConvertParseTreeError::SizeOfTooManyArgs { span } => span.clone(), - ConvertParseTreeError::SizeOfOneGenericArg { span } => span.clone(), - ConvertParseTreeError::IsReferenceTypeTooManyArgs { span } => span.clone(), - ConvertParseTreeError::IsReferenceTypeOneGenericArg { span } => span.clone(), - ConvertParseTreeError::SizeOfValOneArg { span } => span.clone(), ConvertParseTreeError::TupleIndexOutOfRange { span } => span.clone(), ConvertParseTreeError::ShlNotImplemented { span } => span.clone(), ConvertParseTreeError::ShrNotImplemented { span } => span.clone(), @@ -1442,118 +1427,38 @@ fn expr_to_expression(ec: &mut ErrorContext, expr: Expr) -> Result { - if call_path.prefixes.is_empty() - && !call_path.is_absolute - && Intrinsic::try_from_str(call_path.suffix.as_str()) - == Some(Intrinsic::SizeOf) - { - if !arguments.is_empty() { - let error = ConvertParseTreeError::SizeOfTooManyArgs { span }; - return Err(ec.error(error)); - } - let ty = match { - generics_opt.and_then(|(_double_colon_token, generic_args)| { - iter_to_array(generic_args.parameters.into_inner()) - }) - } { - Some([ty]) => ty, - None => { - let error = ConvertParseTreeError::SizeOfOneGenericArg { span }; - return Err(ec.error(error)); - } - }; - let type_span = ty.span(); - let type_name = ty_to_type_info(ec, ty)?; - Expression::IntrinsicFunction { - kind: IntrinsicFunctionKind::SizeOfType { - type_name, - type_span, - }, - span, - } - } else if call_path.prefixes.is_empty() - && !call_path.is_absolute - && Intrinsic::try_from_str(call_path.suffix.as_str()) - == Some(Intrinsic::GetStorageKey) - { - if !arguments.is_empty() { - let error = ConvertParseTreeError::GetStorageKeyTooManyArgs { span }; - return Err(ec.error(error)); - } - if generics_opt.is_some() { - let error = ConvertParseTreeError::GenericsNotSupportedHere { span }; - return Err(ec.error(error)); - } - Expression::IntrinsicFunction { - kind: IntrinsicFunctionKind::GetStorageKey, - span, - } - } else if call_path.prefixes.is_empty() - && !call_path.is_absolute - && Intrinsic::try_from_str(call_path.suffix.as_str()) - == Some(Intrinsic::IsReferenceType) - { - if !arguments.is_empty() { - let error = ConvertParseTreeError::IsReferenceTypeTooManyArgs { span }; - return Err(ec.error(error)); - } - let ty = match { - generics_opt.and_then(|(_double_colon_token, generic_args)| { - iter_to_array(generic_args.parameters.into_inner()) - }) - } { - Some([ty]) => ty, - None => { - let error = - ConvertParseTreeError::IsReferenceTypeOneGenericArg { span }; - return Err(ec.error(error)); - } - }; - let type_span = ty.span(); - let type_name = ty_to_type_info(ec, ty)?; - Expression::IntrinsicFunction { - kind: IntrinsicFunctionKind::IsRefType { - type_name, - type_span, - }, - span, - } - } else if call_path.prefixes.is_empty() - && !call_path.is_absolute - && Intrinsic::try_from_str(call_path.suffix.as_str()) - == Some(Intrinsic::SizeOfVal) - { - let exp = match <[_; 1]>::try_from(arguments) { - Ok([exp]) => Box::new(exp), - Err(..) => { - let error = ConvertParseTreeError::SizeOfValOneArg { span }; - return Err(ec.error(error)); - } - }; - Expression::IntrinsicFunction { - kind: IntrinsicFunctionKind::SizeOfVal { exp }, - span, + let type_arguments = match generics_opt { + Some((_double_colon_token, generic_args)) => { + generic_args_to_type_arguments(ec, generic_args)? } - } else { - let type_arguments = match generics_opt { - Some((_double_colon_token, generic_args)) => { - generic_args_to_type_arguments(ec, generic_args)? - } - None => Vec::new(), - }; - if call_path.prefixes.is_empty() { - Expression::FunctionApplication { - name: call_path, + None => Vec::new(), + }; + match Intrinsic::try_from_str(call_path.suffix.as_str()) { + Some(intrinsic) + if call_path.prefixes.is_empty() && !call_path.is_absolute => + { + Expression::IntrinsicFunction { + kind: intrinsic, arguments, type_arguments, span, } - } else { - Expression::DelineatedPath { - call_path, - args: arguments, - type_arguments, - span, + } + _ => { + if call_path.prefixes.is_empty() { + Expression::FunctionApplication { + name: call_path, + arguments, + type_arguments, + span, + } + } else { + Expression::DelineatedPath { + call_path, + args: arguments, + type_arguments, + span, + } } } } diff --git a/sway-core/src/error.rs b/sway-core/src/error.rs index 8a156b7b02e..ba8a071cee5 100644 --- a/sway-core/src/error.rs +++ b/sway-core/src/error.rs @@ -995,6 +995,20 @@ pub enum CompileError { NonConstantDeclValue { span: Span }, #[error("Declaring storage in a {program_kind} is not allowed.")] StorageDeclarationInNonContract { program_kind: String, span: Span }, + #[error("Unsupported argument type to intrinsic \"{name}\".")] + IntrinsicUnsupportedArgType { name: String, span: Span }, + #[error("Call to \"{name}\" expects {expected} arguments")] + IntrinsicIncorrectNumArgs { + name: String, + expected: u64, + span: Span, + }, + #[error("Call to \"{name}\" expects {expected} type arguments")] + IntrinsicIncorrectNumTArgs { + name: String, + expected: u64, + span: Span, + }, } impl std::convert::From for CompileError { @@ -1151,6 +1165,9 @@ impl Spanned for CompileError { TupleIndexOutOfBounds { span, .. } => span.clone(), NonConstantDeclValue { span } => span.clone(), StorageDeclarationInNonContract { span, .. } => span.clone(), + IntrinsicUnsupportedArgType { span, .. } => span.clone(), + IntrinsicIncorrectNumArgs { span, .. } => span.clone(), + IntrinsicIncorrectNumTArgs { span, .. } => span.clone(), } } } diff --git a/sway-core/src/ir_generation/function.rs b/sway-core/src/ir_generation/function.rs index 173c4c19655..5a49b8c7947 100644 --- a/sway-core/src/ir_generation/function.rs +++ b/sway-core/src/ir_generation/function.rs @@ -15,6 +15,7 @@ use crate::{ type_engine::{insert_type, resolve_type, TypeId, TypeInfo}, }; use sway_ir::{Context, *}; +use sway_parse::intrinsics::Intrinsic; use sway_types::{ ident::Ident, span::{Span, Spanned}, @@ -319,14 +320,22 @@ impl FnCompiler { fn compile_intrinsic_function( &mut self, context: &mut Context, - kind: TypedIntrinsicFunctionKind, + TypedIntrinsicFunctionKind { + kind, + arguments, + type_arguments, + span: _, + }: TypedIntrinsicFunctionKind, span: Span, ) -> Result { + // We safely index into arguments and type_arguments arrays below + // because the type-checker ensures that the arguments are all there. match kind { - TypedIntrinsicFunctionKind::SizeOfVal { exp } => { + Intrinsic::SizeOfVal => { + let exp = arguments[0].clone(); // Compile the expression in case of side-effects but ignore its value. let ir_type = convert_resolved_typeid(context, &exp.return_type, &exp.span)?; - self.compile_expression(context, *exp)?; + self.compile_expression(context, exp)?; Ok(Constant::get_uint( context, 64, @@ -334,8 +343,9 @@ impl FnCompiler { None, )) } - TypedIntrinsicFunctionKind::SizeOfType { type_id, type_span } => { - let ir_type = convert_resolved_typeid(context, &type_id, &type_span)?; + Intrinsic::SizeOfType => { + let targ = type_arguments[0].clone(); + let ir_type = convert_resolved_typeid(context, &targ.type_id, &targ.span)?; Ok(Constant::get_uint( context, 64, @@ -343,17 +353,30 @@ impl FnCompiler { None, )) } - TypedIntrinsicFunctionKind::IsRefType { type_id, type_span } => { - let ir_type = convert_resolved_typeid(context, &type_id, &type_span)?; + Intrinsic::IsReferenceType => { + let targ = type_arguments[0].clone(); + let ir_type = convert_resolved_typeid(context, &targ.type_id, &targ.span)?; Ok(Constant::get_bool(context, !ir_type.is_copy_type(), None)) } - TypedIntrinsicFunctionKind::GetStorageKey => { + Intrinsic::GetStorageKey => { let span_md_idx = MetadataIndex::from_span(context, &span); Ok(self .current_block .ins(context) .get_storage_key(span_md_idx, None)) } + Intrinsic::Eq => { + let lhs = arguments[0].clone(); + let rhs = arguments[1].clone(); + let lhs_value = self.compile_expression(context, lhs)?; + let rhs_value = self.compile_expression(context, rhs)?; + Ok(self.current_block.ins(context).cmp( + Predicate::Equal, + lhs_value, + rhs_value, + None, + )) + } } } diff --git a/sway-core/src/parse_tree/expression/intrinsic_function.rs b/sway-core/src/parse_tree/expression/intrinsic_function.rs deleted file mode 100644 index a6568ad8d0f..00000000000 --- a/sway-core/src/parse_tree/expression/intrinsic_function.rs +++ /dev/null @@ -1,18 +0,0 @@ -use crate::{type_engine::TypeInfo, Expression}; -use sway_types::Span; - -#[derive(Debug, Clone)] -pub enum IntrinsicFunctionKind { - SizeOfVal { - exp: Box, - }, - SizeOfType { - type_name: TypeInfo, - type_span: Span, - }, - IsRefType { - type_name: TypeInfo, - type_span: Span, - }, - GetStorageKey, -} diff --git a/sway-core/src/parse_tree/expression/mod.rs b/sway-core/src/parse_tree/expression/mod.rs index 7fc4f2db2c2..e2b9c3600e1 100644 --- a/sway-core/src/parse_tree/expression/mod.rs +++ b/sway-core/src/parse_tree/expression/mod.rs @@ -6,15 +6,14 @@ use crate::{ use sway_types::{ident::Ident, Span, Spanned}; mod asm; -mod intrinsic_function; mod match_branch; mod method_name; mod scrutinee; pub(crate) use asm::*; -pub use intrinsic_function::*; pub(crate) use match_branch::MatchBranch; pub use method_name::MethodName; pub use scrutinee::*; +use sway_parse::intrinsics::Intrinsic; /// Represents a parsed, but not yet type checked, [Expression](https://en.wikipedia.org/wiki/Expression_(computer_science)). #[derive(Debug, Clone)] @@ -139,7 +138,9 @@ pub enum Expression { span: Span, }, IntrinsicFunction { - kind: IntrinsicFunctionKind, + kind: Intrinsic, + arguments: Vec, + type_arguments: Vec, span: Span, }, } diff --git a/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs b/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs index 510ade465e2..1d363ab136a 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs @@ -1,5 +1,7 @@ use std::fmt; +use itertools::Itertools; +use sway_parse::intrinsics::Intrinsic; use sway_types::Span; use crate::{ @@ -7,166 +9,235 @@ use crate::{ semantic_analysis::TypeCheckContext, type_engine::*, types::DeterministicallyAborts, - CompileError, CompileResult, IntrinsicFunctionKind, + CompileError, CompileResult, Expression, }; use super::TypedExpression; -#[derive(Debug, Clone)] -pub enum TypedIntrinsicFunctionKind { - SizeOfVal { exp: Box }, - SizeOfType { type_id: TypeId, type_span: Span }, - IsRefType { type_id: TypeId, type_span: Span }, - GetStorageKey, -} - -// 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 TypedIntrinsicFunctionKind { - fn eq(&self, other: &Self) -> bool { - use TypedIntrinsicFunctionKind::*; - match (self, other) { - (SizeOfVal { exp: l_exp }, SizeOfVal { exp: r_exp }) => *l_exp == *r_exp, - ( - SizeOfType { - type_id: l_type_id, .. - }, - SizeOfType { - type_id: r_type_id, .. - }, - ) => look_up_type_id(*l_type_id) == look_up_type_id(*r_type_id), - ( - IsRefType { - type_id: l_type_id, .. - }, - IsRefType { - type_id: r_type_id, .. - }, - ) => look_up_type_id(*l_type_id) == look_up_type_id(*r_type_id), - (GetStorageKey, GetStorageKey) => true, - _ => false, - } - } +#[derive(Debug, Clone, PartialEq)] +pub struct TypedIntrinsicFunctionKind { + pub kind: Intrinsic, + pub arguments: Vec, + pub type_arguments: Vec, + pub span: Span, } impl CopyTypes for TypedIntrinsicFunctionKind { fn copy_types(&mut self, type_mapping: &TypeMapping) { - use TypedIntrinsicFunctionKind::*; - match self { - SizeOfVal { exp } => { - exp.copy_types(type_mapping); - } - SizeOfType { type_id, type_span } => { - type_id.update_type(type_mapping, type_span); - } - IsRefType { type_id, type_span } => { - type_id.update_type(type_mapping, type_span); - } - GetStorageKey => {} + for arg in &mut self.arguments { + arg.copy_types(type_mapping); + } + for targ in &mut self.type_arguments { + targ.type_id.update_type(type_mapping, &targ.span); } } } impl fmt::Display for TypedIntrinsicFunctionKind { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use TypedIntrinsicFunctionKind::*; - let s = match self { - SizeOfVal { exp } => format!("size_of_val({})", exp), - SizeOfType { type_id, .. } => format!("size_of({})", look_up_type_id(*type_id)), - IsRefType { type_id, .. } => format!("is_ref_type({})", look_up_type_id(*type_id)), - GetStorageKey => "get_storage_key".to_string(), - }; - write!(f, "{}", s) + let targs = self + .type_arguments + .iter() + .map(|targ| look_up_type_id(targ.type_id)) + .join(", "); + let args = self.arguments.iter().map(|e| format!("{}", e)).join(", "); + + write!(f, "{}::<{}>::({})", self.kind, targs, args) } } impl DeterministicallyAborts for TypedIntrinsicFunctionKind { fn deterministically_aborts(&self) -> bool { - use TypedIntrinsicFunctionKind::*; - match self { - SizeOfVal { exp } => exp.deterministically_aborts(), - SizeOfType { .. } | GetStorageKey | IsRefType { .. } => false, - } + self.arguments.iter().any(|x| x.deterministically_aborts()) } } impl UnresolvedTypeCheck for TypedIntrinsicFunctionKind { fn check_for_unresolved_types(&self) -> Vec { - use TypedIntrinsicFunctionKind::*; - match self { - SizeOfVal { exp } => exp.check_for_unresolved_types(), - SizeOfType { type_id, .. } => type_id.check_for_unresolved_types(), - IsRefType { type_id, .. } => type_id.check_for_unresolved_types(), - GetStorageKey => vec![], - } + self.type_arguments + .iter() + .flat_map(|targ| targ.type_id.check_for_unresolved_types()) + .chain( + self.arguments + .iter() + .flat_map(UnresolvedTypeCheck::check_for_unresolved_types), + ) + .collect() } } impl TypedIntrinsicFunctionKind { pub(crate) fn type_check( mut ctx: TypeCheckContext, - kind: IntrinsicFunctionKind, + kind: Intrinsic, + type_arguments: Vec, + arguments: Vec, + span: Span, ) -> CompileResult<(Self, TypeId)> { let mut warnings = vec![]; let mut errors = vec![]; let (intrinsic_function, return_type) = match kind { - IntrinsicFunctionKind::SizeOfVal { exp } => { + Intrinsic::SizeOfVal => { + if arguments.len() != 1 { + errors.push(CompileError::IntrinsicIncorrectNumArgs { + name: kind.to_string(), + expected: 1, + span, + }); + return err(warnings, errors); + } let ctx = ctx .with_help_text("") .with_type_annotation(insert_type(TypeInfo::Unknown)); let exp = check!( - TypedExpression::type_check(ctx, *exp), + TypedExpression::type_check(ctx, arguments[0].clone()), return err(warnings, errors), warnings, errors ); - let intrinsic_function = - TypedIntrinsicFunctionKind::SizeOfVal { exp: Box::new(exp) }; + let intrinsic_function = TypedIntrinsicFunctionKind { + kind, + arguments: vec![exp], + type_arguments: vec![], + span, + }; let return_type = insert_type(TypeInfo::UnsignedInteger(IntegerBits::SixtyFour)); (intrinsic_function, return_type) } - IntrinsicFunctionKind::SizeOfType { - type_name, - type_span, - } => { + Intrinsic::SizeOfType => { + if !arguments.is_empty() { + errors.push(CompileError::IntrinsicIncorrectNumArgs { + name: kind.to_string(), + expected: 0, + span, + }); + return err(warnings, errors); + } + if type_arguments.len() != 1 { + errors.push(CompileError::IntrinsicIncorrectNumTArgs { + name: kind.to_string(), + expected: 1, + span, + }); + return err(warnings, errors); + } + let targ = type_arguments[0].clone(); let type_id = check!( ctx.resolve_type_with_self( - insert_type(type_name), - &type_span, + insert_type(resolve_type(targ.type_id, &targ.span).unwrap()), + &targ.span, EnforceTypeArguments::Yes ), insert_type(TypeInfo::ErrorRecovery), warnings, errors, ); - let intrinsic_function = - TypedIntrinsicFunctionKind::SizeOfType { type_id, type_span }; + let intrinsic_function = TypedIntrinsicFunctionKind { + kind, + arguments: vec![], + type_arguments: vec![TypeArgument { + type_id, + span: targ.span, + }], + span, + }; let return_type = insert_type(TypeInfo::UnsignedInteger(IntegerBits::SixtyFour)); (intrinsic_function, return_type) } - IntrinsicFunctionKind::IsRefType { - type_name, - type_span, - } => { + Intrinsic::IsReferenceType => { + if type_arguments.len() != 1 { + errors.push(CompileError::IntrinsicIncorrectNumTArgs { + name: kind.to_string(), + expected: 1, + span, + }); + return err(warnings, errors); + } + let targ = type_arguments[0].clone(); let type_id = check!( ctx.resolve_type_with_self( - insert_type(type_name), - &type_span, + insert_type(resolve_type(targ.type_id, &targ.span).unwrap()), + &targ.span, EnforceTypeArguments::Yes ), insert_type(TypeInfo::ErrorRecovery), warnings, errors, ); - let intrinsic_function = - TypedIntrinsicFunctionKind::IsRefType { type_id, type_span }; + let intrinsic_function = TypedIntrinsicFunctionKind { + kind, + arguments: vec![], + type_arguments: vec![TypeArgument { + type_id, + span: targ.span, + }], + span, + }; (intrinsic_function, insert_type(TypeInfo::Boolean)) } - IntrinsicFunctionKind::GetStorageKey => ( - TypedIntrinsicFunctionKind::GetStorageKey, + Intrinsic::GetStorageKey => ( + TypedIntrinsicFunctionKind { + kind, + arguments: vec![], + type_arguments: vec![], + span, + }, insert_type(TypeInfo::B256), ), + Intrinsic::Eq => { + if arguments.len() != 2 { + errors.push(CompileError::IntrinsicIncorrectNumArgs { + name: kind.to_string(), + expected: 2, + span, + }); + return err(warnings, errors); + } + let mut ctx = ctx + .by_ref() + .with_type_annotation(insert_type(TypeInfo::Unknown)); + + let lhs = arguments[0].clone(); + let lhs = check!( + TypedExpression::type_check(ctx.by_ref(), lhs), + return err(warnings, errors), + warnings, + errors + ); + + // Check for supported argument types + let arg_ty = resolve_type(lhs.return_type, &lhs.span).unwrap(); + let is_valid_arg_ty = matches!(arg_ty, TypeInfo::UnsignedInteger(_)) + || matches!(arg_ty, TypeInfo::Boolean); + if !is_valid_arg_ty { + errors.push(CompileError::IntrinsicUnsupportedArgType { + name: kind.to_string(), + span: lhs.span, + }); + return err(warnings, errors); + } + + let rhs = arguments[1].clone(); + let ctx = ctx + .by_ref() + .with_help_text("Incorrect argument type") + .with_type_annotation(lhs.return_type); + let rhs = check!( + TypedExpression::type_check(ctx, rhs), + return err(warnings, errors), + warnings, + errors + ); + ( + TypedIntrinsicFunctionKind { + kind, + arguments: vec![lhs, rhs], + type_arguments: vec![], + span, + }, + insert_type(TypeInfo::Boolean), + ) + } }; ok((intrinsic_function, return_type), warnings, errors) } diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs index fc07f43e3b3..71873e87c5d 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -16,6 +16,7 @@ use crate::{ error::*, parse_tree::*, semantic_analysis::*, type_engine::*, types::DeterministicallyAborts, }; +use sway_parse::intrinsics::Intrinsic; use sway_types::{Ident, Span, Spanned}; use std::{ @@ -519,9 +520,18 @@ impl TypedExpression { .with_help_text(""); Self::type_check_storage_load(ctx, field_names, &expr_span) } - Expression::IntrinsicFunction { kind, span } => { - Self::type_check_intrinsic_function(ctx.by_ref(), kind, span) - } + Expression::IntrinsicFunction { + kind, + type_arguments, + arguments, + span, + } => Self::type_check_intrinsic_function( + ctx.by_ref(), + kind, + type_arguments, + arguments, + span, + ), }; let mut typed_expression = match res.value { Some(r) => r, @@ -1601,13 +1611,21 @@ impl TypedExpression { fn type_check_intrinsic_function( ctx: TypeCheckContext, - kind: IntrinsicFunctionKind, + kind: Intrinsic, + type_arguments: Vec, + arguments: Vec, span: Span, ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; let (intrinsic_function, return_type) = check!( - TypedIntrinsicFunctionKind::type_check(ctx, kind), + TypedIntrinsicFunctionKind::type_check( + ctx, + kind, + type_arguments, + arguments, + span.clone() + ), return err(warnings, errors), warnings, errors diff --git a/sway-core/src/semantic_analysis/node_dependencies.rs b/sway-core/src/semantic_analysis/node_dependencies.rs index e078af35edf..69e7006f0f2 100644 --- a/sway-core/src/semantic_analysis/node_dependencies.rs +++ b/sway-core/src/semantic_analysis/node_dependencies.rs @@ -475,10 +475,9 @@ impl Dependencies { } Expression::TupleIndex { prefix, .. } => self.gather_from_expr(prefix), Expression::StorageAccess { .. } => self, - Expression::IntrinsicFunction { kind, .. } => match kind { - IntrinsicFunctionKind::SizeOfVal { exp } => self.gather_from_expr(exp), - _ => self, - }, + Expression::IntrinsicFunction { arguments, .. } => { + self.gather_from_iter(arguments.iter(), |deps, arg| deps.gather_from_expr(arg)) + } } } diff --git a/sway-core/tests/sway_to_ir/eq_intrinsic.ir b/sway-core/tests/sway_to_ir/eq_intrinsic.ir new file mode 100644 index 00000000000..119bcf76de2 --- /dev/null +++ b/sway-core/tests/sway_to_ir/eq_intrinsic.ir @@ -0,0 +1,21 @@ +script { + fn main() -> bool, !1 { + local ptr bool _ + + entry: + v0 = const u64 1, !2 + v1 = const u64 2, !3 + v2 = cmp eq v0 v1 + v3 = get_ptr ptr bool _, ptr bool, 0, !4 + store v2, ptr v3, !4 + v4 = const bool true, !5 + ret bool v4 + } +} + +!0 = filepath "/path/to/eq_intrinsic.sw" +!1 = span !0 9 59 +!2 = span !0 44 45 +!3 = span !0 47 48 +!4 = span !0 31 50 +!5 = span !0 53 57 diff --git a/sway-core/tests/sway_to_ir/eq_intrinsic.sw b/sway-core/tests/sway_to_ir/eq_intrinsic.sw new file mode 100644 index 00000000000..d32660611f4 --- /dev/null +++ b/sway-core/tests/sway_to_ir/eq_intrinsic.sw @@ -0,0 +1,6 @@ +script; + +fn main() -> bool { + let _ = __eq(1, 2); + true +} diff --git a/sway-ir/src/verify.rs b/sway-ir/src/verify.rs index 1ecb4aabea3..592a52cbcbd 100644 --- a/sway-ir/src/verify.rs +++ b/sway-ir/src/verify.rs @@ -304,6 +304,7 @@ impl<'a> InstructionVerifier<'a> { Ok(()) } } + (Type::Bool, Type::Bool) => Ok(()), _otherwise => Err(IrError::VerifyCmpBadTypes( lhs_ty.as_string(self.context), rhs_ty.as_string(self.context), diff --git a/sway-lib-core/src/ops.sw b/sway-lib-core/src/ops.sw index 4d3382caf31..d009e8d4ce3 100644 --- a/sway-lib-core/src/ops.sw +++ b/sway-lib-core/src/ops.sw @@ -210,46 +210,31 @@ pub trait Eq { impl Eq for bool { fn eq(self, other: Self) -> bool { - asm(r1: self, r2: other, r3) { - eq r3 r1 r2; - r3: bool - } + __eq(self, other) } } impl Eq for u64 { fn eq(self, other: Self) -> bool { - asm(r1: self, r2: other, r3) { - eq r3 r1 r2; - r3: bool - } + __eq(self, other) } } impl Eq for u32 { fn eq(self, other: Self) -> bool { - asm(r1: self, r2: other, r3) { - eq r3 r1 r2; - r3: bool - } + __eq(self, other) } } impl Eq for u16 { fn eq(self, other: Self) -> bool { - asm(r1: self, r2: other, r3) { - eq r3 r1 r2; - r3: bool - } + __eq(self, other) } } impl Eq for u8 { fn eq(self, other: Self) -> bool { - asm(r1: self, r2: other, r3) { - eq r3 r1 r2; - r3: bool - } + __eq(self, other) } } diff --git a/sway-lsp/src/core/token.rs b/sway-lsp/src/core/token.rs index a7d3dd231bf..c03f83ef9b5 100644 --- a/sway-lsp/src/core/token.rs +++ b/sway-lsp/src/core/token.rs @@ -8,7 +8,7 @@ use crate::{ use sway_core::{ constants::TUPLE_NAME_PREFIX, parse_tree::MethodName, type_engine::TypeInfo, AstNode, AstNodeContent, Declaration, Expression, FunctionDeclaration, FunctionParameter, - IntrinsicFunctionKind, VariableDeclaration, WhileLoop, + VariableDeclaration, WhileLoop, }; use sway_types::{ident::Ident, span::Span, Spanned}; use tower_lsp::lsp_types::Range; @@ -441,20 +441,15 @@ fn handle_expression(exp: Expression, tokens: &mut Vec) { tokens.push(token); } } - Expression::IntrinsicFunction { kind, .. } => { - handle_intrinsic_function(kind, tokens); + Expression::IntrinsicFunction { arguments, .. } => { + handle_intrinsic_function(arguments, tokens); } } } -fn handle_intrinsic_function(kind: IntrinsicFunctionKind, tokens: &mut Vec) { - match kind { - IntrinsicFunctionKind::SizeOfVal { exp } => { - handle_expression(*exp, tokens); - } - IntrinsicFunctionKind::SizeOfType { .. } => {} - IntrinsicFunctionKind::IsRefType { .. } => {} - IntrinsicFunctionKind::GetStorageKey => {} +fn handle_intrinsic_function(arguments: Vec, tokens: &mut Vec) { + for arg in arguments { + handle_expression(arg, tokens); } } diff --git a/sway-lsp/src/core/traverse_typed_tree.rs b/sway-lsp/src/core/traverse_typed_tree.rs index fa676176e5e..ee23d9b9cb2 100644 --- a/sway-lsp/src/core/traverse_typed_tree.rs +++ b/sway-lsp/src/core/traverse_typed_tree.rs @@ -332,14 +332,12 @@ fn handle_expression(expression: &TypedExpression, tokens: &mut TokenMap) { } } -fn handle_intrinsic_function(kind: &TypedIntrinsicFunctionKind, tokens: &mut TokenMap) { - match kind { - TypedIntrinsicFunctionKind::SizeOfVal { exp } => { - handle_expression(exp, tokens); - } - TypedIntrinsicFunctionKind::SizeOfType { .. } => {} - TypedIntrinsicFunctionKind::IsRefType { .. } => {} - TypedIntrinsicFunctionKind::GetStorageKey => {} +fn handle_intrinsic_function( + TypedIntrinsicFunctionKind { arguments, .. }: &TypedIntrinsicFunctionKind, + tokens: &mut TokenMap, +) { + for arg in arguments { + handle_expression(arg, tokens); } } diff --git a/sway-parse/src/intrinsics.rs b/sway-parse/src/intrinsics.rs index 7271d8adcc1..ac2640fb455 100644 --- a/sway-parse/src/intrinsics.rs +++ b/sway-parse/src/intrinsics.rs @@ -1,9 +1,25 @@ -#[derive(Eq, PartialEq)] +use std::fmt; + +#[derive(Eq, PartialEq, Debug, Clone)] pub enum Intrinsic { GetStorageKey, IsReferenceType, - SizeOf, + SizeOfType, SizeOfVal, + Eq, +} + +impl fmt::Display for Intrinsic { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + Intrinsic::GetStorageKey => "get_storage_key", + Intrinsic::IsReferenceType => "is_reference_type", + Intrinsic::SizeOfType => "size_of", + Intrinsic::SizeOfVal => "size_of_val", + Intrinsic::Eq => "eq", + }; + write!(f, "{}", s) + } } impl Intrinsic { @@ -12,8 +28,9 @@ impl Intrinsic { Some(match raw { "__get_storage_key" => GetStorageKey, "__is_reference_type" => IsReferenceType, - "__size_of" => SizeOf, + "__size_of" => SizeOfType, "__size_of_val" => SizeOfVal, + "__eq" => Eq, _ => return None, }) } diff --git a/sway-types/src/span.rs b/sway-types/src/span.rs index 55be7142c0b..bae29f8a5fb 100644 --- a/sway-types/src/span.rs +++ b/sway-types/src/span.rs @@ -96,6 +96,11 @@ impl Span { }) } + pub fn from_string(source: String) -> Span { + let len = source.len(); + Span::new(Arc::from(source), 0, len, None).unwrap() + } + pub fn src(&self) -> &Arc { &self.src } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/Forc.lock new file mode 100644 index 00000000000..63ae381216c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/Forc.lock @@ -0,0 +1,14 @@ +[[package]] +name = 'core' +source = 'path+from-root-987CA0C143EB9BCC' +dependencies = [] + +[[package]] +name = 'eq_intrinsic' +source = 'root' +dependencies = ['std'] + +[[package]] +name = 'std' +source = 'path+from-root-987CA0C143EB9BCC' +dependencies = ['core'] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/Forc.toml new file mode 100644 index 00000000000..e84dbdc4d76 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "eq_intrinsic" + +[dependencies] +std = { path = "../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/json_abi_oracle.json new file mode 100644 index 00000000000..905c3bd1034 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/json_abi_oracle.json @@ -0,0 +1,14 @@ +[ + { + "inputs": [], + "name": "main", + "outputs": [ + { + "components": null, + "name": "", + "type": "u64" + } + ], + "type": "function" + } +] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/src/main.sw new file mode 100644 index 00000000000..f0af6b19049 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/src/main.sw @@ -0,0 +1,23 @@ +script; + +struct A { + a: u64, +} + +enum B { + First: (), + Second: u64 +} + +fn main() { + let _ = __eq("hi", "ho"); + let _ = __eq(false, 11); + let _ = __eq(A { a: 1 }, B { a: 1 }); + let _ = __eq((1, 2), (1, 2)); + let _ = __eq([1, 2], [1, 2]); + let _ = __eq(B::First, B::First); + let _ = __eq(B::Second(1), B::Second(1)); + let my_number1: b256 = 0x000000000000000000000000000000000000000000000000000000000000002A; + let my_number2: b256 = 0x000000000000000000000000000000000000000000000000000000000000002A; + let _ = __eq(my_number1, my_number1); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/test.toml new file mode 100644 index 00000000000..299b8ccd092 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/test.toml @@ -0,0 +1,25 @@ +category = "fail" + +# check: $()__eq("hi", "ho"); +# nextln: $()Unsupported argument type to intrinsic "eq". + +# check: $()__eq(false, 11); +# check: $()Mismatched types. + +# check: $()__eq(A { a: 1 }, B { a: 1 }); +# check: $()Unsupported argument type to intrinsic "eq". + +# check: $()__eq((1, 2), (1, 2)); +# check: $()Unsupported argument type to intrinsic "eq". + +# check: $()__eq([1, 2], [1, 2]); +# check: $()Unsupported argument type to intrinsic "eq". + +# check: $()__eq(B::First, B::First); +# check: $()Unsupported argument type to intrinsic "eq". + +# check: $()__eq(B::Second(1), B::Second(1)); +# check: $()Unsupported argument type to intrinsic "eq". + +# check: $()__eq(my_number1, my_number1); +# check: $()Unsupported argument type to intrinsic "eq". \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/Forc.lock new file mode 100644 index 00000000000..63ae381216c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/Forc.lock @@ -0,0 +1,14 @@ +[[package]] +name = 'core' +source = 'path+from-root-987CA0C143EB9BCC' +dependencies = [] + +[[package]] +name = 'eq_intrinsic' +source = 'root' +dependencies = ['std'] + +[[package]] +name = 'std' +source = 'path+from-root-987CA0C143EB9BCC' +dependencies = ['core'] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/Forc.toml new file mode 100644 index 00000000000..9d341be72f6 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "eq_intrinsic" + +[dependencies] +std = { path = "../../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/json_abi_oracle.json new file mode 100644 index 00000000000..905c3bd1034 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/json_abi_oracle.json @@ -0,0 +1,14 @@ +[ + { + "inputs": [], + "name": "main", + "outputs": [ + { + "components": null, + "name": "", + "type": "u64" + } + ], + "type": "function" + } +] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/src/main.sw new file mode 100644 index 00000000000..9a81fd329d6 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/src/main.sw @@ -0,0 +1,39 @@ +script; + +use std::assert::assert; + +fn main() -> u64 { + + assert(__eq(true, true) == (true == true)); + assert(__eq(true, false) != (true != false)); + assert(__eq(true, true) == (true != false)); + + assert(__eq(1, 22) == (1 == 22)); + assert(__eq(1, 1) == (1 == 1)); + + let a: u8 = 1; + let b: u8 = 22; + let c: u8 = 1; + assert(__eq(a, b) == (a == b)); + assert(__eq(a, c) == (a == c)); + + let a: u16 = 1; + let b: u16 = 22; + let c: u16 = 1; + assert(__eq(a, b) == (a == b)); + assert(__eq(a, c) == (a == c)); + + let a: u32 = 1; + let b: u32 = 22; + let c: u32 = 1; + assert(__eq(a, b) == (a == b)); + assert(__eq(a, c) == (a == c)); + + let a: u64 = 1; + let b: u64 = 22; + let c: u64 = 1; + assert(__eq(a, b) == (a == b)); + assert(__eq(a, c) == (a == c)); + + 2 +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/test.toml new file mode 100644 index 00000000000..5617bb5d0a9 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 2 } +validate_abi = true