Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 30 additions & 4 deletions compiler/ast/src/interpreter_value/core_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use crate::{
CoreFunction,
Expression,
Type,
halt2,
interpreter_value::{ExpectTc, Value},
tc_fail2,
};
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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::<SvmIdentifier>().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))
}
2 changes: 1 addition & 1 deletion compiler/ast/src/statement/definition/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
29 changes: 23 additions & 6 deletions compiler/passes/src/const_propagation/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}

Expand Down
12 changes: 11 additions & 1 deletion compiler/passes/src/option_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(())
}
Expand Down
11 changes: 7 additions & 4 deletions compiler/passes/src/storage_lowering/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.

use super::StorageLoweringVisitor;
use crate::VariableType;

use leo_ast::*;
use leo_span::{Span, Symbol, sym};
Expand Down Expand Up @@ -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_ {
Expand Down
6 changes: 0 additions & 6 deletions compiler/passes/src/type_checking/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
5 changes: 0 additions & 5 deletions tests/expectations/compiler/option/bad_types_fail.out
Original file line number Diff line number Diff line change
@@ -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
|
Expand Down
37 changes: 37 additions & 0 deletions tests/expectations/compiler/option/consts.out
Original file line number Diff line number Diff line change
@@ -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;
35 changes: 0 additions & 35 deletions tests/expectations/compiler/option/consts_fail.out

This file was deleted.