Skip to content

Commit e566935

Browse files
fixed dict values its possible to create enums as dictionary values (#1637)
1 parent 05155aa commit e566935

1 file changed

Lines changed: 109 additions & 29 deletions

File tree

src/types.rs

Lines changed: 109 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ use cairo_lang_sierra::{
1616
starknet::StarknetTypeConcrete,
1717
utils::Range,
1818
},
19-
ids::{ConcreteTypeId, UserTypeId},
20-
program::GenericArg,
19+
ids::ConcreteTypeId,
2120
program_registry::ProgramRegistry,
2221
};
2322
use melior::{
@@ -28,7 +27,7 @@ use melior::{
2827
};
2928
use num_bigint::{BigInt, Sign};
3029
use num_traits::{Bounded, One};
31-
use std::{alloc::Layout, error::Error, ops::Deref, sync::OnceLock};
30+
use std::{alloc::Layout, error::Error, ops::Deref};
3231

3332
pub mod array;
3433
mod bitwise;
@@ -956,31 +955,25 @@ impl TypeBuilder for CoreTypeConcrete {
956955
_metadata: &mut MetadataStorage,
957956
_self_ty: &ConcreteTypeId,
958957
) -> Result<Value<'ctx, 'this>, Self::Error> {
959-
static BOOL_USER_TYPE_ID: OnceLock<UserTypeId> = OnceLock::new();
960-
let bool_user_type_id =
961-
BOOL_USER_TYPE_ID.get_or_init(|| UserTypeId::from_string("core::bool"));
962-
963958
Ok(match self {
964-
Self::Enum(info) => match &info.info.long_id.generic_args[0] {
965-
GenericArg::UserType(id) if id == bool_user_type_id => {
966-
let tag = entry.const_int(context, location, 0, 1)?;
967-
968-
let value = entry.append_op_result(llvm::undef(
969-
llvm::r#type::r#struct(
970-
context,
971-
&[
972-
IntegerType::new(context, 1).into(),
973-
llvm::r#type::array(IntegerType::new(context, 8).into(), 0),
974-
],
975-
false,
976-
),
977-
location,
978-
))?;
979-
980-
entry.insert_value(context, location, value, tag, 0)?
981-
}
982-
_ => native_panic!("unsupported dict value type"),
983-
},
959+
Self::Enum(_) => {
960+
// Sierra validates that only enums with two or less values are allowed, and that they are zero size.
961+
let tag = entry.const_int(context, location, 0, 1)?;
962+
963+
let value = entry.append_op_result(llvm::undef(
964+
llvm::r#type::r#struct(
965+
context,
966+
&[
967+
IntegerType::new(context, 1).into(),
968+
llvm::r#type::array(IntegerType::new(context, 8).into(), 0),
969+
],
970+
false,
971+
),
972+
location,
973+
))?;
974+
975+
entry.insert_value(context, location, value, tag, 0)?
976+
}
984977
Self::Felt252(_) => entry.const_int(context, location, 0, 252)?,
985978
Self::Nullable(_) => {
986979
entry.append_op_result(llvm::zero(llvm::r#type::pointer(context, 0), location))?
@@ -1028,11 +1021,19 @@ impl<T> Deref for WithSelf<'_, T> {
10281021
#[cfg(test)]
10291022
mod test {
10301023
use super::TypeBuilder;
1031-
use crate::utils::testing::load_program;
1024+
use crate::{
1025+
context::initialize_mlir, libfuncs::LibfuncHelper, load_cairo, metadata::MetadataStorage,
1026+
utils::testing::load_program,
1027+
};
10321028
use cairo_lang_sierra::{
1033-
extensions::core::{CoreLibfunc, CoreType},
1029+
extensions::core::{CoreLibfunc, CoreType, CoreTypeConcrete},
10341030
program_registry::ProgramRegistry,
10351031
};
1032+
use melior::{
1033+
dialect::llvm,
1034+
ir::{r#type::IntegerType, Block, Location, Module, Region, ValueLike},
1035+
};
1036+
use std::cell::Cell;
10361037

10371038
#[test]
10381039
fn ensure_padded_layouts() {
@@ -1045,4 +1046,83 @@ mod test {
10451046
assert_eq!(layout, layout.pad_to_align());
10461047
}
10471048
}
1049+
1050+
/// `build_default` produces the value used when a missing `Felt252Dict` key is accessed.
1051+
/// The Sierra spec allows any enum with two zero-sized-payload variants
1052+
/// as a dict value type (e.g. `Option<()>`, `Result<(), ()>`, or any user-defined unit enum).
1053+
#[test]
1054+
fn build_default_two_variant_unit_enum() {
1055+
// A user-defined enum with two unit (zero-sized) variants, just like `bool`.
1056+
let (_, program) = load_cairo! {
1057+
#[derive(Drop)]
1058+
enum Color {
1059+
Red,
1060+
Green,
1061+
}
1062+
1063+
fn run_test() -> Color {
1064+
Color::Red
1065+
}
1066+
};
1067+
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(&program).unwrap();
1068+
1069+
// Find the user-defined enum with two zero-sized-payload variants (`Color`).
1070+
let (self_ty, enum_ty) = program
1071+
.type_declarations
1072+
.iter()
1073+
.map(|decl| (&decl.id, registry.get_type(&decl.id).unwrap()))
1074+
.find(|(_, ty)| {
1075+
matches!(ty, CoreTypeConcrete::Enum(info)
1076+
if info.variants.len() == 2
1077+
&& info
1078+
.variants
1079+
.iter()
1080+
.all(|v| registry.get_type(v).unwrap().is_zst(&registry).unwrap()))
1081+
})
1082+
.expect("the program must declare a two-variant unit enum");
1083+
1084+
let context = initialize_mlir();
1085+
let location = Location::unknown(&context);
1086+
let module = Module::new(location);
1087+
1088+
let region = Region::new();
1089+
let block = region.append_block(Block::new(&[]));
1090+
let blocks_arena = bumpalo::Bump::new();
1091+
let mut metadata = MetadataStorage::new();
1092+
let helper = LibfuncHelper {
1093+
module: &module,
1094+
init_block: &block,
1095+
region: &region,
1096+
blocks_arena: &blocks_arena,
1097+
last_block: Cell::new(&block),
1098+
branches: Vec::new(),
1099+
results: Vec::new(),
1100+
1101+
#[cfg(feature = "with-libfunc-profiling")]
1102+
profiler: None,
1103+
};
1104+
1105+
let value = enum_ty
1106+
.build_default(
1107+
&context,
1108+
&registry,
1109+
&block,
1110+
location,
1111+
&helper,
1112+
&mut metadata,
1113+
self_ty,
1114+
)
1115+
.expect("build_default must not panic for a two-variant unit enum");
1116+
1117+
// The default value matches the enum's `{ i1, [0 x i8] }` representation, with tag 0.
1118+
let expected_ty = llvm::r#type::r#struct(
1119+
&context,
1120+
&[
1121+
IntegerType::new(&context, 1).into(),
1122+
llvm::r#type::array(IntegerType::new(&context, 8).into(), 0),
1123+
],
1124+
false,
1125+
);
1126+
assert_eq!(value.r#type(), expected_ty);
1127+
}
10481128
}

0 commit comments

Comments
 (0)