Skip to content

Commit 2eb1e7b

Browse files
committed
Allow external structs.
Specifically, 1. Introduce a PlaintextType::ExternalStruct, and allow referring to such a type in .aleo code by a Locator. 2. Allow casting to external struct types. 3. Let two struct types be considered the same type as long as they are structurally the same.
1 parent dd47c44 commit 2eb1e7b

File tree

31 files changed

+740
-239
lines changed

31 files changed

+740
-239
lines changed

circuit/program/src/data/value/mod.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,24 @@ impl<A: Aleo> Eject for Value<A> {
6666
}
6767
}
6868
}
69+
70+
impl<A: Aleo> From<Plaintext<A>> for Value<A> {
71+
/// Initializes the value from a plaintext.
72+
fn from(plaintext: Plaintext<A>) -> Self {
73+
Self::Plaintext(plaintext)
74+
}
75+
}
76+
77+
impl<A: Aleo> From<Record<A, Plaintext<A>>> for Value<A> {
78+
/// Initializes the value from a record.
79+
fn from(record: Record<A, Plaintext<A>>) -> Self {
80+
Self::Record(record)
81+
}
82+
}
83+
84+
impl<A: Aleo> From<Future<A>> for Value<A> {
85+
/// Initializes the value from a future.
86+
fn from(future: Future<A>) -> Self {
87+
Self::Future(future)
88+
}
89+
}

console/program/src/data_types/array_type/bytes.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
// limitations under the License.
1515

1616
use super::*;
17-
use crate::{Identifier, LiteralType};
17+
use crate::{Identifier, LiteralType, Locator};
1818

1919
impl<N: Network> FromBytes for ArrayType<N> {
2020
fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
@@ -23,7 +23,8 @@ impl<N: Network> FromBytes for ArrayType<N> {
2323
let element_type = match variant {
2424
0 => PlaintextType::Literal(LiteralType::read_le(&mut reader)?),
2525
1 => PlaintextType::Struct(Identifier::read_le(&mut reader)?),
26-
2.. => return Err(error(format!("Failed to deserialize element type {variant}"))),
26+
3 => PlaintextType::ExternalStruct(Locator::read_le(&mut reader)?),
27+
_ => return Err(error(format!("Failed to deserialize element type {variant}"))),
2728
};
2829

2930
// Read the number of dimensions of the array.
@@ -58,7 +59,7 @@ impl<N: Network> ToBytes for ArrayType<N> {
5859
// Note that the lengths are in the order of the outermost dimension to the innermost dimension.
5960
for _ in 1..N::MAX_DATA_DEPTH {
6061
element_type = match element_type {
61-
PlaintextType::Literal(_) | PlaintextType::Struct(_) => break,
62+
PlaintextType::Literal(_) | PlaintextType::Struct(_) | PlaintextType::ExternalStruct(_) => break,
6263
PlaintextType::Array(array_type) => {
6364
lengths.push(*array_type.length());
6465
array_type.next_element_type().clone()
@@ -86,6 +87,10 @@ impl<N: Network> ToBytes for ArrayType<N> {
8687
// out of an abundance of caution.
8788
return Err(error(format!("Array type exceeds the maximum depth of {}.", N::MAX_DATA_DEPTH)));
8889
}
90+
PlaintextType::ExternalStruct(locator) => {
91+
3u8.write_le(&mut writer)?;
92+
locator.write_le(&mut writer)?;
93+
}
8994
}
9095

9196
// Write the number of dimensions of the array.

console/program/src/data_types/array_type/parse.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,19 @@
1414
// limitations under the License.
1515

1616
use super::*;
17-
use crate::{Identifier, LiteralType};
17+
use crate::{Identifier, LiteralType, Locator};
1818

1919
impl<N: Network> Parser for ArrayType<N> {
2020
/// Parses a string into a literal type.
2121
#[inline]
2222
fn parse(string: &str) -> ParserResult<Self> {
2323
// A helper function to parse the innermost element type.
2424
fn parse_inner_element_type<N: Network>(string: &str) -> ParserResult<PlaintextType<N>> {
25-
alt((map(LiteralType::parse, PlaintextType::from), map(Identifier::parse, PlaintextType::from)))(string)
25+
alt((
26+
map(Locator::parse, PlaintextType::from),
27+
map(LiteralType::parse, PlaintextType::from),
28+
map(Identifier::parse, PlaintextType::from),
29+
))(string)
2630
}
2731

2832
// A helper function to parse the length of each dimension.

console/program/src/data_types/plaintext_type/bytes.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ impl<N: Network> FromBytes for PlaintextType<N> {
2323
0 => Ok(Self::Literal(LiteralType::read_le(&mut reader)?)),
2424
1 => Ok(Self::Struct(Identifier::read_le(&mut reader)?)),
2525
2 => Ok(Self::Array(ArrayType::read_le(&mut reader)?)),
26-
3.. => Err(error(format!("Failed to deserialize annotation variant {variant}"))),
26+
3 => Ok(Self::ExternalStruct(Locator::read_le(&mut reader)?)),
27+
4.. => Err(error(format!("Failed to deserialize annotation variant {variant}"))),
2728
}
2829
}
2930
}
@@ -44,6 +45,10 @@ impl<N: Network> ToBytes for PlaintextType<N> {
4445
2u8.write_le(&mut writer)?;
4546
array_type.write_le(&mut writer)
4647
}
48+
Self::ExternalStruct(locator) => {
49+
3u8.write_le(&mut writer)?;
50+
locator.write_le(&mut writer)
51+
}
4752
}
4853
}
4954
}

console/program/src/data_types/plaintext_type/mod.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ mod bytes;
1717
mod parse;
1818
mod serialize;
1919

20-
use crate::{ArrayType, Identifier, LiteralType};
20+
use crate::{ArrayType, Identifier, LiteralType, Locator, ProgramID};
2121
use snarkvm_console_network::prelude::*;
2222

2323
/// A `PlaintextType` defines the type parameter for a literal, struct, or array.
@@ -26,14 +26,49 @@ pub enum PlaintextType<N: Network> {
2626
/// A literal type contains its type name.
2727
/// The format of the type is `<type_name>`.
2828
Literal(LiteralType),
29-
/// An struct type contains its identifier.
29+
/// A struct type contains its identifier.
3030
/// The format of the type is `<identifier>`.
3131
Struct(Identifier<N>),
32+
/// An external struct type contains its locator.
33+
/// The format of the type is `<program_id>/<identifier>`.
34+
ExternalStruct(Locator<N>),
3235
/// An array type contains its element type and length.
3336
/// The format of the type is `[<element_type>; <length>]`.
3437
Array(ArrayType<N>),
3538
}
3639

40+
impl<N: Network> PlaintextType<N> {
41+
/// Are the two types equivalent for the purposes of static checking?
42+
///
43+
/// Since struct types are compared by structure, we can't determine equality
44+
/// by only looking at their names.
45+
pub fn equal_or_structs(&self, rhs: &Self) -> bool {
46+
use PlaintextType::*;
47+
48+
match (self, rhs) {
49+
(ExternalStruct(..) | Struct(..), ExternalStruct(..) | Struct(..)) => true,
50+
(Literal(lit0), Literal(lit1)) => lit0 == lit1,
51+
(Array(array0), Array(array1)) => {
52+
array0.length() == array1.length()
53+
&& array0.base_element_type().equal_or_structs(array1.base_element_type())
54+
}
55+
_ => false,
56+
}
57+
}
58+
59+
// Make unqualified structs into external ones with the given `id`.
60+
pub fn qualify(self, id: ProgramID<N>) -> Self {
61+
match self {
62+
PlaintextType::ExternalStruct(..) | PlaintextType::Literal(..) => self,
63+
PlaintextType::Struct(name) => PlaintextType::ExternalStruct(Locator::new(id, name)),
64+
PlaintextType::Array(array_type) => {
65+
let element_type = array_type.next_element_type().clone().qualify(id);
66+
PlaintextType::Array(ArrayType::new(element_type, vec![*array_type.length()]).unwrap())
67+
}
68+
}
69+
}
70+
}
71+
3772
impl<N: Network> From<LiteralType> for PlaintextType<N> {
3873
/// Initializes a plaintext type from a literal type.
3974
fn from(literal: LiteralType) -> Self {
@@ -48,6 +83,13 @@ impl<N: Network> From<Identifier<N>> for PlaintextType<N> {
4883
}
4984
}
5085

86+
impl<N: Network> From<Locator<N>> for PlaintextType<N> {
87+
/// Initializes a plaintext type from an external struct type.
88+
fn from(locator: Locator<N>) -> Self {
89+
PlaintextType::ExternalStruct(locator)
90+
}
91+
}
92+
5193
impl<N: Network> From<ArrayType<N>> for PlaintextType<N> {
5294
/// Initializes a plaintext type from an array type.
5395
fn from(array: ArrayType<N>) -> Self {

console/program/src/data_types/plaintext_type/parse.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ impl<N: Network> Parser for PlaintextType<N> {
2222
// Parse to determine the plaintext type (order matters).
2323
alt((
2424
map(ArrayType::parse, |type_| Self::Array(type_)),
25+
map(Locator::parse, |locator| Self::ExternalStruct(locator)),
2526
map(Identifier::parse, |identifier| Self::Struct(identifier)),
2627
map(LiteralType::parse, |type_| Self::Literal(type_)),
2728
))(string)
@@ -60,6 +61,7 @@ impl<N: Network> Display for PlaintextType<N> {
6061
Self::Literal(literal) => Display::fmt(literal, f),
6162
// Prints the struct, i.e. signature
6263
Self::Struct(struct_) => Display::fmt(struct_, f),
64+
Self::ExternalStruct(locator) => Display::fmt(locator, f),
6365
// Prints the array type, i.e. [field; 2u32]
6466
Self::Array(array) => Display::fmt(array, f),
6567
}

console/program/src/data_types/register_type/mod.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ mod bytes;
1717
mod parse;
1818
mod serialize;
1919

20-
use crate::{FinalizeType, Identifier, Locator, PlaintextType, ValueType};
20+
use crate::{FinalizeType, Identifier, Locator, PlaintextType, ProgramID, ValueType};
2121
use snarkvm_console_network::prelude::*;
2222

2323
use enum_index::EnumIndex;
@@ -34,6 +34,29 @@ pub enum RegisterType<N: Network> {
3434
Future(Locator<N>),
3535
}
3636

37+
impl<N: Network> RegisterType<N> {
38+
/// Are the two types equivalent for the purposes of static checking?
39+
///
40+
/// Since struct types are compared by structure, we can't determine equality
41+
/// by only looking at their names.
42+
pub fn equal_or_structs(&self, rhs: &Self) -> bool {
43+
use RegisterType::*;
44+
match (self, rhs) {
45+
(Plaintext(a), Plaintext(b)) => a.equal_or_structs(b),
46+
_ => self == rhs,
47+
}
48+
}
49+
50+
// Make unqualified structs or records into external ones with the given `id`.
51+
pub fn qualify(self, id: ProgramID<N>) -> Self {
52+
match self {
53+
RegisterType::Plaintext(plaintext_type) => RegisterType::Plaintext(plaintext_type.qualify(id)),
54+
RegisterType::Record(name) => RegisterType::ExternalRecord(Locator::new(id, name)),
55+
RegisterType::ExternalRecord(..) | RegisterType::Future(..) => self,
56+
}
57+
}
58+
}
59+
3760
impl<N: Network> From<ValueType<N>> for RegisterType<N> {
3861
/// Converts a value type to a register type.
3962
fn from(value: ValueType<N>) -> Self {

synthesizer/process/src/cost.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ fn plaintext_size_in_bytes<N: Network>(stack: &Stack<N>, plaintext_type: &Plaint
155155
// Return the size of the struct.
156156
Ok(size_of_name.saturating_add(size_of_members))
157157
}
158+
PlaintextType::ExternalStruct(locator) => {
159+
let external_stack = stack.get_external_stack(locator.program_id())?;
160+
plaintext_size_in_bytes(&*external_stack, &PlaintextType::Struct(*locator.resource()))
161+
}
158162
PlaintextType::Array(array_type) => {
159163
// Retrieve the number of elements in the array.
160164
let num_elements = **array_type.length() as u64;
@@ -266,7 +270,9 @@ pub fn cost_per_command<N: Network>(
266270
FinalizeType::Plaintext(PlaintextType::Literal(LiteralType::Field)) => Ok(1_500),
267271
FinalizeType::Plaintext(PlaintextType::Literal(_)) => Ok(500),
268272
FinalizeType::Plaintext(PlaintextType::Array(_)) => bail!("'div' does not support arrays"),
269-
FinalizeType::Plaintext(PlaintextType::Struct(_)) => bail!("'div' does not support structs"),
273+
FinalizeType::Plaintext(PlaintextType::Struct(_) | PlaintextType::ExternalStruct(_)) => {
274+
bail!("'div' does not support structs")
275+
}
270276
FinalizeType::Future(_) => bail!("'div' does not support futures"),
271277
}
272278
}
@@ -345,7 +351,9 @@ pub fn cost_per_command<N: Network>(
345351
FinalizeType::Plaintext(PlaintextType::Literal(LiteralType::Scalar)) => Ok(10_000),
346352
FinalizeType::Plaintext(PlaintextType::Literal(_)) => Ok(500),
347353
FinalizeType::Plaintext(PlaintextType::Array(_)) => bail!("'mul' does not support arrays"),
348-
FinalizeType::Plaintext(PlaintextType::Struct(_)) => bail!("'mul' does not support structs"),
354+
FinalizeType::Plaintext(PlaintextType::Struct(_) | PlaintextType::ExternalStruct(_)) => {
355+
bail!("'mul' does not support structs")
356+
}
349357
FinalizeType::Future(_) => bail!("'mul' does not support futures"),
350358
}
351359
}
@@ -365,7 +373,9 @@ pub fn cost_per_command<N: Network>(
365373
FinalizeType::Plaintext(PlaintextType::Literal(LiteralType::Field)) => Ok(1_500),
366374
FinalizeType::Plaintext(PlaintextType::Literal(_)) => Ok(500),
367375
FinalizeType::Plaintext(PlaintextType::Array(_)) => bail!("'pow' does not support arrays"),
368-
FinalizeType::Plaintext(PlaintextType::Struct(_)) => bail!("'pow' does not support structs"),
376+
FinalizeType::Plaintext(PlaintextType::Struct(_) | PlaintextType::ExternalStruct(_)) => {
377+
bail!("'pow' does not support structs")
378+
}
369379
FinalizeType::Future(_) => bail!("'pow' does not support futures"),
370380
}
371381
}

synthesizer/process/src/stack/finalize_types/initialize.rs

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -127,15 +127,10 @@ impl<N: Network> FinalizeTypes<N> {
127127

128128
impl<N: Network> FinalizeTypes<N> {
129129
/// Ensure the given input register is well-formed.
130-
#[inline]
131130
fn check_input(&mut self, stack: &Stack<N>, register: &Register<N>, finalize_type: &FinalizeType<N>) -> Result<()> {
132131
// Ensure the register type is defined in the program.
133132
match finalize_type {
134-
FinalizeType::Plaintext(PlaintextType::Literal(..)) => (),
135-
FinalizeType::Plaintext(PlaintextType::Struct(struct_name)) => {
136-
RegisterTypes::check_struct(stack, struct_name)?
137-
}
138-
FinalizeType::Plaintext(PlaintextType::Array(array_type)) => RegisterTypes::check_array(stack, array_type)?,
133+
FinalizeType::Plaintext(plaintext_type) => RegisterTypes::check_plaintext_type(stack, plaintext_type)?,
139134
FinalizeType::Future(..) => (),
140135
};
141136

@@ -630,19 +625,27 @@ impl<N: Network> FinalizeTypes<N> {
630625
| CastType::Plaintext(PlaintextType::Literal(..)) => {
631626
ensure!(instruction.operands().len() == 1, "Expected 1 operand.");
632627
}
633-
CastType::Plaintext(PlaintextType::Struct(struct_name)) => {
634-
// Ensure the struct name exists in the program.
635-
if !stack.program().contains_struct(struct_name) {
636-
bail!("Struct '{struct_name}' is not defined.")
637-
}
628+
CastType::Plaintext(plaintext @ PlaintextType::Struct(struct_name)) => {
629+
// Ensure that the type is valid.
630+
RegisterTypes::check_plaintext_type(stack, plaintext)?;
638631
// Retrieve the struct.
639632
let struct_ = stack.program().get_struct(struct_name)?;
640633
// Ensure the operand types match the struct.
641634
self.matches_struct(stack, instruction.operands(), struct_)?;
642635
}
643-
CastType::Plaintext(PlaintextType::Array(array_type)) => {
644-
// Ensure that the array type is valid.
645-
RegisterTypes::check_array(stack, array_type)?;
636+
CastType::Plaintext(plaintext @ PlaintextType::ExternalStruct(locator)) => {
637+
// Ensure that the type is valid.
638+
RegisterTypes::check_plaintext_type(stack, plaintext)?;
639+
let external_stack = stack.get_external_stack(locator.program_id())?;
640+
let struct_name = locator.resource();
641+
// Retrieve the struct.
642+
let struct_ = external_stack.program().get_struct(struct_name)?;
643+
// Ensure the operand types match the struct.
644+
self.matches_struct(&*external_stack, instruction.operands(), struct_)?;
645+
}
646+
CastType::Plaintext(plaintext @ PlaintextType::Array(array_type)) => {
647+
// Ensure that the type is valid.
648+
RegisterTypes::check_plaintext_type(stack, plaintext)?;
646649
// Ensure the operand types match the element type.
647650
self.matches_array(stack, instruction.operands(), array_type)?;
648651
}

synthesizer/process/src/stack/finalize_types/mod.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,17 @@ impl<N: Network> FinalizeTypes<N> {
142142
None => bail!("'{identifier}' does not exist in struct '{struct_name}'"),
143143
}
144144
}
145+
// Access the member on the path to output the register type.
146+
(FinalizeType::Plaintext(PlaintextType::ExternalStruct(locator)), Access::Member(identifier)) => {
147+
// Retrieve the member type from the struct and check that it exists.
148+
let external_stack = stack.get_external_stack(locator.program_id())?;
149+
match external_stack.program().get_struct(locator.resource())?.members().get(identifier) {
150+
// Retrieve the member and update `finalize_type` for the next iteration.
151+
Some(member_type) => finalize_type = FinalizeType::Plaintext(member_type.clone()),
152+
// Halts if the member does not exist.
153+
None => bail!("'{identifier}' does not exist in struct '{locator}'"),
154+
}
155+
}
145156
// Access the member on the path to output the register type and check that it is in bounds.
146157
(FinalizeType::Plaintext(PlaintextType::Array(array_type)), Access::Index(index)) => {
147158
match index < array_type.length() {
@@ -178,7 +189,10 @@ impl<N: Network> FinalizeTypes<N> {
178189
None => bail!("Index out of bounds"),
179190
}
180191
}
181-
(FinalizeType::Plaintext(PlaintextType::Struct(..)), Access::Index(..))
192+
(
193+
FinalizeType::Plaintext(PlaintextType::Struct(..) | PlaintextType::ExternalStruct(..)),
194+
Access::Index(..),
195+
)
182196
| (FinalizeType::Plaintext(PlaintextType::Array(..)), Access::Member(..))
183197
| (FinalizeType::Future(..), Access::Member(..)) => {
184198
bail!("Invalid access `{access}`")

0 commit comments

Comments
 (0)