Allow implicit discard when right-hand side is Void.
This is the most intuitive/expected behavior. Otherwise, it forces a pointless let-binding to 'Void' or into a discard.
This commit is contained in:
parent
d73f8fd6c2
commit
d6cc9bdfbe
|
@ -32,6 +32,7 @@
|
||||||
- **aiken-lang**: disallow `MLResult` in a type definition. @rvcas
|
- **aiken-lang**: disallow `MLResult` in a type definition. @rvcas
|
||||||
- **aiken-lang**: reversed deserialization of bls types out of data types. @rvcas
|
- **aiken-lang**: reversed deserialization of bls types out of data types. @rvcas
|
||||||
- **aiken-lang**: validator args unexpectedly unbound causing code gen crashes. @rvcas
|
- **aiken-lang**: validator args unexpectedly unbound causing code gen crashes. @rvcas
|
||||||
|
- **aiken-lang**: allow implicitly discarded values when right-hand-side unified with `Void`. @KtorZ
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|
|
@ -93,6 +93,19 @@ fn validator_illegal_return_type() {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn implicitly_discard_void() {
|
||||||
|
let source_code = r#"
|
||||||
|
pub fn label(str: String) -> Void {
|
||||||
|
trace str Void
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let (warnings, _) = check_validator(parse(source_code)).expect("should type-check");
|
||||||
|
|
||||||
|
assert!(warnings.is_empty(), "no warnings: {warnings:#?}");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn validator_illegal_arity() {
|
fn validator_illegal_arity() {
|
||||||
let source_code = r#"
|
let source_code = r#"
|
||||||
|
|
|
@ -9,16 +9,19 @@ use super::{
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{
|
ast::{
|
||||||
Annotation, Arg, ArgName, AssignmentKind, BinOp, Bls12_381Point, ByteArrayFormatPreference,
|
Annotation, Arg, ArgName, AssignmentKind, BinOp, Bls12_381Point, ByteArrayFormatPreference,
|
||||||
CallArg, ClauseGuard, Constant, Curve, IfBranch, LogicalOpChainKind, RecordUpdateSpread,
|
CallArg, ClauseGuard, Constant, Curve, IfBranch, LogicalOpChainKind, Pattern,
|
||||||
Span, TraceKind, TraceLevel, Tracing, TypedArg, TypedCallArg, TypedClause,
|
RecordUpdateSpread, Span, TraceKind, TraceLevel, Tracing, TypedArg, TypedCallArg,
|
||||||
TypedClauseGuard, TypedIfBranch, TypedPattern, TypedRecordUpdateArg, UnOp, UntypedArg,
|
TypedClause, TypedClauseGuard, TypedIfBranch, TypedPattern, TypedRecordUpdateArg, UnOp,
|
||||||
UntypedClause, UntypedClauseGuard, UntypedIfBranch, UntypedPattern, UntypedRecordUpdateArg,
|
UntypedArg, UntypedClause, UntypedClauseGuard, UntypedIfBranch, UntypedPattern,
|
||||||
|
UntypedRecordUpdateArg,
|
||||||
|
},
|
||||||
|
builtins::{
|
||||||
|
bool, byte_array, function, g1_element, g2_element, int, list, string, tuple, void,
|
||||||
},
|
},
|
||||||
builtins::{bool, byte_array, function, g1_element, g2_element, int, list, string, tuple},
|
|
||||||
expr::{FnStyle, TypedExpr, UntypedExpr},
|
expr::{FnStyle, TypedExpr, UntypedExpr},
|
||||||
format,
|
format,
|
||||||
line_numbers::LineNumbers,
|
line_numbers::LineNumbers,
|
||||||
tipo::fields::FieldMap,
|
tipo::{fields::FieldMap, PatternConstructor},
|
||||||
};
|
};
|
||||||
use std::{cmp::Ordering, collections::HashMap, rc::Rc};
|
use std::{cmp::Ordering, collections::HashMap, rc::Rc};
|
||||||
use vec1::Vec1;
|
use vec1::Vec1;
|
||||||
|
@ -1699,20 +1702,25 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
let mut expressions = Vec::with_capacity(count);
|
let mut expressions = Vec::with_capacity(count);
|
||||||
|
|
||||||
for (i, expression) in untyped.into_iter().enumerate() {
|
for (i, expression) in untyped.into_iter().enumerate() {
|
||||||
match i.cmp(&(count - 1)) {
|
let no_assignment = assert_no_assignment(&expression);
|
||||||
|
|
||||||
|
let typed_expression = scope.infer(expression)?;
|
||||||
|
|
||||||
|
expressions.push(match i.cmp(&(count - 1)) {
|
||||||
// When the expression is the last in a sequence, we enforce it is NOT
|
// When the expression is the last in a sequence, we enforce it is NOT
|
||||||
// an assignment (kind of treat assignments like statements).
|
// an assignment (kind of treat assignments like statements).
|
||||||
Ordering::Equal => assert_no_assignment(&expression)?,
|
Ordering::Equal => {
|
||||||
|
no_assignment?;
|
||||||
|
typed_expression
|
||||||
|
}
|
||||||
|
|
||||||
// This isn't the final expression in the sequence, so it *must*
|
// This isn't the final expression in the sequence, so it *must*
|
||||||
// be a let-binding; we do not allow anything else.
|
// be a let-binding; we do not allow anything else.
|
||||||
Ordering::Less => assert_assignment(&expression)?,
|
Ordering::Less => assert_assignment(typed_expression)?,
|
||||||
|
|
||||||
// Can't actually happen
|
// Can't actually happen
|
||||||
Ordering::Greater => (),
|
Ordering::Greater => typed_expression,
|
||||||
}
|
})
|
||||||
|
|
||||||
expressions.push(scope.infer(expression)?);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(expressions)
|
Ok(expressions)
|
||||||
|
@ -2076,12 +2084,35 @@ fn assert_no_assignment(expr: &UntypedExpr) -> Result<(), Error> {
|
||||||
| UntypedExpr::CurvePoint { .. } => Ok(()),
|
| UntypedExpr::CurvePoint { .. } => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn assert_assignment(expr: &UntypedExpr) -> Result<(), Error> {
|
|
||||||
if !matches!(*expr, UntypedExpr::Assignment { .. }) {
|
fn assert_assignment(expr: TypedExpr) -> Result<TypedExpr, Error> {
|
||||||
|
if !matches!(expr, TypedExpr::Assignment { .. }) {
|
||||||
|
if expr.tipo().is_void() {
|
||||||
|
return Ok(TypedExpr::Assignment {
|
||||||
|
location: expr.location(),
|
||||||
|
tipo: void(),
|
||||||
|
value: expr.clone().into(),
|
||||||
|
pattern: Pattern::Constructor {
|
||||||
|
is_record: false,
|
||||||
|
location: expr.location(),
|
||||||
|
name: "Void".to_string(),
|
||||||
|
constructor: PatternConstructor::Record {
|
||||||
|
name: "Void".to_string(),
|
||||||
|
field_map: None,
|
||||||
|
},
|
||||||
|
arguments: vec![],
|
||||||
|
module: None,
|
||||||
|
with_spread: false,
|
||||||
|
tipo: void(),
|
||||||
|
},
|
||||||
|
kind: AssignmentKind::Let,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return Err(Error::ImplicitlyDiscardedExpression {
|
return Err(Error::ImplicitlyDiscardedExpression {
|
||||||
location: expr.location(),
|
location: expr.location(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(expr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# This file was generated by Aiken
|
||||||
|
# You typically do not need to edit this file
|
||||||
|
|
||||||
|
requirements = []
|
||||||
|
packages = []
|
||||||
|
|
||||||
|
[etags]
|
|
@ -0,0 +1,3 @@
|
||||||
|
name = "aiken-lang/acceptance_test_098"
|
||||||
|
version = "0.0.0"
|
||||||
|
description = ""
|
|
@ -0,0 +1,8 @@
|
||||||
|
fn label(str: String) -> Void {
|
||||||
|
trace str Void
|
||||||
|
}
|
||||||
|
|
||||||
|
test foo() {
|
||||||
|
label(@"Foo")
|
||||||
|
True
|
||||||
|
}
|
Loading…
Reference in New Issue