Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions compiler/noirc_evaluator/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,17 +174,6 @@ impl RuntimeError {
location.span,
)
}
RuntimeError::UnconstrainedSliceReturnToConstrained { .. } => {
let primary_message = self.to_string();
let location =
self.call_stack().back().expect("Expected RuntimeError to have a location");

Diagnostic::simple_error(
primary_message,
"If attempting to return a `Vec` type, `Vec` contains a slice internally.".to_string(),
location.span,
)
}
_ => {
let message = self.to_string();
let location =
Expand Down
10 changes: 9 additions & 1 deletion compiler/noirc_frontend/src/hir/type_check/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ pub enum TypeCheckError {
NoMatchingImplFound { constraints: Vec<(Type, String)>, span: Span },
#[error("Constraint for `{typ}: {trait_name}` is not needed, another matching impl is already in scope")]
UnneededTraitConstraint { trait_name: String, typ: Type, span: Span },
#[error(
"Cannot pass a mutable reference from a constrained runtime to an unconstrained runtime"
)]
ConstrainedReferenceToUnconstrained { span: Span },
#[error("Slices cannot be returned from an unconstrained runtime to a constrained runtime")]
UnconstrainedSliceReturnToConstrained { span: Span },
}

impl TypeCheckError {
Expand Down Expand Up @@ -202,7 +208,9 @@ impl From<TypeCheckError> for Diagnostic {
| TypeCheckError::AmbiguousBitWidth { span, .. }
| TypeCheckError::IntegerAndFieldBinaryOperation { span }
| TypeCheckError::OverflowingAssignment { span, .. }
| TypeCheckError::FieldModulo { span } => {
| TypeCheckError::FieldModulo { span }
| TypeCheckError::ConstrainedReferenceToUnconstrained { span }
| TypeCheckError::UnconstrainedSliceReturnToConstrained { span } => {
Diagnostic::simple_error(error.to_string(), String::new(), span)
}
TypeCheckError::PublicReturnType { typ, span } => Diagnostic::simple_error(
Expand Down
50 changes: 49 additions & 1 deletion compiler/noirc_frontend/src/hir/type_check/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ impl<'interner> TypeChecker<'interner> {
}
}

fn is_unconstrained_call(&self, expr: &ExprId) -> bool {
if let HirExpression::Ident(expr::HirIdent { id, .. }) = self.interner.expression(expr) {
if let Some(DefinitionKind::Function(func_id)) =
self.interner.try_definition(id).map(|def| &def.kind)
{
let modifiers = self.interner.function_modifiers(func_id);
return modifiers.is_unconstrained;
}
}
false
}

/// Infers a type for a given expression, and return this type.
/// As a side-effect, this function will also remember this type in the NodeInterner
/// for the given expr_id key.
Expand Down Expand Up @@ -139,6 +151,15 @@ impl<'interner> TypeChecker<'interner> {
}
HirExpression::Index(index_expr) => self.check_index_expression(expr_id, index_expr),
HirExpression::Call(call_expr) => {
// Need to setup these flags here as `self` is borrowed mutably to type check the rest of the call expression
// These flags are later used to type check calls to unconstrained functions from constrained functions
let current_func = self
.current_function
.expect("Can only have call expression inside of a function body");
let func_mod = self.interner.function_modifiers(&current_func);
let is_current_func_constrained = !func_mod.is_unconstrained;
let is_unconstrained_call = self.is_unconstrained_call(&call_expr.func);

self.check_if_deprecated(&call_expr.func);

let function = self.check_expression(&call_expr.func);
Expand All @@ -147,8 +168,35 @@ impl<'interner> TypeChecker<'interner> {
let typ = self.check_expression(arg);
(typ, *arg, self.interner.expr_span(arg))
});

// Check that we are not passing a mutable reference from a constrained runtime to an unconstrained runtime
for (typ, _, _) in args.iter() {
if is_current_func_constrained
&& is_unconstrained_call
&& matches!(&typ, Type::MutableReference(_))
{
self.errors.push(TypeCheckError::ConstrainedReferenceToUnconstrained {
span: self.interner.expr_span(expr_id),
});
return Type::Error;
}
}

let span = self.interner.expr_span(expr_id);
self.bind_function_type(function, args, span)
let return_type = self.bind_function_type(function, args, span);

// Check that we are not passing a slice from an unconstrained runtime to a constrained runtime
if is_current_func_constrained
&& is_unconstrained_call
&& return_type.contains_slice()
{
self.errors.push(TypeCheckError::UnconstrainedSliceReturnToConstrained {
span: self.interner.expr_span(expr_id),
});
return Type::Error;
}

return_type
}
HirExpression::MethodCall(mut method_call) => {
let mut object_type = self.check_expression(&method_call.object).follow_bindings();
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/hir_def/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ impl Type {
}
}

fn contains_slice(&self) -> bool {
pub(crate) fn contains_slice(&self) -> bool {
match self {
Type::Array(size, _) => matches!(size.as_ref(), Type::NotConstant),
Type::Struct(struct_typ, generics) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "brillig_mut_ref_from_acir"
type = "bin"
authors = [""]
compiler_version = ">=0.23.0"

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
unconstrained fn mut_ref_identity(value: &mut Field) -> Field {
*value
}

fn main(mut x: Field, y: pub Field) {
let returned_x = mut_ref_identity(&mut x);
assert(returned_x == x);
}