Skip to content

Commit

Permalink
feat: add mvp of unsafe blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
TomAFrench committed Feb 26, 2024
1 parent 446dcb3 commit f2a7cd5
Show file tree
Hide file tree
Showing 22 changed files with 334 additions and 206 deletions.
34 changes: 21 additions & 13 deletions aztec_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ fn generate_storage_implementation(module: &mut SortedModule) -> Result<(), Azte
true,
)),
)],
&BlockExpression(vec![storage_constructor_statement]),
&BlockExpression { is_unsafe: false, statements: vec![storage_constructor_statement] },
&[],
&return_type(chained_path!("Self")),
));
Expand Down Expand Up @@ -609,25 +609,25 @@ fn transform_function(
// Add access to the storage struct
if storage_defined {
let storage_def = abstract_storage(&ty.to_lowercase(), false);
func.def.body.0.insert(0, storage_def);
func.def.body.statements.insert(0, storage_def);
}

// Insert the context creation as the first action
let create_context = create_context(&context_name, &func.def.parameters)?;
func.def.body.0.splice(0..0, (create_context).iter().cloned());
func.def.body.statements.splice(0..0, (create_context).iter().cloned());

// Add the inputs to the params
let input = create_inputs(&inputs_name);
func.def.parameters.insert(0, input);

// Abstract return types such that they get added to the kernel's return_values
if let Some(return_values) = abstract_return_values(func) {
func.def.body.0.push(return_values);
func.def.body.statements.push(return_values);
}

// Push the finish method call to the end of the function
let finish_def = create_context_finish();
func.def.body.0.push(finish_def);
func.def.body.statements.push(finish_def);

let return_type = create_return_type(&return_type_name);
func.def.return_type = return_type;
Expand All @@ -651,7 +651,7 @@ fn transform_vm_function(
) -> Result<(), AztecMacroError> {
// Push Avm context creation to the beginning of the function
let create_context = create_avm_context()?;
func.def.body.0.insert(0, create_context);
func.def.body.statements.insert(0, create_context);

// We want the function to be seen as a public function
func.def.is_open = true;
Expand All @@ -671,7 +671,7 @@ fn transform_vm_function(
///
/// This will allow developers to access their contract' storage struct in unconstrained functions
fn transform_unconstrained(func: &mut NoirFunction) {
func.def.body.0.insert(0, abstract_storage("Unconstrained", true));
func.def.body.statements.insert(0, abstract_storage("Unconstrained", true));
}

fn collect_crate_structs(crate_id: &CrateId, context: &HirContext) -> Vec<StructId> {
Expand Down Expand Up @@ -1002,10 +1002,15 @@ fn generate_selector_impl(structure: &NoirStruct) -> TypeImpl {
let mut from_signature_path = selector_path.clone();
from_signature_path.segments.push(ident("from_signature"));

let selector_fun_body = BlockExpression(vec![make_statement(StatementKind::Expression(call(
variable_path(from_signature_path),
vec![expression(ExpressionKind::Literal(Literal::Str(SIGNATURE_PLACEHOLDER.to_string())))],
)))]);
let selector_fun_body = BlockExpression {
is_unsafe: false,
statements: vec![make_statement(StatementKind::Expression(call(
variable_path(from_signature_path),
vec![expression(ExpressionKind::Literal(Literal::Str(
SIGNATURE_PLACEHOLDER.to_string(),
)))],
)))],
};

// Define `FunctionSelector` return type
let return_type =
Expand Down Expand Up @@ -1230,7 +1235,7 @@ fn create_avm_context() -> Result<Statement, AztecMacroError> {
fn abstract_return_values(func: &NoirFunction) -> Option<Statement> {
let current_return_type = func.return_type().typ;
let len = func.def.body.len();
let last_statement = &func.def.body.0[len - 1];
let last_statement = &func.def.body.statements[len - 1];

// TODO: (length, type) => We can limit the size of the array returned to be limited by kernel size
// Doesn't need done until we have settled on a kernel size
Expand Down Expand Up @@ -1487,7 +1492,10 @@ fn create_loop_over(var: Expression, loop_body: Vec<Statement>) -> Statement {

// What will be looped over
// - `hasher.add({ident}[i] as Field)`
let for_loop_block = expression(ExpressionKind::Block(BlockExpression(loop_body)));
let for_loop_block = expression(ExpressionKind::Block(BlockExpression {
is_unsafe: false,
statements: loop_body,
}));

// `for i in 0..{ident}.len()`
make_statement(StatementKind::For(ForLoopStatement {
Expand Down
37 changes: 22 additions & 15 deletions compiler/noirc_frontend/src/ast/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,16 +180,19 @@ impl Expression {
// with tuples without calling them. E.g. `if c { t } else { e }(a, b)` is interpreted
// as a sequence of { if, tuple } rather than a function call. This behavior matches rust.
let kind = if matches!(&lhs.kind, ExpressionKind::If(..)) {
ExpressionKind::Block(BlockExpression(vec![
Statement { kind: StatementKind::Expression(lhs), span },
Statement {
kind: StatementKind::Expression(Expression::new(
ExpressionKind::Tuple(arguments),
ExpressionKind::Block(BlockExpression {
is_unsafe: false,
statements: vec![
Statement { kind: StatementKind::Expression(lhs), span },
Statement {
kind: StatementKind::Expression(Expression::new(
ExpressionKind::Tuple(arguments),
span,
)),
span,
)),
span,
},
]))
},
],
})
} else {
ExpressionKind::Call(Box::new(CallExpression { func: Box::new(lhs), arguments }))
};
Expand Down Expand Up @@ -456,19 +459,22 @@ pub struct IndexExpression {
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct BlockExpression(pub Vec<Statement>);
pub struct BlockExpression {
pub is_unsafe: bool,
pub statements: Vec<Statement>,
}

impl BlockExpression {
pub fn pop(&mut self) -> Option<StatementKind> {
self.0.pop().map(|stmt| stmt.kind)
self.statements.pop().map(|stmt| stmt.kind)
}

pub fn len(&self) -> usize {
self.0.len()
self.statements.len()
}

pub fn is_empty(&self) -> bool {
self.0.is_empty()
self.statements.is_empty()
}
}

Expand Down Expand Up @@ -537,8 +543,9 @@ impl Display for Literal {

impl Display for BlockExpression {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{{")?;
for statement in &self.0 {
let safety = if self.is_unsafe { "unsafe " } else { "" };
writeln!(f, "{safety}{{")?;
for statement in &self.statements {
let statement = statement.kind.to_string();
for line in statement.lines() {
writeln!(f, " {line}")?;
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/ast/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ impl NoirFunction {
&mut self.def
}
pub fn number_of_statements(&self) -> usize {
self.def.body.0.len()
self.def.body.statements.len()
}
pub fn span(&self) -> Span {
self.def.span
Expand Down
16 changes: 11 additions & 5 deletions compiler/noirc_frontend/src/ast/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -588,10 +588,13 @@ impl ForRange {
};

let block_span = block.span;
let new_block = BlockExpression(vec![
let_elem,
Statement { kind: StatementKind::Expression(block), span: block_span },
]);
let new_block = BlockExpression {
is_unsafe: false,
statements: vec![
let_elem,
Statement { kind: StatementKind::Expression(block), span: block_span },
],
};
let new_block = Expression::new(ExpressionKind::Block(new_block), block_span);
let for_loop = Statement {
kind: StatementKind::For(ForLoopStatement {
Expand All @@ -603,7 +606,10 @@ impl ForRange {
span: for_loop_span,
};

let block = ExpressionKind::Block(BlockExpression(vec![let_array, for_loop]));
let block = ExpressionKind::Block(BlockExpression {
is_unsafe: false,
statements: vec![let_array, for_loop],
});
StatementKind::Expression(Expression::new(block, for_loop_span))
}
}
Expand Down
59 changes: 38 additions & 21 deletions compiler/noirc_frontend/src/debug/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,10 @@ impl DebugInstrumenter {
})
.collect();

self.walk_scope(&mut func.body.0, func.span);
self.walk_scope(&mut func.body.statements, func.span);

// prepend fn params:
func.body.0 = [set_fn_params, func.body.0.clone()].concat();
func.body.statements = [set_fn_params, func.body.statements.clone()].concat();
}

// Modify a vector of statements in-place, adding instrumentation for sets and drops.
Expand Down Expand Up @@ -212,7 +212,10 @@ impl DebugInstrumenter {
pattern: ast::Pattern::Tuple(vars_pattern, let_stmt.pattern.span()),
r#type: ast::UnresolvedType::unspecified(),
expression: ast::Expression {
kind: ast::ExpressionKind::Block(ast::BlockExpression(block_stmts)),
kind: ast::ExpressionKind::Block(ast::BlockExpression {
is_unsafe: true,
statements: block_stmts,
}),
span: let_stmt.expression.span,
},
}),
Expand Down Expand Up @@ -299,11 +302,14 @@ impl DebugInstrumenter {
kind: ast::StatementKind::Assign(ast::AssignStatement {
lvalue: assign_stmt.lvalue.clone(),
expression: ast::Expression {
kind: ast::ExpressionKind::Block(ast::BlockExpression(vec![
ast::Statement { kind: let_kind, span: expression_span },
new_assign_stmt,
ast::Statement { kind: ret_kind, span: expression_span },
])),
kind: ast::ExpressionKind::Block(ast::BlockExpression {
is_unsafe: false,
statements: vec![
ast::Statement { kind: let_kind, span: expression_span },
new_assign_stmt,
ast::Statement { kind: ret_kind, span: expression_span },
],
}),
span: expression_span,
},
}),
Expand All @@ -313,7 +319,7 @@ impl DebugInstrumenter {

fn walk_expr(&mut self, expr: &mut ast::Expression) {
match &mut expr.kind {
ast::ExpressionKind::Block(ast::BlockExpression(ref mut statements)) => {
ast::ExpressionKind::Block(ast::BlockExpression { ref mut statements, .. }) => {
self.scope.push(HashMap::default());
self.walk_scope(statements, expr.span);
}
Expand Down Expand Up @@ -384,14 +390,17 @@ impl DebugInstrumenter {

self.walk_expr(&mut for_stmt.block);
for_stmt.block = ast::Expression {
kind: ast::ExpressionKind::Block(ast::BlockExpression(vec![
set_stmt,
ast::Statement {
kind: ast::StatementKind::Semi(for_stmt.block.clone()),
span: for_stmt.block.span,
},
drop_stmt,
])),
kind: ast::ExpressionKind::Block(ast::BlockExpression {
is_unsafe: false,
statements: vec![
set_stmt,
ast::Statement {
kind: ast::StatementKind::Semi(for_stmt.block.clone()),
span: for_stmt.block.span,
},
drop_stmt,
],
}),
span: for_stmt.span,
};
}
Expand Down Expand Up @@ -447,7 +456,9 @@ pub fn build_debug_crate_file() -> String {
__debug_var_assign_oracle(var_id, value);
}
pub fn __debug_var_assign<T>(var_id: u32, value: T) {
__debug_var_assign_inner(var_id, value);
unsafe {{
__debug_var_assign_inner(var_id, value);
}}
}
#[oracle(__debug_var_drop)]
Expand All @@ -456,7 +467,9 @@ pub fn build_debug_crate_file() -> String {
__debug_var_drop_oracle(var_id);
}
pub fn __debug_var_drop<T>(var_id: u32) {
__debug_var_drop_inner(var_id);
unsafe {{
__debug_var_drop_inner(var_id);
}}
}
#[oracle(__debug_dereference_assign)]
Expand All @@ -465,7 +478,9 @@ pub fn build_debug_crate_file() -> String {
__debug_dereference_assign_oracle(var_id, value);
}
pub fn __debug_dereference_assign<T>(var_id: u32, value: T) {
__debug_dereference_assign_inner(var_id, value);
unsafe {{
__debug_dereference_assign_inner(var_id, value);
}}
}
"#
.to_string(),
Expand All @@ -489,7 +504,9 @@ pub fn build_debug_crate_file() -> String {
__debug_oracle_member_assign_{n}(var_id, value, {vars});
}}
pub fn __debug_member_assign_{n}<T, Index>(var_id: u32, value: T, {var_sig}) {{
__debug_inner_member_assign_{n}(var_id, value, {vars});
unsafe {{
__debug_inner_member_assign_{n}(var_id, value, {vars});
}}
}}
"#
Expand Down
8 changes: 4 additions & 4 deletions compiler/noirc_frontend/src/hir/resolution/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ impl<'a> Resolver<'a> {
typ: typ.clone(),
span: name.span(),
}),
body: BlockExpression(Vec::new()),
body: BlockExpression { is_unsafe: false, statements: Vec::new() },
span: name.span(),
where_clause: where_clause.to_vec(),
return_type: return_type.clone(),
Expand Down Expand Up @@ -1926,9 +1926,9 @@ impl<'a> Resolver<'a> {
}

fn resolve_block(&mut self, block_expr: BlockExpression) -> HirExpression {
let statements =
self.in_new_scope(|this| vecmap(block_expr.0, |stmt| this.intern_stmt(stmt.kind)));
HirExpression::Block(HirBlockExpression(statements))
let statements = self
.in_new_scope(|this| vecmap(block_expr.statements, |stmt| this.intern_stmt(stmt.kind)));
HirExpression::Block(HirBlockExpression { is_unsafe: block_expr.is_unsafe, statements })
}

pub fn intern_block(&mut self, block: BlockExpression) -> ExprId {
Expand Down
4 changes: 3 additions & 1 deletion compiler/noirc_frontend/src/hir/type_check/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ pub enum TypeCheckError {
ConstrainedReferenceToUnconstrained { span: Span },
#[error("Slices cannot be returned from an unconstrained runtime to a constrained runtime")]
UnconstrainedSliceReturnToConstrained { span: Span },
#[error("unsafe")]
Unsafe { span: Span },
}

impl TypeCheckError {
Expand Down Expand Up @@ -211,7 +213,7 @@ impl From<TypeCheckError> for Diagnostic {
| TypeCheckError::OverflowingAssignment { span, .. }
| TypeCheckError::FieldModulo { span }
| TypeCheckError::ConstrainedReferenceToUnconstrained { span }
| TypeCheckError::UnconstrainedSliceReturnToConstrained { span } => {
| TypeCheckError::UnconstrainedSliceReturnToConstrained { span } | TypeCheckError::Unsafe { span } => {
Diagnostic::simple_error(error.to_string(), String::new(), span)
}
TypeCheckError::PublicReturnType { typ, span } => Diagnostic::simple_error(
Expand Down
Loading

0 comments on commit f2a7cd5

Please sign in to comment.