diff --git a/compiler/ast/src/interpreter_value/core_function.rs b/compiler/ast/src/interpreter_value/core_function.rs index b362327d5c2..835854f53ac 100644 --- a/compiler/ast/src/interpreter_value/core_function.rs +++ b/compiler/ast/src/interpreter_value/core_function.rs @@ -28,6 +28,7 @@ use crate::{ CoreFunction, Expression, Type, + halt2, interpreter_value::{ExpectTc, Value}, tc_fail2, }; @@ -300,12 +301,22 @@ pub fn evaluate_core_function( helper.mapping_get(program, name, &key).is_some().into() } CoreFunction::OptionalUnwrap => { - // TODO - return Ok(None); + let (is_some, val) = unpack_option_struct(helper.pop_value().expect_tc(span)?.contents, span)?; + + if is_some { + Value { id: None, contents: ValueVariants::Svm(val.into()) } + } else { + halt2!(span, "called unwrap on a none value") + } } CoreFunction::OptionalUnwrapOr => { - // TODO - return Ok(None); + let (is_some, val) = unpack_option_struct(helper.pop_value().expect_tc(span)?.contents, span)?; + + let ValueVariants::Svm(SvmValue::Plaintext(or)) = helper.pop_value().expect_tc(span)?.contents else { + tc_fail2!() + }; + + Value { id: None, contents: ValueVariants::Svm(if is_some { val.into() } else { or.into() }) } } CoreFunction::VectorPush | CoreFunction::VectorLen @@ -333,3 +344,18 @@ pub fn evaluate_core_function( Ok(Some(value)) } + +fn unpack_option_struct(v: ValueVariants, span: Span) -> Result<(bool, SvmPlaintext)> { + let ValueVariants::Svm(SvmValue::Plaintext(Plaintext::Struct(members, _))) = v else { tc_fail2!() }; + + let get = |name: &str| { + let id = Symbol::intern(name).to_string().parse::().expect("type checker failure"); + members.get(&id).expect_tc(span) + }; + + let SvmPlaintext::Literal(SvmLiteral::Boolean(is_some), _) = get("is_some")? else { tc_fail2!() }; + + let val = get("val")?.clone(); + + Ok((**is_some, val)) +} diff --git a/compiler/ast/src/statement/definition/mod.rs b/compiler/ast/src/statement/definition/mod.rs index 4e6d7676f55..35f6c6047d2 100644 --- a/compiler/ast/src/statement/definition/mod.rs +++ b/compiler/ast/src/statement/definition/mod.rs @@ -22,7 +22,7 @@ use itertools::Itertools as _; use serde::{Deserialize, Serialize}; use std::fmt; -/// A `let` or `const` declaration statement. +/// A `let` declaration statement. #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] pub struct DefinitionStatement { /// The bindings / variable names to declare. diff --git a/compiler/passes/src/const_propagation/ast.rs b/compiler/passes/src/const_propagation/ast.rs index cc3881b0c7b..888dd9fc867 100644 --- a/compiler/passes/src/const_propagation/ast.rs +++ b/compiler/passes/src/const_propagation/ast.rs @@ -405,11 +405,9 @@ impl AstReconstructor for ConstPropagationVisitor<'_> { ) -> (Expression, Self::AdditionalOutput) { let type_info = self.state.type_table.get(&input.id()); - // If this is an optional, then unwrap it first. - let type_info = type_info.as_ref().map(|ty| match ty { - Type::Optional(opt) => *opt.inner.clone(), - _ => ty.clone(), - }); + if let Some(Type::Optional(_)) = type_info { + return (input.into(), None); + } if let Ok(value) = interpreter_value::literal_to_value(&input, &type_info) { // If we know the type of an unsuffixed literal, might as well change it to a suffixed literal. This way, we @@ -536,7 +534,26 @@ impl AstReconstructor for ConstPropagationVisitor<'_> { } fn reconstruct_const(&mut self, mut input: ConstDeclaration) -> (Statement, Self::AdditionalOutput) { - if matches!(input.type_, Type::Optional(_)) { + // If there is any optional in the type definition, leave it for now. + fn recursive_optional(type_: &Type, slf: &ConstPropagationVisitor) -> bool { + match type_ { + Type::Array(array_type) => recursive_optional(array_type.element_type(), slf), + Type::Composite(composite_type) => { + if let Some(cmp) = + slf.state.symbol_table.lookup_struct(composite_type.path.absolute_path().as_ref()) + { + cmp.members.iter().any(|mbr| recursive_optional(&mbr.type_, slf)) + } else { + false + } + } + Type::Optional(_) => true, + Type::Tuple(tuple_type) => tuple_type.elements.iter().any(|element| recursive_optional(element, slf)), + _ => false, + } + } + + if recursive_optional(&input.type_, self) { return (input.into(), None); } diff --git a/compiler/passes/src/option_lowering/mod.rs b/compiler/passes/src/option_lowering/mod.rs index f6f652d6776..4e5b534240c 100644 --- a/compiler/passes/src/option_lowering/mod.rs +++ b/compiler/passes/src/option_lowering/mod.rs @@ -57,7 +57,15 @@ //! After this pass, no `T?` types remain in the program: all optional values are represented explicitly //! as structs with `is_some` and `val` fields. -use crate::{Pass, PathResolution, SymbolTable, SymbolTableCreation, TypeChecking, TypeCheckingInput}; +use crate::{ + ConstPropagation, + Pass, + PathResolution, + SymbolTable, + SymbolTableCreation, + TypeChecking, + TypeCheckingInput, +}; use leo_ast::{ArrayType, CompositeType, ProgramReconstructor as _, Type}; use leo_errors::Result; @@ -101,6 +109,8 @@ impl Pass for OptionLowering { PathResolution::do_pass((), state)?; SymbolTableCreation::do_pass((), state)?; TypeChecking::do_pass(input.clone(), state)?; + // Now there are no more optionals, we can now evaluate the core unwrap functions of const optionals in the interpreter. + ConstPropagation::do_pass((), state)?; Ok(()) } diff --git a/compiler/passes/src/storage_lowering/ast.rs b/compiler/passes/src/storage_lowering/ast.rs index 0b6a3315637..2ec331248bc 100644 --- a/compiler/passes/src/storage_lowering/ast.rs +++ b/compiler/passes/src/storage_lowering/ast.rs @@ -15,6 +15,7 @@ // along with the Leo library. If not, see . use super::StorageLoweringVisitor; +use crate::VariableType; use leo_ast::*; use leo_span::{Span, Symbol, sym}; @@ -654,10 +655,12 @@ impl leo_ast::AstReconstructor for StorageLoweringVisitor<'_> { fn reconstruct_path(&mut self, input: Path, _additional: &()) -> (Expression, Self::AdditionalOutput) { // Check if this path corresponds to a global symbol. - let Some(var) = self.state.symbol_table.lookup_global(&Location::new(self.program, input.absolute_path())) - else { - // Nothing to do - return (input.into(), vec![]); + let var = match self.state.symbol_table.lookup_global(&Location::new(self.program, input.absolute_path())) { + Some(var) if var.declaration == VariableType::Storage => var, + _ => { + // Nothing to do + return (input.into(), vec![]); + } }; match &var.type_ { diff --git a/compiler/passes/src/type_checking/ast.rs b/compiler/passes/src/type_checking/ast.rs index 6b2fd8db52e..04713e97355 100644 --- a/compiler/passes/src/type_checking/ast.rs +++ b/compiler/passes/src/type_checking/ast.rs @@ -1971,12 +1971,6 @@ impl AstVisitor for TypeCheckingVisitor<'_> { fn visit_const(&mut self, input: &ConstDeclaration) { self.visit_type(&input.type_); - // For now, consts that contain optional types are not supported. - // TODO: remove this restriction by supporting const evaluation of optionals including `None`. - if self.contains_optional_type(&input.type_) { - self.emit_err(TypeCheckerError::const_cannot_be_optional(input.span)); - } - // Check that the type of the definition is not a unit type, singleton tuple type, or nested tuple type. match &input.type_ { // If the type is an empty tuple, return an error. diff --git a/tests/expectations/compiler/option/bad_types_fail.out b/tests/expectations/compiler/option/bad_types_fail.out index 200df1e889e..40a1c9b0079 100644 --- a/tests/expectations/compiler/option/bad_types_fail.out +++ b/tests/expectations/compiler/option/bad_types_fail.out @@ -1,8 +1,3 @@ -Error [ETYC0372163]: Constants cannot have an optional type or a type that contains an optional - --> compiler-test:16:5 - | - 16 | const BAD_CONST_ARRAY: [u8?; 2] = [1u8, 2i8]; // ERROR - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Error [ETYC0372117]: Expected type `u8?` but type `i8` was found. --> compiler-test:16:45 | diff --git a/tests/expectations/compiler/option/consts.out b/tests/expectations/compiler/option/consts.out new file mode 100644 index 00000000000..a1fcd30dc00 --- /dev/null +++ b/tests/expectations/compiler/option/consts.out @@ -0,0 +1,37 @@ +program const_optionals.aleo; + +struct Optional__8hhrPm4c3KB: + is_some as boolean; + val as u8; + +struct MyStruct: + x as Optional__8hhrPm4c3KB; + +function const_single_optional: + assert.eq true true; + output 10u8 as u8.private; + +function const_array_of_optionals: + assert.eq true true; + output 2u8 as u8.private; + +function const_tuple_with_optional: + assert.eq true true; + output 99u8 as u8.private; + +function const_nested_array: + assert.eq true true; + assert.eq true true; + output 7u8 as u8.private; + +function const_struct_with_optional: + assert.eq true true; + output 88u8 as u8.private; + +function const_array_of_structs: + assert.eq true true; + assert.eq true true; + output 33u8 as u8.private; + +constructor: + assert.eq edition 0u16; diff --git a/tests/expectations/compiler/option/consts_fail.out b/tests/expectations/compiler/option/consts_fail.out deleted file mode 100644 index d950964d0b4..00000000000 --- a/tests/expectations/compiler/option/consts_fail.out +++ /dev/null @@ -1,35 +0,0 @@ -Error [ETYC0372163]: Constants cannot have an optional type or a type that contains an optional - --> compiler-test:6:5 - | - 6 | const A: u8? = 10u8; - | ^^^^^^^^^^^^^^^^^^^^ -Error [ETYC0372163]: Constants cannot have an optional type or a type that contains an optional - --> compiler-test:7:5 - | - 7 | const B: [u8?; 3] = [1u8, 2u8, 3u8]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Error [ETYC0372163]: Constants cannot have an optional type or a type that contains an optional - --> compiler-test:8:5 - | - 8 | const C: (u8, u8?) = (42u8, 99u8); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Error [ETYC0372163]: Constants cannot have an optional type or a type that contains an optional - --> compiler-test:9:5 - | - 9 | const D: [[u8?; 2]; 2] = [[4u8, 5u8], [6u8, 7u8]]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Error [ETYC0372163]: Constants cannot have an optional type or a type that contains an optional - --> compiler-test:15:5 - | - 15 | const E: MyStruct = MyStruct { x: 88u8 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Error [ETYC0372163]: Constants cannot have an optional type or a type that contains an optional - --> compiler-test:16:5 - | - 16 | const F: [MyStruct; 2] = [MyStruct { x: 11u8 }, MyStruct { x: 22u8 }]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Error [ETYC0372163]: Constants cannot have an optional type or a type that contains an optional - --> compiler-test:33:9 - | - 33 | const D_local: [[u8?; 2]; 2] = [[1u8, 2u8], [3u8, 4u8]]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/tests/compiler/option/consts_fail.leo b/tests/tests/compiler/option/consts.leo similarity index 100% rename from tests/tests/compiler/option/consts_fail.leo rename to tests/tests/compiler/option/consts.leo