Skip to content

Commit f636bed

Browse files
committed
Remove () expressions and () type except returns.
() serves no purpose beyond indicating the absence of return values, at least until/unless we have some kind of type polymorphism.
1 parent 71ce020 commit f636bed

30 files changed

+192
-292
lines changed

compiler/ast/src/statement/return_.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ pub struct ReturnStatement {
3333

3434
impl fmt::Display for ReturnStatement {
3535
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
36-
write!(f, "return {}", self.expression)
36+
if let Expression::Unit(..) = self.expression {
37+
write!(f, "return")
38+
} else {
39+
write!(f, "return {}", self.expression)
40+
}
3741
}
3842
}
3943

compiler/parser/src/parser/expression.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -608,18 +608,20 @@ impl<N: Network> ParserContext<'_, N> {
608608

609609
let (mut elements, trailing, span) = self.parse_expr_tuple()?;
610610

611-
match elements.len() {
612-
// If the tuple expression is empty, return a `UnitExpression`.
613-
0 => Ok(Expression::Unit(UnitExpression { span, id: self.node_builder.next_id() })),
614-
1 => match trailing {
611+
match (elements.len(), trailing) {
612+
(0, _) | (1, true) => {
613+
// A tuple with 0 or 1 elements - emit an error since tuples must have at least two elements.
614+
Err(ParserError::tuple_must_have_at_least_two_elements("expression", span).into())
615+
}
616+
(1, false) => {
615617
// If there is one element in the tuple but no trailing comma, e.g `(foo)`, return the element.
616-
false => Ok(elements.swap_remove(0)),
617-
// If there is one element in the tuple and a trailing comma, e.g `(foo,)`, emit an error since tuples must have at least two elements.
618-
true => Err(ParserError::tuple_must_have_at_least_two_elements("expression", span).into()),
619-
},
620-
// Otherwise, return a tuple expression.
621-
// Note: This is the only place where `TupleExpression` is constructed in the parser.
622-
_ => Ok(Expression::Tuple(TupleExpression { elements, span, id: self.node_builder.next_id() })),
618+
Ok(elements.remove(0))
619+
}
620+
_ => {
621+
// Otherwise, return a tuple expression.
622+
// Note: This is the only place where `TupleExpression` is constructed in the parser.
623+
Ok(Expression::Tuple(TupleExpression { elements, span, id: self.node_builder.next_id() }))
624+
}
623625
}
624626
}
625627

compiler/passes/src/code_generation/visit_statements.rs

Lines changed: 44 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -80,59 +80,56 @@ impl<'a> CodeGenerator<'a> {
8080
}
8181

8282
fn visit_return(&mut self, input: &'a ReturnStatement) -> String {
83-
let outputs = match input.expression {
83+
if let Expression::Unit(..) = input.expression {
8484
// Skip empty return statements.
85-
Expression::Unit(_) => String::new(),
86-
_ => {
87-
let (operand, mut expression_instructions) = self.visit_expression(&input.expression);
88-
// Get the output type of the function.
89-
let output = self.current_function.unwrap().output.iter();
90-
// If the operand string is empty, initialize an empty vector.
91-
let operand_strings = match operand.is_empty() {
92-
true => vec![],
93-
false => operand.split(' ').collect_vec(),
94-
};
85+
return String::new();
86+
}
9587

96-
let mut future_output = String::new();
97-
let mut instructions = operand_strings
98-
.iter()
99-
.zip_eq(output)
100-
.map(|(operand, output)| {
101-
// Transitions outputs with no mode are private.
102-
// Note that this unwrap is safe because we set the variant before traversing the function.
103-
let visibility = match (self.variant.unwrap().is_transition(), output.mode) {
104-
(true, Mode::None) => Mode::Private,
105-
(_, mode) => mode,
106-
};
107-
108-
if let Type::Future(_) = output.type_ {
109-
future_output = format!(
110-
" output {} as {}.aleo/{}.future;\n",
111-
operand,
112-
self.program_id.unwrap().name,
113-
self.current_function.unwrap().identifier,
114-
);
115-
String::new()
116-
} else {
117-
format!(
118-
" output {} as {};\n",
119-
operand,
120-
self.visit_type_with_visibility(&output.type_, visibility)
121-
)
122-
}
123-
})
124-
.join("");
88+
let (operand, mut expression_instructions) = self.visit_expression(&input.expression);
89+
// Get the output type of the function.
90+
let output = self.current_function.unwrap().output.iter();
91+
// If the operand string is empty, initialize an empty vector.
92+
let operand_strings = match operand.is_empty() {
93+
true => vec![],
94+
false => operand.split(' ').collect_vec(),
95+
};
96+
97+
let mut future_output = String::new();
98+
let mut instructions = operand_strings
99+
.iter()
100+
.zip_eq(output)
101+
.map(|(operand, output)| {
102+
// Transitions outputs with no mode are private.
103+
// Note that this unwrap is safe because we set the variant before traversing the function.
104+
let visibility = match (self.variant.unwrap().is_transition(), output.mode) {
105+
(true, Mode::None) => Mode::Private,
106+
(_, mode) => mode,
107+
};
125108

126-
// Insert future output at the end.
127-
instructions.push_str(&future_output);
109+
if let Type::Future(_) = output.type_ {
110+
future_output = format!(
111+
" output {} as {}.aleo/{}.future;\n",
112+
operand,
113+
self.program_id.unwrap().name,
114+
self.current_function.unwrap().identifier,
115+
);
116+
String::new()
117+
} else {
118+
format!(
119+
" output {} as {};\n",
120+
operand,
121+
self.visit_type_with_visibility(&output.type_, visibility)
122+
)
123+
}
124+
})
125+
.join("");
128126

129-
expression_instructions.push_str(&instructions);
127+
// Insert future output at the end.
128+
instructions.push_str(&future_output);
130129

131-
expression_instructions
132-
}
133-
};
130+
expression_instructions.push_str(&instructions);
134131

135-
outputs
132+
expression_instructions
136133
}
137134

138135
fn visit_definition(&mut self, _input: &'a DefinitionStatement) -> String {

compiler/passes/src/type_checking/check_expressions.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -996,12 +996,7 @@ impl<'a, N: Network> ExpressionVisitor<'a> for TypeChecker<'a, N> {
996996
ty
997997
}
998998

999-
fn visit_unit(&mut self, input: &'a UnitExpression, additional: &Self::AdditionalInput) -> Self::Output {
1000-
// Unit expression are only allowed inside a return statement.
1001-
if !self.scope_state.is_return {
1002-
self.emit_err(TypeCheckerError::unit_expression_only_in_return_statements(input.span()));
1003-
}
1004-
self.maybe_assert_type(&Type::Unit, additional, input.span());
1005-
Type::Unit
999+
fn visit_unit(&mut self, _input: &'a UnitExpression, _additional: &Self::AdditionalInput) -> Self::Output {
1000+
unreachable!("Unit expressions should not exist in normal code");
10061001
}
10071002
}

compiler/passes/src/type_checking/check_statements.rs

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,6 @@ impl<'a, N: Network> StatementVisitor<'a> for TypeChecker<'a, N> {
222222

223223
// Check that the type of the definition is not a unit type, singleton tuple type, or nested tuple type.
224224
match &input.type_ {
225-
// If the type is an empty tuple, return an error.
226-
Type::Unit => self.emit_err(TypeCheckerError::lhs_must_be_identifier_or_tuple(input.span)),
227225
// If the type is a singleton tuple, return an error.
228226
Type::Tuple(tuple) => match tuple.length() {
229227
0 | 1 => unreachable!("Parsing guarantees that tuple types have at least two elements."),
@@ -421,20 +419,12 @@ impl<'a, N: Network> StatementVisitor<'a> for TypeChecker<'a, N> {
421419
// Set the `has_return` flag.
422420
self.scope_state.has_return = true;
423421

424-
// Check that the return expression is not a nested tuple.
425-
if let Expression::Tuple(TupleExpression { elements, .. }) = &input.expression {
426-
for element in elements {
427-
if matches!(element, Expression::Tuple(_)) {
428-
self.emit_err(TypeCheckerError::nested_tuple_expression(element.span()));
429-
}
430-
}
431-
}
432-
433-
// Set the `is_return` flag. This is necessary to allow unit expressions in the return statement.
434-
self.scope_state.is_return = true;
435422
// Type check the associated expression.
436-
self.visit_expression(&input.expression, &return_type);
437-
// Unset the `is_return` flag.
438-
self.scope_state.is_return = false;
423+
if let Expression::Unit(..) = input.expression {
424+
// TODO - do will this error message be appropriate?
425+
self.maybe_assert_type(&Type::Unit, &return_type, input.expression.span());
426+
} else {
427+
self.visit_expression(&input.expression, &return_type);
428+
}
439429
}
440430
}

compiler/passes/src/type_checking/checker.rs

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -856,29 +856,30 @@ impl<'a, N: Network> TypeChecker<'a, N> {
856856
}
857857

858858
/// Emits an error if the type or its constituent types is not valid.
859-
pub(crate) fn assert_type_is_valid(&mut self, type_: &Type, span: Span) -> bool {
860-
let mut is_valid = true;
859+
pub(crate) fn assert_type_is_valid(&mut self, type_: &Type, span: Span) {
861860
match type_ {
861+
// Unit types may only appear as the return type of a function.
862+
Type::Unit => {
863+
self.emit_err(TypeCheckerError::unit_type_only_return(span));
864+
}
862865
// String types are temporarily disabled.
863866
Type::String => {
864-
is_valid = false;
865867
self.emit_err(TypeCheckerError::strings_are_not_supported(span));
866868
}
867869
// Check that the named composite type has been defined.
868870
Type::Composite(struct_) if self.lookup_struct(struct_.program, struct_.id.name).is_none() => {
869-
is_valid = false;
870871
self.emit_err(TypeCheckerError::undefined_type(struct_.id.name, span));
871872
}
872873
// Check that the constituent types of the tuple are valid.
873874
Type::Tuple(tuple_type) => {
874875
for type_ in tuple_type.elements().iter() {
875-
is_valid &= self.assert_type_is_valid(type_, span)
876+
self.assert_type_is_valid(type_, span);
876877
}
877878
}
878879
// Check that the constituent types of mapping are valid.
879880
Type::Mapping(mapping_type) => {
880-
is_valid &= self.assert_type_is_valid(&mapping_type.key, span);
881-
is_valid &= self.assert_type_is_valid(&mapping_type.value, span);
881+
self.assert_type_is_valid(&mapping_type.key, span);
882+
self.assert_type_is_valid(&mapping_type.value, span);
882883
}
883884
// Check that the array element types are valid.
884885
Type::Array(array_type) => {
@@ -909,11 +910,10 @@ impl<'a, N: Network> TypeChecker<'a, N> {
909910
}
910911
_ => {} // Do nothing.
911912
}
912-
is_valid &= self.assert_type_is_valid(array_type.element_type(), span)
913+
self.assert_type_is_valid(array_type.element_type(), span);
913914
}
914915
_ => {} // Do nothing.
915916
}
916-
is_valid
917917
}
918918

919919
/// Emits an error if the type is not a mapping.
@@ -1067,17 +1067,19 @@ impl<'a, N: Network> TypeChecker<'a, N> {
10671067
}
10681068
}
10691069
}
1070-
// Check that the type of output is defined.
1071-
if self.assert_type_is_valid(&function_output.type_, function_output.span) {
1072-
// If the function is not a transition function, then it cannot output a record.
1073-
if let Type::Composite(struct_) = function_output.type_.clone() {
1074-
if !function.variant.is_transition()
1075-
&& self.lookup_struct(struct_.program, struct_.id.name).unwrap().is_record
1076-
{
1077-
self.emit_err(TypeCheckerError::function_cannot_input_or_output_a_record(function_output.span));
1078-
}
1070+
1071+
// Check that the output type is valid.
1072+
self.assert_type_is_valid(&function_output.type_, function_output.span);
1073+
1074+
// If the function is not a transition function, then it cannot output a record.
1075+
if let Type::Composite(struct_) = function_output.type_.clone() {
1076+
if !function.variant.is_transition()
1077+
&& self.lookup_struct(struct_.program, struct_.id.name).map_or(false, |s| s.is_record)
1078+
{
1079+
self.emit_err(TypeCheckerError::function_cannot_input_or_output_a_record(function_output.span));
10791080
}
10801081
}
1082+
10811083
// Check that the type of the output is not a tuple. This is necessary to forbid nested tuples.
10821084
if matches!(&function_output.type_, Type::Tuple(_)) {
10831085
self.emit_err(TypeCheckerError::nested_tuple_type(function_output.span))

compiler/passes/src/type_checking/scope_state.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ pub struct ScopeState {
2525
pub(crate) variant: Option<Variant>,
2626
/// Whether or not the function that we are currently traversing has a return statement.
2727
pub(crate) has_return: bool,
28-
/// Whether or not we are currently traversing a return statement.
29-
pub(crate) is_return: bool,
3028
/// Current program name.
3129
pub(crate) program_name: Option<Symbol>,
3230
/// Whether or not we are currently traversing a stub.
@@ -50,7 +48,6 @@ impl ScopeState {
5048
function: None,
5149
variant: None,
5250
has_return: false,
53-
is_return: false,
5451
program_name: None,
5552
is_stub: true,
5653
futures: IndexMap::new(),

errors/src/errors/type_checker/type_checker_error.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,8 +418,7 @@ create_messages!(
418418
help: None,
419419
}
420420

421-
// TODO: Consider changing this to a warning.
422-
421+
// TODO This error is unused. Remove it in a future version.
423422
@formatted
424423
assign_unit_expression_to_variable {
425424
args: (),
@@ -476,6 +475,7 @@ create_messages!(
476475
help: None,
477476
}
478477

478+
// TODO This error is unused. Remove it in a future version.
479479
@formatted
480480
unit_expression_only_in_return_statements {
481481
args: (),
@@ -976,4 +976,11 @@ create_messages!(
976976
),
977977
help: Some("Valid second operands are `u8`, `u16`, or `u32`".into()),
978978
}
979+
980+
@formatted
981+
unit_type_only_return {
982+
args: (),
983+
msg: "The unit type () may appear only as the return type of a function.".to_string(),
984+
help: None,
985+
}
979986
);

tests/expectations/compiler/array/array_with_units_fail.out

Lines changed: 0 additions & 21 deletions
This file was deleted.

tests/expectations/compiler/constants/const_type_error_fail.out

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,15 @@
11
namespace = "Compile"
22
expectation = "Fail"
33
outputs = ["""
4-
Error [ETYC0372055]: The left-hand side of a `DefinitionStatement` can only be an identifier or tuple. Note that a tuple must contain at least two elements.
5-
--> compiler-test:7:9
6-
|
7-
7 | const A: () = ();
8-
| ^^^^^^^^^^^^^^^^
94
Error [ETYC0372070]: The value of a const declaration must be a literal
105
--> compiler-test:7:9
116
|
12-
7 | const A: () = ();
13-
| ^^^^^^^^^^^^^^^^
14-
Error [ETYC0372056]: Unit expressions can only be used in return statements.
15-
--> compiler-test:7:23
16-
|
17-
7 | const A: () = ();
18-
| ^^
19-
Error [ETYC0372070]: The value of a const declaration must be a literal
20-
--> compiler-test:8:9
21-
|
22-
8 | const B: u8 = ((1u8,1u8),1u8);
7+
7 | const B: u8 = ((1u8,1u8),1u8);
238
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
249
Error [ETYC0372023]: Tuples must be explicitly typed in Leo
25-
--> compiler-test:8:23
10+
--> compiler-test:7:23
2611
|
27-
8 | const B: u8 = ((1u8,1u8),1u8);
12+
7 | const B: u8 = ((1u8,1u8),1u8);
2813
| ^^^^^^^^^^^^^^^
2914
|
3015
= The function definition must match the function return statement

tests/expectations/compiler/finalize/set_in_an_assignment_fail.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
namespace = "Compile"
22
expectation = "Fail"
33
outputs = ["""
4-
Error [ETYC0372055]: The left-hand side of a `DefinitionStatement` can only be an identifier or tuple. Note that a tuple must contain at least two elements.
4+
Error [ETYC0372123]: The unit type () may appear only as the return type of a function.
55
--> compiler-test:11:9
66
|
77
11 | let result: () = Mapping::set(amounts, addr, amount);

0 commit comments

Comments
 (0)