From b41ade524ca0d8d5509c7ad1f8772e06f9b78f92 Mon Sep 17 00:00:00 2001 From: Sean Billig Date: Mon, 22 Mar 2021 11:56:32 -0700 Subject: [PATCH 1/3] Add panic hook with message to user --- Cargo.lock | 1 + compiler/Cargo.toml | 1 + compiler/src/errors.rs | 27 ++++++++++++++++++++++++++- src/main.rs | 3 +++ 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 4c4974af68..c558de9bae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -646,6 +646,7 @@ dependencies = [ "fe-parser", "hex", "maplit", + "once_cell", "primitive-types", "rand", "rstest", diff --git a/compiler/Cargo.toml b/compiler/Cargo.toml index e24f4b9558..e3f3ae0cab 100644 --- a/compiler/Cargo.toml +++ b/compiler/Cargo.toml @@ -26,6 +26,7 @@ stringreader = "0.1" # This fork supports concurrent compilation, which is required for Rust tests. solc = { git = "https://github.com/g-r-a-n-t/solc-rust", optional = true } maplit = "1.0.2" +once_cell = "1.5.2" [dev-dependencies] evm-runtime = "0.18" diff --git a/compiler/src/errors.rs b/compiler/src/errors.rs index 18e9dd7f18..62752522a3 100644 --- a/compiler/src/errors.rs +++ b/compiler/src/errors.rs @@ -1,7 +1,21 @@ //! Errors returned by the compilers and ABI builder. use fe_parser::tokenizer::TokenizeError; -use serde::export::Formatter; +use once_cell::sync::Lazy; +use std::fmt::Formatter; +use std::panic; + +const BUG_REPORT_URL: &str = "https://github.com/ethereum/fe/issues/new"; +static DEFAULT_PANIC_HOOK: Lazy) + Sync + Send + 'static>> = + Lazy::new(|| { + let hook = panic::take_hook(); + panic::set_hook(Box::new(|info| report_ice(info))); + hook + }); + +pub fn install_compiler_panic_hook() { + Lazy::force(&DEFAULT_PANIC_HOOK); +} /// Errors can either be an object or static reference. #[derive(Debug)] @@ -78,3 +92,14 @@ impl<'a> From for CompileError { CompileError::str(&format!("ethabi error: {}", e)) } } + +fn report_ice(info: &panic::PanicInfo) { + (*DEFAULT_PANIC_HOOK)(info); + + eprintln!(); + eprintln!("You've hit an internal compiler error. This is a bug in the Fe compiler."); + eprintln!("Fe is still under heavy development, and isn't yet ready for production use."); + eprintln!(); + eprintln!("If you would, please report this bug at the following URL:"); + eprintln!(" {}", BUG_REPORT_URL); +} diff --git a/src/main.rs b/src/main.rs index e2e15e35be..903349d443 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,7 @@ use clap::{ mod _utils; use crate::_utils::pretty_curly_print; +use fe_compiler::errors::install_compiler_panic_hook; use fe_compiler::types::CompiledModule; const DEFAULT_OUTPUT_DIR_NAME: &str = "output"; @@ -34,6 +35,8 @@ arg_enum! { } pub fn main() { + install_compiler_panic_hook(); + let matches = App::new("Fe") .version(VERSION) .about("Compiler for the Fe language") From c2ec459a906a91d82e6fcef0743d142da782e2c4 Mon Sep 17 00:00:00 2001 From: Sean Billig Date: Mon, 22 Mar 2021 13:06:01 -0700 Subject: [PATCH 2/3] Panic if Yul mapping fails --- compiler/src/lib.rs | 2 +- compiler/src/yul/mappers/assignments.rs | 100 ++++--- compiler/src/yul/mappers/contracts.rs | 15 +- compiler/src/yul/mappers/declarations.rs | 12 +- compiler/src/yul/mappers/expressions.rs | 263 ++++++++---------- compiler/src/yul/mappers/functions.rs | 123 ++++---- compiler/src/yul/mappers/module.rs | 9 +- compiler/src/yul/mod.rs | 12 +- compiler/src/yul/operations/abi.rs | 14 +- .../src/yul/runtime/functions/contracts.rs | 3 +- 10 files changed, 242 insertions(+), 311 deletions(-) diff --git a/compiler/src/lib.rs b/compiler/src/lib.rs index 9210b071dc..edb52165bf 100644 --- a/compiler/src/lib.rs +++ b/compiler/src/lib.rs @@ -47,7 +47,7 @@ pub fn compile( .map_err(|error| CompileError::str(&error.format_user(src)))?; // compile to yul - let yul_contracts = yul::compile(&context, &lowered_fe_module)?; + let yul_contracts = yul::compile(&context, &lowered_fe_module); // compile to bytecode if required #[cfg(feature = "solc-backend")] diff --git a/compiler/src/yul/mappers/assignments.rs b/compiler/src/yul/mappers/assignments.rs index 15586ce01f..0fbb76aea2 100644 --- a/compiler/src/yul/mappers/assignments.rs +++ b/compiler/src/yul/mappers/assignments.rs @@ -1,4 +1,3 @@ -use crate::errors::CompileError; use crate::yul::mappers::expressions; use crate::yul::operations::data as data_operations; use fe_analyzer::namespace::types::FixedSize; @@ -12,10 +11,7 @@ use std::convert::TryFrom; use yultsur::*; /// Builds a Yul statement from a Fe assignment. -pub fn assign( - context: &Context, - stmt: &Node, -) -> Result { +pub fn assign(context: &Context, stmt: &Node) -> yul::Statement { if let fe::FuncStmt::Assign { targets, value } = &stmt.kind { if targets.len() > 1 { unimplemented!("multiple assignment targets") @@ -26,52 +22,50 @@ pub fn assign( context.get_expression(first_target), context.get_expression(value), ) { - let target = expressions::expr(&context, first_target)?; - let value = expressions::expr(&context, value)?; + let target = expressions::expr(&context, first_target); + let value = expressions::expr(&context, value); let typ = FixedSize::try_from(target_attributes.typ.to_owned()) - .map_err(|_| CompileError::static_str("invalid attributes"))?; - - return Ok( - match ( - value_attributes.final_location(), - target_attributes.final_location(), - ) { - (Location::Memory, Location::Storage { .. }) => { - data_operations::mcopys(typ, target, value) - } - (Location::Memory, Location::Value) => { - let target = expr_as_ident(target)?; - let value = data_operations::mload(typ, value); - statement! { [target] := [value] } - } - (Location::Memory, Location::Memory) => { - let target = expr_as_ident(target)?; - statement! { [target] := [value] } - } - (Location::Storage { .. }, Location::Storage { .. }) => { - data_operations::scopys(typ, target, value) - } - (Location::Storage { .. }, Location::Value) => { - let target = expr_as_ident(target)?; - let value = data_operations::sload(typ, value); - statement! { [target] := [value] } - } - (Location::Storage { .. }, Location::Memory) => { - unreachable!("raw sto to mem assign") - } - (Location::Value, Location::Memory) => { - data_operations::mstore(typ, target, value) - } - (Location::Value, Location::Storage { .. }) => { - data_operations::sstore(typ, target, value) - } - (Location::Value, Location::Value) => { - let target = expr_as_ident(target)?; - statement! { [target] := [value] } - } - }, - ); + .expect("invalid attributes"); + + return match ( + value_attributes.final_location(), + target_attributes.final_location(), + ) { + (Location::Memory, Location::Storage { .. }) => { + data_operations::mcopys(typ, target, value) + } + (Location::Memory, Location::Value) => { + let target = expr_as_ident(target); + let value = data_operations::mload(typ, value); + statement! { [target] := [value] } + } + (Location::Memory, Location::Memory) => { + let target = expr_as_ident(target); + statement! { [target] := [value] } + } + (Location::Storage { .. }, Location::Storage { .. }) => { + data_operations::scopys(typ, target, value) + } + (Location::Storage { .. }, Location::Value) => { + let target = expr_as_ident(target); + let value = data_operations::sload(typ, value); + statement! { [target] := [value] } + } + (Location::Storage { .. }, Location::Memory) => { + unreachable!("raw sto to mem assign") + } + (Location::Value, Location::Memory) => { + data_operations::mstore(typ, target, value) + } + (Location::Value, Location::Storage { .. }) => { + data_operations::sstore(typ, target, value) + } + (Location::Value, Location::Value) => { + let target = expr_as_ident(target); + statement! { [target] := [value] } + } + }; } } } @@ -79,12 +73,12 @@ pub fn assign( unreachable!() } -fn expr_as_ident(expr: yul::Expression) -> Result { +fn expr_as_ident(expr: yul::Expression) -> yul::Identifier { if let yul::Expression::Identifier(ident) = expr { - return Ok(ident); + ident + } else { + panic!("expression is not an identifier"); } - - Err(CompileError::static_str("expression is not an identifier")) } #[cfg(test)] diff --git a/compiler/src/yul/mappers/contracts.rs b/compiler/src/yul/mappers/contracts.rs index a273125d36..a18d05c2bc 100644 --- a/compiler/src/yul/mappers/contracts.rs +++ b/compiler/src/yul/mappers/contracts.rs @@ -1,4 +1,3 @@ -use crate::errors::CompileError; use crate::yul::constructor; use crate::yul::mappers::functions; use crate::yul::runtime; @@ -13,7 +12,7 @@ pub fn contract_def( context: &Context, stmt: &Node, created_contracts: Vec, -) -> Result { +) -> yul::Object { if let fe::ModuleStmt::ContractDef { name, body } = &stmt.kind { let contract_name = name.kind; let mut init_function = None; @@ -25,12 +24,10 @@ pub fn contract_def( (context.get_function(stmt), &stmt.kind) { if name.kind == "__init__" { - init_function = Some(( - functions::func_def(context, stmt)?, - attributes.param_types(), - )) + init_function = + Some((functions::func_def(context, stmt), attributes.param_types())) } else { - user_functions.push(functions::func_def(context, stmt)?) + user_functions.push(functions::func_def(context, stmt)) } } } @@ -99,12 +96,12 @@ pub fn contract_def( }; // We return the contract initialization object. - return Ok(yul::Object { + return yul::Object { name: identifier! { (contract_name) }, code: constructor_code, objects: constructor_objects, data, - }); + }; } unreachable!() diff --git a/compiler/src/yul/mappers/declarations.rs b/compiler/src/yul/mappers/declarations.rs index 47f48dd069..72a04f9083 100644 --- a/compiler/src/yul/mappers/declarations.rs +++ b/compiler/src/yul/mappers/declarations.rs @@ -1,4 +1,3 @@ -use crate::errors::CompileError; use crate::yul::mappers::expressions; use crate::yul::names; use fe_analyzer::namespace::types::{ @@ -11,17 +10,14 @@ use fe_parser::node::Node; use yultsur::*; /// Builds a Yul statement from a Fe variable declaration -pub fn var_decl( - context: &Context, - stmt: &Node, -) -> Result { +pub fn var_decl(context: &Context, stmt: &Node) -> yul::Statement { let decl_type = context.get_declaration(stmt).expect("missing attributes"); if let fe::FuncStmt::VarDecl { target, value, .. } = &stmt.kind { let target = names::var_name(expressions::expr_name_str(&target)); - return Ok(if let Some(value) = value { - let value = expressions::expr(context, &value)?; + return if let Some(value) = value { + let value = expressions::expr(context, &value); statement! { let [target] := [value] } } else { match decl_type { @@ -31,7 +27,7 @@ pub fn var_decl( statement! { let [target] := alloc([size]) } } } - }); + }; } unreachable!() diff --git a/compiler/src/yul/mappers/expressions.rs b/compiler/src/yul/mappers/expressions.rs index e90c7456cd..ba405ed3a4 100644 --- a/compiler/src/yul/mappers/expressions.rs +++ b/compiler/src/yul/mappers/expressions.rs @@ -1,4 +1,3 @@ -use crate::errors::CompileError; use crate::yul::names; use crate::yul::operations::{ abi as abi_operations, @@ -38,10 +37,10 @@ use std::str::FromStr; use yultsur::*; /// Builds a Yul expression from a Fe expression. -pub fn expr(context: &Context, exp: &Node) -> Result { +pub fn expr(context: &Context, exp: &Node) -> yul::Expression { if let Some(attributes) = context.get_expression(exp) { let expression = match &exp.kind { - fe::Expr::Name(_) => Ok(expr_name(exp)), + fe::Expr::Name(_) => expr_name(exp), fe::Expr::Num(_) => expr_num(exp), fe::Expr::Bool(_) => expr_bool(exp), fe::Expr::Subscript { .. } => expr_subscript(context, exp), @@ -57,14 +56,14 @@ pub fn expr(context: &Context, exp: &Node) -> Result expr_tuple(exp), fe::Expr::Str(_) => expr_str(exp), fe::Expr::Ellipsis => unimplemented!(), - }?; + }; match ( attributes.location.to_owned(), attributes.move_location.to_owned(), ) { (from, Some(to)) => move_expression(expression, attributes.typ.to_owned(), from, to), - (_, None) => Ok(expression), + (_, None) => expression, } } else { panic!("missing expression attributes for {:?}", exp) @@ -76,39 +75,30 @@ fn move_expression( typ: Type, from: Location, to: Location, -) -> Result { +) -> yul::Expression { let typ = FixedSize::try_from(typ).expect("Invalid type"); match (from.clone(), to.clone()) { - (Location::Storage { .. }, Location::Value) => Ok(data_operations::sload(typ, val)), - (Location::Memory, Location::Value) => Ok(data_operations::mload(typ, val)), - (Location::Memory, Location::Memory) => Ok(data_operations::mcopym(typ, val)), - (Location::Storage { .. }, Location::Memory) => Ok(data_operations::scopym(typ, val)), - _ => Err(CompileError::str(&format!( - "invalid expression move: {:?} {:?}", - from, to - ))), + (Location::Storage { .. }, Location::Value) => data_operations::sload(typ, val), + (Location::Memory, Location::Value) => data_operations::mload(typ, val), + (Location::Memory, Location::Memory) => data_operations::mcopym(typ, val), + (Location::Storage { .. }, Location::Memory) => data_operations::scopym(typ, val), + _ => panic!("invalid expression move: {:?} {:?}", from, to), } } -pub fn call_arg( - context: &Context, - arg: &Node, -) -> Result { +pub fn call_arg(context: &Context, arg: &Node) -> yul::Expression { match &arg.kind { fe::CallArg::Arg(value) => expr(context, value), fe::CallArg::Kwarg(fe::Kwarg { name: _, value }) => expr(context, value), } } -fn expr_call(context: &Context, exp: &Node) -> Result { +fn expr_call(context: &Context, exp: &Node) -> yul::Expression { if let fe::Expr::Call { args, func } = &exp.kind { if let Some(call_type) = context.get_call(func) { - let yul_args: Vec = args - .kind - .iter() - .map(|val| call_arg(context, val)) - .collect::>()?; + let yul_args: Vec = + args.kind.iter().map(|val| call_arg(context, val)).collect(); return match call_type { CallType::BuiltinFunction { func } => match func { @@ -124,16 +114,16 @@ fn expr_call(context: &Context, exp: &Node) -> Result Ok(struct_operations::new(val, yul_args)), - CallType::TypeConstructor { .. } => Ok(yul_args[0].to_owned()), + } => struct_operations::new(val, yul_args), + CallType::TypeConstructor { .. } => yul_args[0].to_owned(), CallType::SelfAttribute { func_name } => { let func_name = names::func_name(func_name); - Ok(expression! { [func_name]([yul_args...]) }) + expression! { [func_name]([yul_args...]) } } CallType::ValueAttribute => { if let fe::Expr::Attribute { value, attr } = &func.kind { @@ -141,12 +131,12 @@ fn expr_call(context: &Context, exp: &Node) -> Result Ok(contract_operations::call( + (Type::Contract(contract), func_name) => contract_operations::call( contract, func_name, - expr(context, value)?, + expr(context, value), yul_args, - )), + ), (typ, func_name) => { match builtins::ValueMethod::from_str(func_name) .expect("uncaught analyzer error") @@ -157,10 +147,10 @@ fn expr_call(context: &Context, exp: &Node) -> Result expr(context, value), builtins::ValueMethod::Clone => expr(context, value), builtins::ValueMethod::AbiEncode => match typ { - Type::Struct(struct_) => Ok(abi_operations::encode( + Type::Struct(struct_) => abi_operations::encode( vec![struct_], - vec![expr(context, value)?], - )), + vec![expr(context, value)], + ), _ => panic!("invalid attributes"), }, builtins::ValueMethod::AbiEncodePacked => todo!(), @@ -178,15 +168,15 @@ fn expr_call(context: &Context, exp: &Node) -> Result { - Ok(contract_operations::create2( + contract_operations::create2( &contract, yul_args[0].to_owned(), yul_args[1].to_owned(), - )) + ) + } + (Type::Contract(contract), ContractTypeMethod::Create) => { + contract_operations::create(&contract, yul_args[0].to_owned()) } - (Type::Contract(contract), ContractTypeMethod::Create) => Ok( - contract_operations::create(&contract, yul_args[0].to_owned()), - ), _ => panic!("invalid attributes"), } } @@ -197,13 +187,10 @@ fn expr_call(context: &Context, exp: &Node) -> Result, -) -> Result { +pub fn expr_comp_operation(context: &Context, exp: &Node) -> yul::Expression { if let fe::Expr::CompOperation { left, op, right } = &exp.kind { - let yul_left = expr(context, left)?; - let yul_right = expr(context, right)?; + let yul_left = expr(context, left); + let yul_right = expr(context, right); let typ = &context .get_expression(left) @@ -211,23 +198,23 @@ pub fn expr_comp_operation( .typ; return match op.kind { - fe::CompOperator::Eq => Ok(expression! { eq([yul_left], [yul_right]) }), - fe::CompOperator::NotEq => Ok(expression! { iszero((eq([yul_left], [yul_right]))) }), + fe::CompOperator::Eq => expression! { eq([yul_left], [yul_right]) }, + fe::CompOperator::NotEq => expression! { iszero((eq([yul_left], [yul_right]))) }, fe::CompOperator::Lt => match typ.is_signed_integer() { - true => Ok(expression! { slt([yul_left], [yul_right]) }), - false => Ok(expression! { lt([yul_left], [yul_right]) }), + true => expression! { slt([yul_left], [yul_right]) }, + false => expression! { lt([yul_left], [yul_right]) }, }, fe::CompOperator::LtE => match typ.is_signed_integer() { - true => Ok(expression! { iszero((sgt([yul_left], [yul_right]))) }), - false => Ok(expression! { iszero((gt([yul_left], [yul_right]))) }), + true => expression! { iszero((sgt([yul_left], [yul_right]))) }, + false => expression! { iszero((gt([yul_left], [yul_right]))) }, }, fe::CompOperator::Gt => match typ.is_signed_integer() { - true => Ok(expression! { sgt([yul_left], [yul_right]) }), - false => Ok(expression! { gt([yul_left], [yul_right]) }), + true => expression! { sgt([yul_left], [yul_right]) }, + false => expression! { gt([yul_left], [yul_right]) }, }, fe::CompOperator::GtE => match typ.is_signed_integer() { - true => Ok(expression! { iszero((slt([yul_left], [yul_right]))) }), - false => Ok(expression! { iszero((lt([yul_left], [yul_right]))) }), + true => expression! { iszero((slt([yul_left], [yul_right]))) }, + false => expression! { iszero((lt([yul_left], [yul_right]))) }, }, _ => unimplemented!(), }; @@ -236,13 +223,10 @@ pub fn expr_comp_operation( unreachable!() } -pub fn expr_bin_operation( - context: &Context, - exp: &Node, -) -> Result { +pub fn expr_bin_operation(context: &Context, exp: &Node) -> yul::Expression { if let fe::Expr::BinOperation { left, op, right } = &exp.kind { - let yul_left = expr(context, left)?; - let yul_right = expr(context, right)?; + let yul_left = expr(context, left); + let yul_right = expr(context, right); let typ = &context .get_expression(left) @@ -252,45 +236,45 @@ pub fn expr_bin_operation( return match op.kind { fe::BinOperator::Add => match typ { Type::Base(Base::Numeric(integer)) => { - Ok(expression! { [names::checked_add(integer)]([yul_left], [yul_right]) }) + expression! { [names::checked_add(integer)]([yul_left], [yul_right]) } } _ => unimplemented!("Addition for non-numeric types not yet supported"), }, fe::BinOperator::Sub => match typ { Type::Base(Base::Numeric(integer)) => { - Ok(expression! { [names::checked_sub(integer)]([yul_left], [yul_right]) }) + expression! { [names::checked_sub(integer)]([yul_left], [yul_right]) } } _ => unimplemented!("Subtraction for non-numeric types not yet supported"), }, fe::BinOperator::Mult => match typ { Type::Base(Base::Numeric(integer)) => { - Ok(expression! { [names::checked_mul(integer)]([yul_left], [yul_right]) }) + expression! { [names::checked_mul(integer)]([yul_left], [yul_right]) } } _ => unreachable!(), }, fe::BinOperator::Div => match typ { Type::Base(Base::Numeric(integer)) => { - Ok(expression! { [names::checked_div(integer)]([yul_left], [yul_right]) }) + expression! { [names::checked_div(integer)]([yul_left], [yul_right]) } } _ => unreachable!(), }, - fe::BinOperator::BitAnd => Ok(expression! { and([yul_left], [yul_right]) }), - fe::BinOperator::BitOr => Ok(expression! { or([yul_left], [yul_right]) }), - fe::BinOperator::BitXor => Ok(expression! { xor([yul_left], [yul_right]) }), - fe::BinOperator::LShift => Ok(expression! { shl([yul_right], [yul_left]) }), + fe::BinOperator::BitAnd => expression! { and([yul_left], [yul_right]) }, + fe::BinOperator::BitOr => expression! { or([yul_left], [yul_right]) }, + fe::BinOperator::BitXor => expression! { xor([yul_left], [yul_right]) }, + fe::BinOperator::LShift => expression! { shl([yul_right], [yul_left]) }, fe::BinOperator::RShift => match typ.is_signed_integer() { - true => Ok(expression! { sar([yul_right], [yul_left]) }), - false => Ok(expression! { shr([yul_right], [yul_left]) }), + true => expression! { sar([yul_right], [yul_left]) }, + false => expression! { shr([yul_right], [yul_left]) }, }, fe::BinOperator::Mod => match typ { Type::Base(Base::Numeric(integer)) => { - Ok(expression! { [names::checked_mod(integer)]([yul_left], [yul_right]) }) + expression! { [names::checked_mod(integer)]([yul_left], [yul_right]) } } _ => unreachable!(), }, fe::BinOperator::Pow => match typ { Type::Base(Base::Numeric(integer)) => { - Ok(expression! { [names::checked_exp(integer)]([yul_left], [yul_right]) }) + expression! { [names::checked_exp(integer)]([yul_left], [yul_right]) } } _ => unreachable!(), }, @@ -301,19 +285,16 @@ pub fn expr_bin_operation( unreachable!() } -pub fn expr_unary_operation( - context: &Context, - exp: &Node, -) -> Result { +pub fn expr_unary_operation(context: &Context, exp: &Node) -> yul::Expression { if let fe::Expr::UnaryOperation { op, operand } = &exp.kind { - let yul_operand = expr(context, operand)?; + let yul_operand = expr(context, operand); return match &op.kind { fe::UnaryOperator::USub => { let zero = literal_expression! {0}; - Ok(expression! { sub([zero], [yul_operand]) }) + expression! { sub([zero], [yul_operand]) } } - fe::UnaryOperator::Not => Ok(expression! { iszero([yul_operand]) }), + fe::UnaryOperator::Not => expression! { iszero([yul_operand]) }, _ => todo!(), }; } @@ -331,10 +312,7 @@ pub fn expr_name_str<'a>(exp: &Node>) -> &'a str { } /// Builds a Yul expression from the first slice, if it is an index. -pub fn slices_index( - context: &Context, - slices: &Node>>, -) -> Result { +pub fn slices_index(context: &Context, slices: &Node>>) -> yul::Expression { if let Some(first_slice) = slices.kind.first() { return slice_index(context, first_slice); } @@ -342,10 +320,7 @@ pub fn slices_index( unreachable!() } -pub fn slice_index( - context: &Context, - slice: &Node, -) -> Result { +pub fn slice_index(context: &Context, slice: &Node) -> yul::Expression { if let fe::Slice::Index(index) = &slice.kind { return expr(context, index); } @@ -353,12 +328,12 @@ pub fn slice_index( unreachable!() } -fn expr_tuple(exp: &Node) -> Result { +fn expr_tuple(exp: &Node) -> yul::Expression { if let fe::Expr::Tuple { elts } = &exp.kind { if !elts.is_empty() { todo!("Non empty Tuples aren't yet supported") } else { - return Ok(literal_expression! {0x0}); + return literal_expression! {0x0}; } } @@ -371,23 +346,23 @@ fn expr_name(exp: &Node) -> yul::Expression { identifier_expression! { [names::var_name(name)] } } -fn expr_num(exp: &Node) -> Result { +fn expr_num(exp: &Node) -> yul::Expression { if let fe::Expr::Num(num) = &exp.kind { - return Ok(literal_expression! {(num)}); + return literal_expression! {(num)}; } unreachable!() } -fn expr_bool(exp: &Node) -> Result { +fn expr_bool(exp: &Node) -> yul::Expression { if let fe::Expr::Bool(val) = &exp.kind { - return Ok(literal_expression! {(val)}); + return literal_expression! {(val)}; } unreachable!() } -fn expr_str(exp: &Node) -> Result { +fn expr_str(exp: &Node) -> yul::Expression { if let fe::Expr::Str(lines) = &exp.kind { let content = lines.join(""); let string_identifier = format!(r#""{}""#, keccak::full(content.as_bytes())); @@ -395,38 +370,32 @@ fn expr_str(exp: &Node) -> Result { let offset = expression! { dataoffset([literal_expression! { (string_identifier) }]) }; let size = expression! { datasize([literal_expression! { (string_identifier) }]) }; - return Ok(expression! {load_data_string([offset], [size])}); + return expression! {load_data_string([offset], [size])}; } unreachable!() } -fn expr_subscript( - context: &Context, - exp: &Node, -) -> Result { +fn expr_subscript(context: &Context, exp: &Node) -> yul::Expression { if let fe::Expr::Subscript { value, slices } = &exp.kind { if let Some(value_attributes) = context.get_expression(value) { - let value = expr(context, value)?; - let index = slices_index(context, slices)?; + let value = expr(context, value); + let index = slices_index(context, slices); return match value_attributes.typ.to_owned() { - Type::Map(_) => Ok(data_operations::keyed_map(value, index)), - Type::Array(array) => Ok(data_operations::indexed_array(array, value, index)), - _ => Err(CompileError::static_str("invalid attributes")), + Type::Map(_) => data_operations::keyed_map(value, index), + Type::Array(array) => data_operations::indexed_array(array, value, index), + _ => panic!("invalid attributes"), }; } - return Err(CompileError::static_str("missing attributes")); + panic!("missing attributes"); } unreachable!() } -fn expr_attribute( - context: &Context, - exp: &Node, -) -> Result { +fn expr_attribute(context: &Context, exp: &Node) -> yul::Expression { if let fe::Expr::Attribute { value, attr } = &exp.kind { // If the given value has expression attributes, we handle it as an expression // by first mapping the value and then performing the expected operation @@ -435,11 +404,11 @@ fn expr_attribute( // If the given value does not have expression attributes, we assume it is a // builtin object and map it as such. return if let Some(attributes) = context.get_expression(value) { - let value = expr(context, value)?; + let value = expr(context, value); match &attributes.typ { Type::Struct(struct_) => { - Ok(struct_operations::get_attribute(struct_, attr.kind, value)) + struct_operations::get_attribute(struct_, attr.kind, value) } _ => panic!("invalid attributes"), } @@ -447,34 +416,34 @@ fn expr_attribute( match Object::from_str(expr_name_str(value)) { Ok(Object::Self_) => expr_attribute_self(context, exp), Ok(Object::Block) => match BlockField::from_str(attr.kind) { - Ok(BlockField::Coinbase) => Ok(expression! { coinbase() }), - Ok(BlockField::Difficulty) => Ok(expression! { difficulty() }), - Ok(BlockField::Number) => Ok(expression! { number() }), - Ok(BlockField::Timestamp) => Ok(expression! { timestamp() }), - Err(_) => Err(CompileError::static_str("invalid `block` attribute name")), + Ok(BlockField::Coinbase) => expression! { coinbase() }, + Ok(BlockField::Difficulty) => expression! { difficulty() }, + Ok(BlockField::Number) => expression! { number() }, + Ok(BlockField::Timestamp) => expression! { timestamp() }, + Err(_) => panic!("invalid `block` attribute name"), }, Ok(Object::Chain) => match ChainField::from_str(attr.kind) { - Ok(ChainField::Id) => Ok(expression! { chainid() }), - Err(_) => Err(CompileError::static_str("invalid `chain` attribute name")), + Ok(ChainField::Id) => expression! { chainid() }, + Err(_) => panic!("invalid `chain` attribute name"), }, Ok(Object::Msg) => match MsgField::from_str(attr.kind) { Ok(MsgField::Data) => todo!(), - Ok(MsgField::Sender) => Ok(expression! { caller() }), - Ok(MsgField::Sig) => Ok(expression! { + Ok(MsgField::Sender) => expression! { caller() }, + Ok(MsgField::Sig) => expression! { and( [ expression! { calldataload(0) } ], [ expression! { shl(224, 0xffffffff) } ] ) - }), - Ok(MsgField::Value) => Ok(expression! { callvalue() }), - Err(_) => Err(CompileError::static_str("invalid `msg` attribute name")), + }, + Ok(MsgField::Value) => expression! { callvalue() }, + Err(_) => panic!("invalid `msg` attribute name"), }, Ok(Object::Tx) => match TxField::from_str(attr.kind) { - Ok(TxField::GasPrice) => Ok(expression! { gasprice() }), - Ok(TxField::Origin) => Ok(expression! { origin() }), - Err(_) => Err(CompileError::static_str("invalid `msg` attribute name")), + Ok(TxField::GasPrice) => expression! { gasprice() }, + Ok(TxField::Origin) => expression! { origin() }, + Err(_) => panic!("invalid `msg` attribute name"), }, - Err(_) => Err(CompileError::static_str("invalid attributes")), + Err(_) => panic!("invalid attributes"), } }; } @@ -482,13 +451,10 @@ fn expr_attribute( unreachable!() } -fn expr_attribute_self( - context: &Context, - exp: &Node, -) -> Result { +fn expr_attribute_self(context: &Context, exp: &Node) -> yul::Expression { if let fe::Expr::Attribute { attr, .. } = &exp.kind { if let Ok(builtins::SelfField::Address) = builtins::SelfField::from_str(attr.kind) { - return Ok(expression! { address() }); + return expression! { address() }; } } @@ -496,16 +462,16 @@ fn expr_attribute_self( let nonce = if let Location::Storage { nonce: Some(nonce) } = attributes.location { nonce } else { - return Err(CompileError::static_str("invalid attributes")); + panic!("invalid attributes"); }; return match attributes.typ { - Type::Map(_) => Ok(literal_expression! { (nonce) }), - _ => Ok(nonce_to_ptr(nonce)), + Type::Map(_) => literal_expression! { (nonce) }, + _ => nonce_to_ptr(nonce), }; } - Err(CompileError::static_str("missing attributes")) + panic!("missing attributes") } /// Converts a storage nonce into a pointer based on the keccak256 hash @@ -518,33 +484,30 @@ pub fn nonce_to_ptr(nonce: usize) -> yul::Expression { literal_expression! { (ptr) } } -fn expr_ternary(context: &Context, exp: &Node) -> Result { +fn expr_ternary(context: &Context, exp: &Node) -> yul::Expression { if let fe::Expr::Ternary { if_expr, test, else_expr, } = &exp.kind { - let yul_test_expr = expr(context, test)?; - let yul_if_expr = expr(context, if_expr)?; - let yul_else_expr = expr(context, else_expr)?; + let yul_test_expr = expr(context, test); + let yul_if_expr = expr(context, if_expr); + let yul_else_expr = expr(context, else_expr); - return Ok(expression! {ternary([yul_test_expr], [yul_if_expr], [yul_else_expr])}); + return expression! {ternary([yul_test_expr], [yul_if_expr], [yul_else_expr])}; } unreachable!() } -fn expr_bool_operation( - context: &Context, - exp: &Node, -) -> Result { +fn expr_bool_operation(context: &Context, exp: &Node) -> yul::Expression { if let fe::Expr::BoolOperation { left, op, right } = &exp.kind { - let yul_left = expr(context, left)?; - let yul_right = expr(context, right)?; + let yul_left = expr(context, left); + let yul_right = expr(context, right); return match op.kind { - fe::BoolOperator::And => Ok(expression! {and([yul_left], [yul_right])}), - fe::BoolOperator::Or => Ok(expression! {or([yul_left], [yul_right])}), + fe::BoolOperator::And => expression! {and([yul_left], [yul_right])}, + fe::BoolOperator::Or => expression! {or([yul_left], [yul_right])}, }; } diff --git a/compiler/src/yul/mappers/functions.rs b/compiler/src/yul/mappers/functions.rs index 946fc5dc7c..130c483495 100644 --- a/compiler/src/yul/mappers/functions.rs +++ b/compiler/src/yul/mappers/functions.rs @@ -1,4 +1,3 @@ -use crate::errors::CompileError; use crate::yul::mappers::{ assignments, declarations, @@ -21,18 +20,15 @@ use yultsur::*; pub fn multiple_func_stmt( context: &Context, statements: &[Node], -) -> Result, CompileError> { +) -> Vec { statements .iter() .map(|statement| func_stmt(context, statement)) - .collect::, _>>() + .collect() } /// Builds a Yul function definition from a Fe function definition. -pub fn func_def( - context: &Context, - def: &Node, -) -> Result { +pub fn func_def(context: &Context, def: &Node) -> yul::Statement { if let ( Some(attributes), fe::ContractStmt::FuncDef { @@ -46,20 +42,20 @@ pub fn func_def( { let function_name = names::func_name(name.kind); let param_names = args.iter().map(|arg| func_def_arg(arg)).collect::>(); - let function_statements = multiple_func_stmt(context, body)?; + let function_statements = multiple_func_stmt(context, body); return if attributes.return_type.is_empty_tuple() { - Ok(function_definition! { + function_definition! { function [function_name]([param_names...]) { [function_statements...] } - }) + } } else { - Ok(function_definition! { + function_definition! { function [function_name]([param_names...]) -> return_val { [function_statements...] } - }) + } }; } @@ -72,7 +68,7 @@ fn func_def_arg(arg: &Node) -> yul::Identifier { names::var_name(name) } -fn func_stmt(context: &Context, stmt: &Node) -> Result { +fn func_stmt(context: &Context, stmt: &Node) -> yul::Statement { match &stmt.kind { fe::FuncStmt::Return { .. } => func_return(context, stmt), fe::FuncStmt::VarDecl { .. } => declarations::var_decl(context, stmt), @@ -84,14 +80,14 @@ fn func_stmt(context: &Context, stmt: &Node) -> Result if_statement(context, stmt), fe::FuncStmt::Assert { .. } => assert(context, stmt), fe::FuncStmt::Expr { .. } => expr(context, stmt), - fe::FuncStmt::Pass => Ok(statement! { pop(0) }), + fe::FuncStmt::Pass => statement! { pop(0) }, fe::FuncStmt::Break => break_statement(context, stmt), fe::FuncStmt::Continue => continue_statement(context, stmt), fe::FuncStmt::Revert => revert(stmt), } } -fn for_loop(context: &Context, stmt: &Node) -> Result { +fn for_loop(context: &Context, stmt: &Node) -> yul::Statement { if let fe::FuncStmt::For { target, iter, @@ -99,9 +95,9 @@ fn for_loop(context: &Context, stmt: &Node) -> Result) -> Result, -) -> Result { +fn if_statement(context: &Context, stmt: &Node) -> yul::Statement { if let fe::FuncStmt::If { test, body, or_else, } = &stmt.kind { - let yul_test = expressions::expr(context, &test)?; - let yul_body = multiple_func_stmt(context, body)?; - let yul_or_else = multiple_func_stmt(context, or_else)?; + let yul_test = expressions::expr(context, &test); + let yul_body = multiple_func_stmt(context, body); + let yul_or_else = multiple_func_stmt(context, or_else); - return Ok(switch! { + return switch! { switch ([yul_test]) (case 1 {[yul_body...]}) (case 0 {[yul_or_else...]}) - }); + }; } unreachable!() } -fn expr(context: &Context, stmt: &Node) -> Result { +fn expr(context: &Context, stmt: &Node) -> yul::Statement { if let fe::FuncStmt::Expr { value } = &stmt.kind { - let expr = expressions::expr(context, value)?; + let expr = expressions::expr(context, value); let attributes = context.get_expression(value).expect("missing attributes"); return if attributes.typ.is_empty_tuple() { - Ok(yul::Statement::Expression(expr)) + yul::Statement::Expression(expr) } else { - Ok(statement! { pop([expr])}) + statement! { pop([expr])} }; } unreachable!() } -fn revert(stmt: &Node) -> Result { +fn revert(stmt: &Node) -> yul::Statement { if let fe::FuncStmt::Revert = &stmt.kind { - return Ok(statement! { revert(0, 0) }); + return statement! { revert(0, 0) }; } unreachable!() } -fn emit(context: &Context, stmt: &Node) -> Result { +fn emit(context: &Context, stmt: &Node) -> yul::Statement { if let fe::FuncStmt::Emit { value } = &stmt.kind { if let fe::Expr::Call { func: _, args } = &value.kind { let event_values = args .kind .iter() .map(|arg| expressions::call_arg(context, arg)) - .collect::>()?; + .collect(); if let Some(event) = context.get_emit(stmt) { - return Ok(data_operations::emit_event(event.to_owned(), event_values)); + return data_operations::emit_event(event.to_owned(), event_values); } - return Err(CompileError::static_str("missing event definition")); + panic!("missing event definition"); } - return Err(CompileError::static_str( - "emit statements must contain a call expression", - )); + panic!("emit statements must contain a call expression",); } unreachable!() } -fn assert(context: &Context, stmt: &Node) -> Result { +fn assert(context: &Context, stmt: &Node) -> yul::Statement { if let fe::FuncStmt::Assert { test, msg: _ } = &stmt.kind { - let test = expressions::expr(context, test)?; + let test = expressions::expr(context, test); - return Ok(statement! { if (iszero([test])) { (revert(0, 0)) } }); + return statement! { if (iszero([test])) { (revert(0, 0)) } }; } unreachable!() } -fn break_statement( - _context: &Context, - stmt: &Node, -) -> Result { +fn break_statement(_context: &Context, stmt: &Node) -> yul::Statement { if let fe::FuncStmt::Break {} = &stmt.kind { - return Ok(statement! { break }); + return statement! { break }; } unreachable!() } -fn continue_statement( - _context: &Context, - stmt: &Node, -) -> Result { +fn continue_statement(_context: &Context, stmt: &Node) -> yul::Statement { if let fe::FuncStmt::Continue {} = &stmt.kind { - return Ok(statement! { continue }); + return statement! { continue }; } unreachable!() } -fn func_return( - context: &Context, - stmt: &Node, -) -> Result { +fn func_return(context: &Context, stmt: &Node) -> yul::Statement { if let fe::FuncStmt::Return { value } = &stmt.kind { return match value { Some(value) => { // Ensure `return ()` is handled as if the function does not return let attributes = context.get_expression(value).expect("Missing attributes"); if attributes.is_empty_tuple() { - return Ok(statement! { leave }); + return statement! { leave }; } - let value = expressions::expr(context, value)?; - return Ok(block_statement! { + let value = expressions::expr(context, value); + return block_statement! { (return_val := [value]) (leave) - }); + }; } - None => Ok(statement! { leave }), + None => statement! { leave }, }; } unreachable!() } -fn while_loop( - context: &Context, - stmt: &Node, -) -> Result { +fn while_loop(context: &Context, stmt: &Node) -> yul::Statement { if let fe::FuncStmt::While { test, body, or_else: _, } = &stmt.kind { - let test = expressions::expr(context, test)?; - let yul_body = multiple_func_stmt(context, body)?; + let test = expressions::expr(context, test); + let yul_body = multiple_func_stmt(context, body); - return Ok(block_statement! { + return block_statement! { (for {} ([test]) {} { [yul_body...] }) - }); + }; } unreachable!() diff --git a/compiler/src/yul/mappers/module.rs b/compiler/src/yul/mappers/module.rs index 53a36fc86c..6e497549f2 100644 --- a/compiler/src/yul/mappers/module.rs +++ b/compiler/src/yul/mappers/module.rs @@ -1,4 +1,3 @@ -use crate::errors::CompileError; use crate::yul::mappers::contracts; use fe_analyzer::Context; use fe_parser::ast as fe; @@ -8,11 +7,11 @@ use yultsur::yul; pub type YulContracts = HashMap; /// Builds a vector of Yul contracts from a Fe module. -pub fn module(context: &Context, module: &fe::Module) -> Result { +pub fn module(context: &Context, module: &fe::Module) -> YulContracts { module .body .iter() - .try_fold(YulContracts::new(), |mut contracts, stmt| { + .fold(YulContracts::new(), |mut contracts, stmt| { match &stmt.kind { fe::ModuleStmt::TypeDef { .. } => {} fe::ModuleStmt::ContractDef { name, .. } => { @@ -26,7 +25,7 @@ pub fn module(context: &Context, module: &fe::Module) -> Result>(); - let contract = contracts::contract_def(context, stmt, created_contracts)?; + let contract = contracts::contract_def(context, stmt, created_contracts); if contracts.insert(name.kind.to_string(), contract).is_some() { panic!("duplicate contract definition"); @@ -37,6 +36,6 @@ pub fn module(context: &Context, module: &fe::Module) -> Result unimplemented!(), } - Ok(contracts) + contracts }) } diff --git a/compiler/src/yul/mod.rs b/compiler/src/yul/mod.rs index 27d405b2db..d3e938badb 100644 --- a/compiler/src/yul/mod.rs +++ b/compiler/src/yul/mod.rs @@ -1,6 +1,5 @@ //! Fe to Yul compiler. -use crate::errors::CompileError; use crate::types::{ FeModuleAst, NamedYulContracts, @@ -16,9 +15,14 @@ pub mod runtime; mod utils; /// Compiles Fe source code to Yul. -pub fn compile(context: &Context, module: &FeModuleAst) -> Result { - Ok(mappers::module::module(context, module)? +/// +/// # Panics +/// +/// Any failure to compile an AST to Yul is considered a bug, and thus panics. +/// Invalid ASTs should be caught by an analysis step prior to Yul generation. +pub fn compile(context: &Context, module: &FeModuleAst) -> NamedYulContracts { + mappers::module::module(context, module) .drain() .map(|(name, object)| (name, object.to_string().replace("\"", "\\\""))) - .collect::()) + .collect::() } diff --git a/compiler/src/yul/operations/abi.rs b/compiler/src/yul/operations/abi.rs index a63a9f8f67..59276991d3 100644 --- a/compiler/src/yul/operations/abi.rs +++ b/compiler/src/yul/operations/abi.rs @@ -56,7 +56,11 @@ pub fn encode_size(types: Vec, vals: Vec) -> } /// Returns an expression that gives the max size of the encoded values. -pub fn static_encode_size(types: Vec) -> Result { +/// +/// # Panics +/// This will panic if any of the elements of the `types` vector have dynamic +/// size. +pub fn static_encode_size(types: Vec) -> yul::Expression { let mut static_size = 0; for typ in types { @@ -75,10 +79,7 @@ pub fn static_encode_size(types: Vec) -> Result { - return Err( - "tried to get the static encoding size of dynamically sized value" - .to_string(), - ) + panic!("tried to get the static encoding size of dynamically sized value") } } } @@ -86,7 +87,7 @@ pub fn static_encode_size(types: Vec) -> Result Vec { } } else { let decoding_size = - abi_operations::static_encode_size(vec![function.return_type.clone()]) - .expect("failed to get the static encoding size"); + abi_operations::static_encode_size(vec![function.return_type.clone()]); let decoding_operation = abi_operations::decode( vec![function.return_type], identifier_expression! { outstart }, From 935b1b49c2b2340a393447d16713a60b6ee69ef1 Mon Sep 17 00:00:00 2001 From: Sean Billig Date: Tue, 23 Mar 2021 12:44:49 -0700 Subject: [PATCH 3/3] Add newsfragments/327.internal.md --- newsfragments/327.internal.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 newsfragments/327.internal.md diff --git a/newsfragments/327.internal.md b/newsfragments/327.internal.md new file mode 100644 index 0000000000..b6a21f720b --- /dev/null +++ b/newsfragments/327.internal.md @@ -0,0 +1 @@ +Failures in the Yul generation phase now panic; any failure is a bug.