diff --git a/crates/analyzer/src/db.rs b/crates/analyzer/src/db.rs index a0ece832c7..e81f1d8a07 100644 --- a/crates/analyzer/src/db.rs +++ b/crates/analyzer/src/db.rs @@ -195,6 +195,9 @@ pub trait AnalyzerDb: SourceDb + Upcast + UpcastMut #[salsa::invoke(queries::types::type_alias_type)] #[salsa::cycle(queries::types::type_alias_type_cycle)] fn type_alias_type(&self, id: TypeAliasId) -> Analysis>; + #[salsa::invoke(queries::types::is_zero_sized)] + #[salsa::cycle(queries::types::is_zero_sized_cycle)] + fn is_zero_sized(&self, ty: TypeId) -> bool; } #[salsa::database(AnalyzerDbStorage, SourceDbStorage)] diff --git a/crates/analyzer/src/db/queries/events.rs b/crates/analyzer/src/db/queries/events.rs index fd7a4548b4..529f32941c 100644 --- a/crates/analyzer/src/db/queries/events.rs +++ b/crates/analyzer/src/db/queries/events.rs @@ -40,13 +40,23 @@ pub fn event_type(db: &dyn AnalyzerDb, event: EventId) -> Analysis Ok(typ), + typ if typ.has_fixed_size(scope.db()) => { + if *is_idx && typ.is_zero_sized(db) { + Err(TypeError::new(scope.error( + "expected a non zero sized type for an indexed event field", + typ_node.span, + "this must be a non zero sized type", + ))) + } else { + Ok(typ) + } + } _ => Err(TypeError::new(scope.error( "event field type must have a fixed size", typ_node.span, "this can't be used as an event field", ))), - }); + }) // If we've already seen the max number of indexed fields, // ignore the `idx` qualifier on this one. We'll emit an error below. diff --git a/crates/analyzer/src/db/queries/types.rs b/crates/analyzer/src/db/queries/types.rs index 91bfb35619..9f2e8310a6 100644 --- a/crates/analyzer/src/db/queries/types.rs +++ b/crates/analyzer/src/db/queries/types.rs @@ -7,7 +7,7 @@ use crate::db::Analysis; use crate::errors::TypeError; use crate::namespace::items::{FunctionSigId, ImplId, TraitId, TypeAliasId}; use crate::namespace::scopes::ItemScope; -use crate::namespace::types::{self, TypeId}; +use crate::namespace::types::{self, Array, Base, FeString, Tuple, Type, TypeId}; use crate::traversal::types::type_desc; use crate::AnalyzerDb; @@ -69,3 +69,36 @@ pub fn type_alias_type_cycle( Analysis::new(err, context.diagnostics.take().into()) } + +pub fn is_zero_sized(db: &dyn AnalyzerDb, ty: types::TypeId) -> bool { + let typ = db.lookup_intern_type(ty); + match typ { + Type::Base(base) => matches!(base, Base::Unit), + Type::Array(Array { size, inner }) => size == 0 || inner.is_zero_sized(db), + Type::Tuple(Tuple { items }) => { + for item in items.iter() { + if !db.is_zero_sized(*item) { + return false; + } + } + true + } + Type::String(FeString { max_size }) => max_size == 0, + Type::Struct(struct_id) => { + for field_type_id in db.struct_all_fields(struct_id).iter() { + // assume all type is correct. + let field_type = db.struct_field_type(*field_type_id).value.unwrap(); + if !(field_type).is_zero_sized(db) { + return false; + } + } + true + } + Type::Generic(_) | Type::Map(_) | Type::SelfContract(_) | Type::Contract(_) => false, + } +} + +// because we already catch this error so this should be empty +pub fn is_zero_sized_cycle(_db: &dyn AnalyzerDb, _cycle: &[String], _ty: &types::TypeId) -> bool { + true +} diff --git a/crates/analyzer/src/namespace/types.rs b/crates/analyzer/src/namespace/types.rs index 36f75d37a4..cae3a37f5e 100644 --- a/crates/analyzer/src/namespace/types.rs +++ b/crates/analyzer/src/namespace/types.rs @@ -84,6 +84,9 @@ impl TypeId { pub fn is_base(&self, db: &dyn AnalyzerDb) -> bool { self.typ(db).is_base() } + pub fn is_zero_sized(&self, db: &dyn AnalyzerDb) -> bool { + db.is_zero_sized(*self) + } pub fn is_bool(&self, db: &dyn AnalyzerDb) -> bool { matches!(self.typ(db), Type::Base(Base::Bool)) } diff --git a/crates/analyzer/tests/errors.rs b/crates/analyzer/tests/errors.rs index c5609ec922..ad4396865e 100644 --- a/crates/analyzer/tests/errors.rs +++ b/crates/analyzer/tests/errors.rs @@ -255,6 +255,7 @@ test_file! { external_call_type_error } test_file! { external_call_wrong_number_of_params } test_file! { contract_function_with_generic_params } test_file! { indexed_event } +test_file! { invalid_type_in_event_zero_sized } test_file! { invalid_compiler_version } test_file! { invalid_block_field } test_file! { invalid_chain_field } diff --git a/crates/analyzer/tests/snapshots/errors__invalid_type_in_event_zero_sized.snap b/crates/analyzer/tests/snapshots/errors__invalid_type_in_event_zero_sized.snap new file mode 100644 index 0000000000..856f2c0e71 --- /dev/null +++ b/crates/analyzer/tests/snapshots/errors__invalid_type_in_event_zero_sized.snap @@ -0,0 +1,77 @@ +--- +source: crates/analyzer/tests/errors.rs +expression: "error_string(&path, test_files::fixture(path))" +--- +error: recursive struct `Foo` + ┌─ compile_errors/invalid_type_in_event_zero_sized.fe:12:8 + │ +12 │ struct Foo { + │ ^^^ struct `Foo` has infinite size due to recursive definition + +error: recursive struct `Baz` + ┌─ compile_errors/invalid_type_in_event_zero_sized.fe:16:8 + │ +16 │ struct Baz { + │ ^^^ struct `Baz` has infinite size due to recursive definition + +error: event field type must have a non-zero types + ┌─ compile_errors/invalid_type_in_event_zero_sized.fe:22:13 + │ +22 │ idx a : () + │ ^^ this type can't be used as an event field + +error: event field type must have a non-zero types + ┌─ compile_errors/invalid_type_in_event_zero_sized.fe:24:13 + │ +24 │ idx c : EmptyStruct + │ ^^^^^^^^^^^ this type can't be used as an event field + +error: event field type must have a non-zero types + ┌─ compile_errors/invalid_type_in_event_zero_sized.fe:29:12 + │ +29 │ idx b: ((), EmptyStruct) + │ ^^^^^^^^^^^^^^^^^ this type can't be used as an event field + +error: event field type must have a non-zero types + ┌─ compile_errors/invalid_type_in_event_zero_sized.fe:30:12 + │ +30 │ idx c: Array + │ ^^^^^^^^^^^^^^^^^^^^^ this type can't be used as an event field + +error: event field type must have a non-zero types + ┌─ compile_errors/invalid_type_in_event_zero_sized.fe:34:12 + │ +34 │ idx a: Array<(), 0> + │ ^^^^^^^^^^^^ this type can't be used as an event field + +error: event field type must have a non-zero types + ┌─ compile_errors/invalid_type_in_event_zero_sized.fe:35:12 + │ +35 │ idx b: Array + │ ^^^^^^^^^^^^^^^^^^^^^^^ this type can't be used as an event field + +error: event field type must have a non-zero types + ┌─ compile_errors/invalid_type_in_event_zero_sized.fe:39:12 + │ +39 │ idx a: Array<(), 0> + │ ^^^^^^^^^^^^ this type can't be used as an event field + +error: event field type must have a non-zero types + ┌─ compile_errors/invalid_type_in_event_zero_sized.fe:40:12 + │ +40 │ idx b: AnotherEmpty + │ ^^^^^^^^^^^^ this type can't be used as an event field + +error: event field type must have a non-zero types + ┌─ compile_errors/invalid_type_in_event_zero_sized.fe:47:12 + │ +47 │ idx f: Foo + │ ^^^ this type can't be used as an event field + +error: event field type is invalid + ┌─ compile_errors/invalid_type_in_event_zero_sized.fe:52:8 + │ +52 │ b: Map + │ ^^^^^^^^^^^^^^ this type can't be used as an event field + + diff --git a/crates/common/src/panic.rs b/crates/common/src/panic.rs index eccfbd8a7c..83736b1e09 100644 --- a/crates/common/src/panic.rs +++ b/crates/common/src/panic.rs @@ -1,13 +1,13 @@ use once_cell::sync::Lazy; use std::panic; +type PanicHook = dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static; 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(report_ice)); - hook - }); +static DEFAULT_PANIC_HOOK: Lazy> = Lazy::new(|| { + let hook = panic::take_hook(); + panic::set_hook(Box::new(report_ice)); + hook +}); pub fn install_panic_hook() { Lazy::force(&DEFAULT_PANIC_HOOK); diff --git a/crates/test-files/fixtures/compile_errors/invalid_type_in_event_zero_sized.fe b/crates/test-files/fixtures/compile_errors/invalid_type_in_event_zero_sized.fe new file mode 100644 index 0000000000..d62f75c02d --- /dev/null +++ b/crates/test-files/fixtures/compile_errors/invalid_type_in_event_zero_sized.fe @@ -0,0 +1,53 @@ +struct EmptyStruct {} +contract Contract {} + +struct UnEmptyStruct { + v: u32 +} + +struct AnotherEmpty { + x: EmptyStruct +} + +struct Foo { + x: Baz +} + +struct Baz { + x: Foo +} + + +event FirstCase { + idx a : () + idx b : Contract + idx c : EmptyStruct +} + +event SecondCase { + idx a: u256 + idx b: ((), EmptyStruct) + idx c: Array +} + +event ThirdCase { + idx a: Array<(), 0> + idx b: Array +} + +event FifthCase { + idx a: Array<(), 0> + idx b: AnotherEmpty + idx c: Array +} + +event SixthCase { + idx d: UnEmptyStruct + idx e: ((), UnEmptyStruct) + idx f: Foo +} + +event SeventhCase { + idx a: u256 + b: Map +} \ No newline at end of file