Skip to content

Commit c0ab5c8

Browse files
committed
Allow external record assignment.
We still have to forbid it in a narrower conditional scope than where the variable was defined, though.
1 parent 13e9547 commit c0ab5c8

File tree

5 files changed

+83
-28
lines changed

5 files changed

+83
-28
lines changed

compiler/passes/src/type_checking/expression.rs

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,24 @@ impl TypeCheckingVisitor<'_> {
3636
}
3737
};
3838

39-
// Prohibit assignment to an external record or a member thereof.
40-
// This is necessary as an assignment in a conditional branch would become a
41-
// ternary, which can't happen.
42-
if self.is_external_record(&ty) {
43-
self.emit_err(TypeCheckerError::assignment_to_external_record(&ty, input.span()));
44-
}
39+
// Prohibit assignment to an external record in a narrower conditional scope.
40+
let external_record = self.is_external_record(&ty);
41+
let external_record_tuple =
42+
matches!(&ty, Type::Tuple(tuple) if tuple.elements().iter().any(|ty| self.is_external_record(ty)));
43+
44+
if external_record || external_record_tuple {
45+
let Expression::Identifier(id) = input else {
46+
// This is not valid Leo and will have triggered an error elsewhere.
47+
return Type::Err;
48+
};
4549

46-
// Similarly prohibit assignment to a tuple with an external record member.
47-
if let Type::Tuple(tuple) = &ty {
48-
if tuple.elements().iter().any(|ty| self.is_external_record(ty)) {
49-
self.emit_err(TypeCheckerError::assignment_to_external_record(&ty, input.span()));
50+
if !self.symbol_in_conditional_scope(id.name) {
51+
if external_record {
52+
self.emit_err(TypeCheckerError::assignment_to_external_record_cond(&ty, input.span()));
53+
} else {
54+
// Note that this will cover both assigning to a tuple variable and assigning to a member of a tuple.
55+
self.emit_err(TypeCheckerError::assignment_to_external_record_tuple_cond(&ty, input.span()));
56+
}
5057
}
5158
}
5259

@@ -158,6 +165,11 @@ impl TypeCheckingVisitor<'_> {
158165
self.visit_expression(&input.inner, &None)
159166
};
160167

168+
// Make sure we're not assigning to a member of an external record.
169+
if assign && self.is_external_record(&ty) {
170+
self.emit_err(TypeCheckerError::assignment_to_external_record_member(&ty, input.span));
171+
}
172+
161173
// Check that the type of `inner` in `inner.name` is a struct.
162174
match ty {
163175
Type::Err => Type::Err,

compiler/passes/src/type_checking/visitor.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ impl TypeCheckingVisitor<'_> {
9696
/// Use this method when you know the actual type.
9797
/// Emits an error to the handler if the `actual` type is not equal to the `expected` type.
9898
pub fn assert_and_return_type(&mut self, actual: Type, expected: &Option<Type>, span: Span) -> Type {
99-
if expected.is_some() {
99+
// If expected is `Type::Err`, we don't want to actually report a redundant error.
100+
if expected.is_some() && !matches!(expected, Some(Type::Err)) {
100101
self.check_eq_types(&Some(actual.clone()), expected, span);
101102
}
102103
actual

errors/src/errors/type_checker/type_checker_error.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,6 +1039,7 @@ create_messages!(
10391039
help: Some("Ternary conditionals may not contain an external record type.".to_string()),
10401040
}
10411041

1042+
// TODO: unused.
10421043
@formatted
10431044
assignment_to_external_record {
10441045
args: (ty: impl Display),
@@ -1059,10 +1060,32 @@ create_messages!(
10591060
msg: format!("Record name `{r1}` is prefixed by the record name `{r2}`. Record names must not be prefixes of other record names."),
10601061
help: None,
10611062
}
1063+
10621064
@formatted
10631065
range_bounds_type_mismatch{
10641066
args: (),
10651067
msg: format!("mismatched types in loop iterator range bounds"),
10661068
help: None,
10671069
}
1070+
1071+
@formatted
1072+
assignment_to_external_record_member {
1073+
args: (ty: impl Display),
1074+
msg: format!("Cannot assign to a member of the external record `{ty}`."),
1075+
help: None,
1076+
}
1077+
1078+
@formatted
1079+
assignment_to_external_record_cond {
1080+
args: (ty: impl Display),
1081+
msg: format!("Cannot assign to the external record type `{ty}` in this location."),
1082+
help: Some("External record variables may not be assigned to in narrower conditonal scopes than they were defined.".into()),
1083+
}
1084+
1085+
@formatted
1086+
assignment_to_external_record_tuple_cond {
1087+
args: (ty: impl Display),
1088+
msg: format!("Cannot assign to the tuple type `{ty}` containing an external record in this location."),
1089+
help: Some("Tuples containing external records may not be assigned to in narrower conditonal scopes than they were defined.".into()),
1090+
}
10681091
);
Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,26 @@
1-
Error [ETYC0372131]: Cannot assign to type `test0.aleo/R` or a member thereof.
1+
Error [ETYC0372136]: Cannot assign to the external record type `test0.aleo/R` in this location.
22
--> compiler-test:12:13
33
|
44
12 | r1 = test0.aleo/foo();
55
| ^^
66
|
7-
= External record types and tuples containing them may not be assigned to.
8-
Error [ETYC0372131]: Cannot assign to type `(test0.aleo/R, test0.aleo/R)` or a member thereof.
9-
--> compiler-test:20:13
7+
= External record variables may not be assigned to in narrower conditonal scopes than they were defined.
8+
Error [ETYC0372137]: Cannot assign to the tuple type `(test0.aleo/R, test0.aleo/R)` containing an external record in this location.
9+
--> compiler-test:21:13
1010
|
11-
20 | tupl.0 = test0.aleo/foo();
11+
21 | tupl.0 = test0.aleo/foo();
1212
| ^^^^
1313
|
14-
= External record types and tuples containing them may not be assigned to.
15-
Error [ETYC0372131]: Cannot assign to type `test0.aleo/R` or a member thereof.
16-
--> compiler-test:20:18
14+
= Tuples containing external records may not be assigned to in narrower conditonal scopes than they were defined.
15+
Error [ETYC0372137]: Cannot assign to the tuple type `(test0.aleo/R, test0.aleo/R)` containing an external record in this location.
16+
--> compiler-test:25:13
1717
|
18-
20 | tupl.0 = test0.aleo/foo();
19-
| ^
20-
|
21-
= External record types and tuples containing them may not be assigned to.
22-
Error [ETYC0372131]: Cannot assign to type `test0.aleo/R` or a member thereof.
23-
--> compiler-test:27:9
18+
25 | tupl = (test0.aleo/foo(), test0.aleo/foo());
19+
| ^^^^
2420
|
25-
27 | r.x = x;
26-
| ^
21+
= Tuples containing external records may not be assigned to in narrower conditonal scopes than they were defined.
22+
Error [ETYC0372135]: Cannot assign to a member of the external record `test0.aleo/R`.
23+
--> compiler-test:33:9
2724
|
28-
= External record types and tuples containing them may not be assigned to.
25+
33 | r.x = x;
26+
| ^^^

tests/tests/compiler/statements/assign_external_record_fail.leo

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,35 @@ program test1.aleo {
3232
transition bar2(x: bool) -> test0.aleo/R {
3333
let tupl: (test0.aleo/R, test0.aleo/R) = (test0.aleo/foo(), test0.aleo/foo());
3434
if x {
35+
// This should fail - external record assignment in narrower conditional scope.
3536
tupl.0 = test0.aleo/foo();
3637
}
38+
if x {
39+
// Ditto.
40+
tupl = (test0.aleo/foo(), test0.aleo/foo());
41+
}
3742
return tupl.0;
3843
}
3944

4045
transition bar3(x: bool) -> test0.aleo/R {
4146
let r: test0.aleo/R = test0.aleo/foo();
47+
// This should fail.
4248
r.x = x;
49+
if x {
50+
let r2: test0.aleo/R = test0.aleo/foo();
51+
// This should pass.
52+
r2 = test0.aleo/foo();
53+
}
54+
return r;
55+
}
56+
57+
transition bar4() -> test0.aleo/R {
58+
let r: test0.aleo/R = test0.aleo/foo();
59+
for i: u8 in 0u8..4u8 {
60+
// This should pass - it's an external record assignment but
61+
// not in a narrower conditional scope..
62+
r = test0.aleo/foo();
63+
}
4364
return r;
4465
}
4566
}

0 commit comments

Comments
 (0)