Skip to content

Commit

Permalink
Merge pull request #27917 from AleoHQ/feat/add-opcodes
Browse files Browse the repository at this point in the history
[Feat] Add `network.id` and `self.address` opcodes
  • Loading branch information
d0cd authored May 1, 2024
2 parents 13b29db + 37d0b0e commit 29115ed
Show file tree
Hide file tree
Showing 23 changed files with 786 additions and 108 deletions.
114 changes: 57 additions & 57 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ members = [
[workspace.dependencies.snarkvm]
#version = "0.16.19"
git = "https://github.com/AleoHQ/snarkVM"
rev = "2cbf34a"
rev = "da3d78a"

[lib]
path = "leo/lib.rs"
Expand Down
2 changes: 1 addition & 1 deletion compiler/ast/src/expressions/literal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use super::*;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Literal {
// todo: deserialize values here
/// An address literal, e.g., `aleo1qnr4dkkvkgfqph0vzc3y6z2eu975wnpz2925ntjccd5cfqxtyu8s7pyjh9`.
/// An address literal, e.g., `aleo1qnr4dkkvkgfqph0vzc3y6z2eu975wnpz2925ntjccd5cfqxtyu8s7pyjh9` or `hello.aleo`.
Address(String, #[serde(with = "leo_span::span_json")] Span, NodeID),
/// A boolean literal, either `true` or `false`.
Boolean(bool, #[serde(with = "leo_span::span_json")] Span, NodeID),
Expand Down
36 changes: 33 additions & 3 deletions compiler/parser/src/parser/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -441,8 +441,8 @@ impl ParserContext<'_> {

// Parses an external function call `credits.aleo/transfer()` or locator `token.aleo/accounts`.
fn parse_external_resource(&mut self, expr: Expression, network_span: Span) -> Result<Expression> {
// Eat an external function call.
self.eat(&Token::Div); // todo: Make `/` a more general token.
// Parse `/`.
self.expect(&Token::Div)?;

// Parse name.
let name = self.expect_identifier()?;
Expand Down Expand Up @@ -507,8 +507,35 @@ impl ParserContext<'_> {
} else if self.eat(&Token::Leo) {
return Err(ParserError::only_aleo_external_calls(expr.span()).into());
} else if self.eat(&Token::Aleo) {
expr = self.parse_external_resource(expr, self.prev_token.span)?;
if self.token.token == Token::Div {
expr = self.parse_external_resource(expr, self.prev_token.span)?;
} else {
// Parse as address literal, e.g. `hello.aleo`.
if !matches!(expr, Expression::Identifier(_)) {
self.emit_err(ParserError::unexpected(expr.to_string(), "an identifier", expr.span()))
}

expr = Expression::Literal(Literal::Address(
format!("{}.aleo", expr),
expr.span(),
self.node_builder.next_id(),
))
}
} else {
// Parse instances of `self.address`.
if let Expression::Identifier(id) = expr {
if id.name == sym::SelfLower && self.token.token == Token::Address {
let span = self.expect(&Token::Address)?;
// Convert `self.address` to the current program name. TODO: Move this conversion to canonicalization pass when the new pass is added.
// Note that the unwrap is safe as in order to get to this stage of parsing a program name must have already been parsed.
return Ok(Expression::Literal(Literal::Address(
format!("{}.aleo", self.program_name.unwrap()),
expr.span() + span,
self.node_builder.next_id(),
)));
}
}

// Parse identifier name.
let name = self.expect_identifier()?;

Expand Down Expand Up @@ -769,6 +796,9 @@ impl ParserContext<'_> {
Token::Future => {
Expression::Identifier(Identifier { name: sym::Future, span, id: self.node_builder.next_id() })
}
Token::Network => {
Expression::Identifier(Identifier { name: sym::network, span, id: self.node_builder.next_id() })
}
t if crate::type_::TYPE_TOKENS.contains(&t) => Expression::Identifier(Identifier {
name: t.keyword_to_symbol().unwrap(),
span,
Expand Down
4 changes: 4 additions & 0 deletions compiler/parser/src/tokenizer/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ pub enum Token {
Block,
Eof,
Leo,
Network,
}

/// Represents all valid Leo keyword tokens.
Expand Down Expand Up @@ -180,6 +181,7 @@ pub const KEYWORD_TOKENS: &[Token] = &[
Token::Inline,
Token::Let,
Token::Mapping,
Token::Network,
Token::Private,
Token::Program,
Token::Public,
Expand Down Expand Up @@ -237,6 +239,7 @@ impl Token {
Token::Let => sym::Let,
Token::Leo => sym::leo,
Token::Mapping => sym::mapping,
Token::Network => sym::network,
Token::Private => sym::private,
Token::Program => sym::program,
Token::Public => sym::public,
Expand Down Expand Up @@ -374,6 +377,7 @@ impl fmt::Display for Token {
Block => write!(f, "block"),
Leo => write!(f, "leo"),
Eof => write!(f, "<eof>"),
Network => write!(f, "network"),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/passes/src/code_generation/visit_program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ impl<'a> CodeGenerator<'a> {
// TODO: Figure out a better way to initialize.
self.variable_mapping.insert(&sym::SelfLower, "self".to_string());
self.variable_mapping.insert(&sym::block, "block".to_string());
self.variable_mapping.insert(&sym::network, "network".to_string());
self.current_function = Some(function);

// Construct the header of the function.
Expand Down
4 changes: 3 additions & 1 deletion compiler/passes/src/flattening/flatten_statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,9 @@ impl StatementReconstructor for Flattener<'_> {
let guard = self.construct_guard();

match input.expression {
Expression::Unit(_) | Expression::Identifier(_) => self.returns.push((guard, input)),
Expression::Unit(_) | Expression::Identifier(_) | Expression::Access(_) => {
self.returns.push((guard, input))
}
_ => unreachable!("SSA guarantees that the expression is always an identifier or unit expression."),
};

Expand Down
32 changes: 14 additions & 18 deletions compiler/passes/src/type_checking/check_expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,22 +202,12 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
Expression::Identifier(identifier) if identifier.name == sym::SelfLower => match access.name.name {
sym::caller => {
// Check that the operation is not invoked in a `finalize` block.
if self.scope_state.variant == Some(Variant::AsyncFunction) {
self.handler.emit_err(TypeCheckerError::invalid_operation_inside_finalize(
"self.caller",
access.name.span(),
))
}
self.check_access_allowed("self.caller", false, access.name.span());
return Some(Type::Address);
}
sym::signer => {
// Check that operation is not invoked in a `finalize` block.
if self.scope_state.variant == Some(Variant::AsyncFunction) {
self.handler.emit_err(TypeCheckerError::invalid_operation_inside_finalize(
"self.signer",
access.name.span(),
))
}
self.check_access_allowed("self.signer", false, access.name.span());
return Some(Type::Address);
}
_ => {
Expand All @@ -228,18 +218,24 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
Expression::Identifier(identifier) if identifier.name == sym::block => match access.name.name {
sym::height => {
// Check that the operation is invoked in a `finalize` block.
if self.scope_state.variant != Some(Variant::AsyncFunction) {
self.handler.emit_err(TypeCheckerError::invalid_operation_outside_finalize(
"block.height",
access.name.span(),
))
}
self.check_access_allowed("block.height", true, access.name.span());
return Some(Type::Integer(IntegerType::U32));
}
_ => {
self.emit_err(TypeCheckerError::invalid_block_access(access.name.span()));
}
},
// If the access expression is of the form `network.<name>`, then check that the <name> is valid.
Expression::Identifier(identifier) if identifier.name == sym::network => match access.name.name {
sym::id => {
// Check that the operation is not invoked outside a `finalize` block.
self.check_access_allowed("network.id", true, access.name.span());
return Some(Type::Integer(IntegerType::U16));
}
_ => {
self.emit_err(TypeCheckerError::invalid_block_access(access.name.span()));
}
},
_ => {
// Check that the type of `inner` in `inner.name` is a struct.
match self.visit_expression(&access.inner, &None) {
Expand Down
43 changes: 16 additions & 27 deletions compiler/passes/src/type_checking/checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ impl<'a> TypeChecker<'a> {
/// Emits an error if the correct number of arguments are not provided.
/// Emits an error if the arguments are not of the correct type.
pub(crate) fn check_core_function_call(
&self,
&mut self,
core_function: CoreFunction,
arguments: &[(Option<Type>, Span)],
function_span: Span,
Expand Down Expand Up @@ -978,10 +978,7 @@ impl<'a> TypeChecker<'a> {
}
CoreFunction::MappingGet => {
// Check that the operation is invoked in a `finalize` block.
if self.scope_state.variant != Some(Variant::AsyncFunction) {
self.handler
.emit_err(TypeCheckerError::invalid_operation_outside_finalize("Mapping::get", function_span))
}
self.check_access_allowed("Mapping::get", true, function_span);
// Check that the first argument is a mapping.
if let Some(mapping_type) = self.assert_mapping_type(&arguments[0].0, arguments[0].1) {
// Check that the second argument matches the key type of the mapping.
Expand All @@ -994,12 +991,7 @@ impl<'a> TypeChecker<'a> {
}
CoreFunction::MappingGetOrUse => {
// Check that the operation is invoked in a `finalize` block.
if self.scope_state.variant != Some(Variant::AsyncFunction) {
self.handler.emit_err(TypeCheckerError::invalid_operation_outside_finalize(
"Mapping::get_or",
function_span,
))
}
self.check_access_allowed("Mapping::get_or", true, function_span);
// Check that the first argument is a mapping.
if let Some(mapping_type) = self.assert_mapping_type(&arguments[0].0, arguments[0].1) {
// Check that the second argument matches the key type of the mapping.
Expand All @@ -1014,10 +1006,7 @@ impl<'a> TypeChecker<'a> {
}
CoreFunction::MappingSet => {
// Check that the operation is invoked in a `finalize` block.
if self.scope_state.variant != Some(Variant::AsyncFunction) {
self.handler
.emit_err(TypeCheckerError::invalid_operation_outside_finalize("Mapping::set", function_span))
}
self.check_access_allowed("Mapping::set", true, function_span);
// Check that the first argument is a mapping.
if let Some(mapping_type) = self.assert_mapping_type(&arguments[0].0, arguments[0].1) {
// Cannot modify external mappings.
Expand All @@ -1036,12 +1025,7 @@ impl<'a> TypeChecker<'a> {
}
CoreFunction::MappingRemove => {
// Check that the operation is invoked in a `finalize` block.
if self.scope_state.variant != Some(Variant::AsyncFunction) {
self.handler.emit_err(TypeCheckerError::invalid_operation_outside_finalize(
"Mapping::remove",
function_span,
))
}
self.check_access_allowed("Mapping::remove", true, function_span);
// Check that the first argument is a mapping.
if let Some(mapping_type) = self.assert_mapping_type(&arguments[0].0, arguments[0].1) {
// Cannot modify external mappings.
Expand All @@ -1059,12 +1043,7 @@ impl<'a> TypeChecker<'a> {
}
CoreFunction::MappingContains => {
// Check that the operation is invoked in a `finalize` block.
if self.scope_state.variant != Some(Variant::AsyncFunction) {
self.handler.emit_err(TypeCheckerError::invalid_operation_outside_finalize(
"Mapping::contains",
function_span,
))
}
self.check_access_allowed("Mapping::contains", true, function_span);
// Check that the first argument is a mapping.
if let Some(mapping_type) = self.assert_mapping_type(&arguments[0].0, arguments[0].1) {
// Check that the second argument matches the key type of the mapping.
Expand Down Expand Up @@ -1506,6 +1485,16 @@ impl<'a> TypeChecker<'a> {
self.handler.emit_err(err);
}
}

// Checks if the access operation is valid inside the current function variant.
pub(crate) fn check_access_allowed(&mut self, name: &str, finalize_op: bool, span: Span) {
// Check that the function context matches.
if self.scope_state.variant == Some(Variant::AsyncFunction) && !finalize_op {
self.handler.emit_err(TypeCheckerError::invalid_operation_inside_finalize(name, span))
} else if self.scope_state.variant != Some(Variant::AsyncFunction) && finalize_op {
self.handler.emit_err(TypeCheckerError::invalid_operation_outside_finalize(name, span))
}
}
}

fn types_to_string(types: &[Type]) -> String {
Expand Down
2 changes: 2 additions & 0 deletions compiler/span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,8 @@ symbols! {
stub,
block,
height,
network,
id,
}

/// An interned string.
Expand Down
18 changes: 18 additions & 0 deletions tests/expectations/compiler/address/special_address.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
namespace: Compile
expectation: Pass
outputs:
- - compile:
- initial_symbol_table: f159adcd5ea24c580a6b8535b217667a40173809e5802a0b03db3dc5b9bec9aa
type_checked_symbol_table: 9fd0ee7288d5151305f4cd3820594bd9db7accb589bc936e186709af1df6de21
unrolled_symbol_table: 9fd0ee7288d5151305f4cd3820594bd9db7accb589bc936e186709af1df6de21
initial_ast: 21db64864f84959ad71dace62a2487285c3b6bb74818536b83a54b63a2bd8d82
unrolled_ast: 21db64864f84959ad71dace62a2487285c3b6bb74818536b83a54b63a2bd8d82
ssa_ast: d4e2a516deaa30f8663bb3cd1501c52e3cc781b330dbb149ee3bad0692a8cb59
flattened_ast: 175fbd23f91421e3d47440c8a7e00fb9e3a2bef1147e061cd8a3f2bd0c978098
destructured_ast: a23caa23b3ac10d6c2a1b119af502a9ec4380cf521eb65da2c9e2a5f19d44172
inlined_ast: a23caa23b3ac10d6c2a1b119af502a9ec4380cf521eb65da2c9e2a5f19d44172
dce_ast: a23caa23b3ac10d6c2a1b119af502a9ec4380cf521eb65da2c9e2a5f19d44172
bytecode: d9e6c28f9e5527abe9cdb07b9d35375901446415f5d645b0363597200ee45be7
errors: ""
warnings: ""
18 changes: 18 additions & 0 deletions tests/expectations/compiler/expression/network_id.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
namespace: Compile
expectation: Pass
outputs:
- - compile:
- initial_symbol_table: 02b83350abcecb36109bf268cf52b9fc867ab1893c49badf31ff527156528943
type_checked_symbol_table: 1ace971bd20adb9ce07f802070f05c51733af791ef32c7b1130d4a4b2182768d
unrolled_symbol_table: 1ace971bd20adb9ce07f802070f05c51733af791ef32c7b1130d4a4b2182768d
initial_ast: 6dc0a710ab752f571f4dae9fdb172a7fa1c43e3af3858cbc8cf96c5d510a0c3a
unrolled_ast: 6dc0a710ab752f571f4dae9fdb172a7fa1c43e3af3858cbc8cf96c5d510a0c3a
ssa_ast: e09d30595377e81788433b702f76f1338ff4bb720f8564e2560e5f78ebd18bc0
flattened_ast: 16df732ae63243e249201817b30ae02b8a190072d39894648607970eb2b09192
destructured_ast: b2f615fbb0825b50c961b4014e2e2d60117b543cab0d2e1acd4f3237c878e95e
inlined_ast: d3f7291df3faf6f8a4893b91133fe183d44c35f3d9c4b9d270d71f943482f965
dce_ast: d3f7291df3faf6f8a4893b91133fe183d44c35f3d9c4b9d270d71f943482f965
bytecode: ae04a04e7ffb01dfdd0ae0249f31649302bc120ea928c5ace16bc0879140e1f9
errors: ""
warnings: ""
Loading

0 comments on commit 29115ed

Please sign in to comment.