Skip to content

Commit 9594dee

Browse files
authored
feat(experimental): Enable ownership syntax (#7603)
1 parent 04f968a commit 9594dee

File tree

39 files changed

+302
-160
lines changed

39 files changed

+302
-160
lines changed

compiler/noirc_driver/src/abi_gen.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ pub(super) fn abi_type_from_hir_type(context: &Context, typ: &Type) -> AbiType {
136136
| Type::Slice(_)
137137
| Type::Function(_, _, _, _) => unreachable!("{typ} cannot be used in the abi"),
138138
Type::FmtString(_, _) => unreachable!("format strings cannot be used in the abi"),
139-
Type::MutableReference(_) => unreachable!("&mut cannot be used in the abi"),
139+
Type::Reference(..) => unreachable!("references cannot be used in the abi"),
140140
}
141141
}
142142

compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ impl FunctionContext<'_> {
371371
unary.location,
372372
))
373373
}
374-
UnaryOp::MutableReference => {
374+
UnaryOp::Reference { mutable: _ } => {
375375
Ok(self.codegen_reference(&unary.rhs)?.map(|rhs| {
376376
match rhs {
377377
value::Value::Normal(value) => {

compiler/noirc_frontend/src/ast/expression.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,9 @@ impl BinaryOpKind {
383383
pub enum UnaryOp {
384384
Minus,
385385
Not,
386-
MutableReference,
386+
Reference {
387+
mutable: bool,
388+
},
387389

388390
/// If implicitly_added is true, this operation was implicitly added by the compiler for a
389391
/// field dereference. The compiler may undo some of these implicitly added dereferences if
@@ -732,7 +734,8 @@ impl Display for UnaryOp {
732734
match self {
733735
UnaryOp::Minus => write!(f, "-"),
734736
UnaryOp::Not => write!(f, "!"),
735-
UnaryOp::MutableReference => write!(f, "&mut"),
737+
UnaryOp::Reference { mutable } if *mutable => write!(f, "&mut"),
738+
UnaryOp::Reference { .. } => write!(f, "&"),
736739
UnaryOp::Dereference { .. } => write!(f, "*"),
737740
}
738741
}

compiler/noirc_frontend/src/ast/mod.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,8 @@ pub enum UnresolvedTypeData {
135135
/// A Trait as return type or parameter of function, including its generics
136136
TraitAsType(Path, GenericTypeArgs),
137137

138-
/// &mut T
139-
MutableReference(Box<UnresolvedType>),
138+
/// &T and &mut T
139+
Reference(Box<UnresolvedType>, /*mutable*/ bool),
140140

141141
// Note: Tuples have no visibility, instead each of their elements may have one.
142142
Tuple(Vec<UnresolvedType>),
@@ -311,7 +311,8 @@ impl std::fmt::Display for UnresolvedTypeData {
311311
other => write!(f, "fn[{other}]({args}) -> {ret}"),
312312
}
313313
}
314-
MutableReference(element) => write!(f, "&mut {element}"),
314+
Reference(element, false) => write!(f, "&{element}"),
315+
Reference(element, true) => write!(f, "&mut {element}"),
315316
Quoted(quoted) => write!(f, "{}", quoted),
316317
Unit => write!(f, "()"),
317318
Error => write!(f, "error"),
@@ -346,7 +347,7 @@ impl std::fmt::Display for UnresolvedTypeExpression {
346347
impl UnresolvedType {
347348
pub fn is_synthesized(&self) -> bool {
348349
match &self.typ {
349-
UnresolvedTypeData::MutableReference(ty) => ty.is_synthesized(),
350+
UnresolvedTypeData::Reference(ty, _) => ty.is_synthesized(),
350351
UnresolvedTypeData::Named(_, _, synthesized) => *synthesized,
351352
_ => false,
352353
}
@@ -424,7 +425,7 @@ impl UnresolvedTypeData {
424425
path_is_wildcard || an_arg_is_unresolved
425426
}
426427
UnresolvedTypeData::TraitAsType(_path, args) => args.contains_unspecified(),
427-
UnresolvedTypeData::MutableReference(typ) => typ.contains_unspecified(),
428+
UnresolvedTypeData::Reference(typ, _) => typ.contains_unspecified(),
428429
UnresolvedTypeData::Tuple(args) => args.iter().any(|arg| arg.contains_unspecified()),
429430
UnresolvedTypeData::Function(args, ret, env, _unconstrained) => {
430431
let args_contains_unspecified = args.iter().any(|arg| arg.contains_unspecified());

compiler/noirc_frontend/src/ast/visitor.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ pub trait Visitor {
393393
true
394394
}
395395

396-
fn visit_mutable_reference_type(&mut self, _: &UnresolvedType, _: Span) -> bool {
396+
fn visit_reference_type(&mut self, _: &UnresolvedType, _mutable: bool, _: Span) -> bool {
397397
true
398398
}
399399

@@ -1382,8 +1382,8 @@ impl UnresolvedType {
13821382
generic_type_args.accept(visitor);
13831383
}
13841384
}
1385-
UnresolvedTypeData::MutableReference(unresolved_type) => {
1386-
if visitor.visit_mutable_reference_type(unresolved_type, self.location.span) {
1385+
UnresolvedTypeData::Reference(unresolved_type, mutable) => {
1386+
if visitor.visit_reference_type(unresolved_type, *mutable, self.location.span) {
13871387
unresolved_type.accept(visitor);
13881388
}
13891389
}

compiler/noirc_frontend/src/elaborator/enums.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -938,7 +938,7 @@ impl<'elab, 'ctx> MatchCompiler<'elab, 'ctx> {
938938
| Type::NamedGeneric(_, _)
939939
| Type::CheckedCast { .. }
940940
| Type::Function(_, _, _, _)
941-
| Type::MutableReference(_)
941+
| Type::Reference(..)
942942
| Type::Forall(_, _)
943943
| Type::Constant(_, _)
944944
| Type::Quoted(_)

compiler/noirc_frontend/src/elaborator/expressions.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use crate::{
3838
token::{FmtStrFragment, Tokens},
3939
};
4040

41-
use super::{Elaborator, LambdaContext, UnsafeBlockStatus};
41+
use super::{Elaborator, LambdaContext, UnsafeBlockStatus, UnstableFeature};
4242

4343
impl Elaborator<'_> {
4444
pub(crate) fn elaborate_expression(&mut self, expr: Expression) -> (ExprId, Type) {
@@ -344,8 +344,12 @@ impl Elaborator<'_> {
344344

345345
let operator = prefix.operator;
346346

347-
if let UnaryOp::MutableReference = operator {
348-
self.check_can_mutate(rhs, rhs_location);
347+
if let UnaryOp::Reference { mutable } = operator {
348+
if mutable {
349+
self.check_can_mutate(rhs, rhs_location);
350+
} else {
351+
self.use_unstable_feature(UnstableFeature::Ownership, location);
352+
}
349353
}
350354

351355
let expr =

compiler/noirc_frontend/src/elaborator/mod.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,7 @@ impl<'context> Elaborator<'context> {
588588
for (mut constraint, expr_id, select_impl) in context.trait_constraints {
589589
let location = self.interner.expr_location(&expr_id);
590590

591-
if matches!(&constraint.typ, Type::MutableReference(_)) {
591+
if matches!(&constraint.typ, Type::Reference(..)) {
592592
let (_, dereferenced_typ) =
593593
self.insert_auto_dereferences(expr_id, constraint.typ.clone());
594594
constraint.typ = dereferenced_typ;
@@ -1078,7 +1078,7 @@ impl<'context> Elaborator<'context> {
10781078
self.mark_type_as_used(from);
10791079
self.mark_type_as_used(to);
10801080
}
1081-
Type::MutableReference(typ) => {
1081+
Type::Reference(typ, _) => {
10821082
self.mark_type_as_used(typ);
10831083
}
10841084
Type::InfixExpr(left, _op, right, _) => {
@@ -1461,8 +1461,8 @@ impl<'context> Elaborator<'context> {
14611461
self.self_type = Some(self_type.clone());
14621462
let self_type_location = trait_impl.object_type.location;
14631463

1464-
if matches!(self_type, Type::MutableReference(_)) {
1465-
self.push_err(DefCollectorErrorKind::MutableReferenceInTraitImpl {
1464+
if matches!(self_type, Type::Reference(..)) {
1465+
self.push_err(DefCollectorErrorKind::ReferenceInTraitImpl {
14661466
location: self_type_location,
14671467
});
14681468
}
@@ -1755,7 +1755,7 @@ impl<'context> Elaborator<'context> {
17551755
);
17561756
self.check_type_is_not_more_private_then_item(name, visibility, env, location);
17571757
}
1758-
Type::MutableReference(typ) | Type::Array(_, typ) | Type::Slice(typ) => {
1758+
Type::Reference(typ, _) | Type::Array(_, typ) | Type::Slice(typ) => {
17591759
self.check_type_is_not_more_private_then_item(name, visibility, typ, location);
17601760
}
17611761
Type::InfixExpr(left, _op, right, _) => {

compiler/noirc_frontend/src/elaborator/options.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ use std::str::FromStr;
33
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
44
pub enum UnstableFeature {
55
Enums,
6-
ArrayOwnership,
6+
Ownership,
77
}
88

99
impl std::fmt::Display for UnstableFeature {
1010
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1111
match self {
1212
Self::Enums => write!(f, "enums"),
13-
Self::ArrayOwnership => write!(f, "array-ownership"),
13+
Self::Ownership => write!(f, "ownership"),
1414
}
1515
}
1616
}
@@ -21,7 +21,7 @@ impl FromStr for UnstableFeature {
2121
fn from_str(s: &str) -> Result<Self, Self::Err> {
2222
match s {
2323
"enums" => Ok(Self::Enums),
24-
"array-ownership" => Ok(Self::ArrayOwnership),
24+
"ownership" => Ok(Self::Ownership),
2525
other => Err(format!("Unknown unstable feature '{other}'")),
2626
}
2727
}

compiler/noirc_frontend/src/elaborator/statements.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -437,8 +437,8 @@ impl Elaborator<'_> {
437437
let (mut lvalue, mut lvalue_type, mut mutable) = self.elaborate_lvalue(*array);
438438

439439
// Before we check that the lvalue is an array, try to dereference it as many times
440-
// as needed to unwrap any &mut wrappers.
441-
while let Type::MutableReference(element) = lvalue_type.follow_bindings() {
440+
// as needed to unwrap any `&` or `&mut` wrappers.
441+
while let Type::Reference(element, _) = lvalue_type.follow_bindings() {
442442
let element_type = element.as_ref().clone();
443443
lvalue =
444444
HirLValue::Dereference { lvalue: Box::new(lvalue), element_type, location };
@@ -482,7 +482,9 @@ impl Elaborator<'_> {
482482
let lvalue = Box::new(lvalue);
483483

484484
let element_type = Type::type_variable(self.interner.next_type_variable_id());
485-
let expected_type = Type::MutableReference(Box::new(element_type.clone()));
485+
486+
// Always expect a mutable reference here since we're storing to it
487+
let expected_type = Type::Reference(Box::new(element_type.clone()), true);
486488

487489
self.unify(&reference_type, &expected_type, || TypeCheckError::TypeMismatch {
488490
expected_typ: expected_type.to_string(),
@@ -539,9 +541,8 @@ impl Elaborator<'_> {
539541
}
540542
}
541543
}
542-
// If the lhs is a mutable reference we automatically transform
543-
// lhs.field into (*lhs).field
544-
Type::MutableReference(element) => {
544+
// If the lhs is a reference we automatically transform `lhs.field` into `(*lhs).field`
545+
Type::Reference(element, mutable) => {
545546
if let Some(mut dereference_lhs) = dereference_lhs {
546547
dereference_lhs(self, lhs_type.clone(), element.as_ref().clone());
547548
return self.check_field_access(
@@ -553,7 +554,7 @@ impl Elaborator<'_> {
553554
} else {
554555
let (element, index) =
555556
self.check_field_access(element, field_name, location, dereference_lhs)?;
556-
return Some((Type::MutableReference(Box::new(element)), index));
557+
return Some((Type::Reference(Box::new(element), *mutable), index));
557558
}
558559
}
559560
_ => (),

compiler/noirc_frontend/src/elaborator/types.rs

+31-18
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::{
1212
Signedness, UnaryOp, UnresolvedGeneric, UnresolvedGenerics, UnresolvedType,
1313
UnresolvedTypeData, UnresolvedTypeExpression, WILDCARD_TYPE,
1414
},
15+
elaborator::UnstableFeature,
1516
hir::{
1617
def_collector::dc_crate::CompilationError,
1718
def_map::{ModuleDefId, fully_qualified_module_path},
@@ -141,8 +142,11 @@ impl Elaborator<'_> {
141142
}
142143
}
143144
}
144-
MutableReference(element) => {
145-
Type::MutableReference(Box::new(self.resolve_type_inner(*element, kind)))
145+
Reference(element, mutable) => {
146+
if !mutable {
147+
self.use_unstable_feature(UnstableFeature::Ownership, location);
148+
}
149+
Type::Reference(Box::new(self.resolve_type_inner(*element, kind)), mutable)
146150
}
147151
Parenthesized(typ) => self.resolve_type_inner(*typ, kind),
148152
Resolved(id) => self.interner.get_quoted_type(id).clone(),
@@ -845,7 +849,7 @@ impl Elaborator<'_> {
845849
/// Insert as many dereference operations as necessary to automatically dereference a method
846850
/// call object to its base value type T.
847851
pub(super) fn insert_auto_dereferences(&mut self, object: ExprId, typ: Type) -> (ExprId, Type) {
848-
if let Type::MutableReference(element) = typ.follow_bindings() {
852+
if let Type::Reference(element, _mut) = typ.follow_bindings() {
849853
let location = self.interner.id_location(object);
850854

851855
let object = self.interner.push_expr(HirExpression::Prefix(HirPrefixExpression {
@@ -1304,17 +1308,26 @@ impl Elaborator<'_> {
13041308
_ => Ok((rhs_type.clone(), true)),
13051309
}
13061310
}
1307-
crate::ast::UnaryOp::MutableReference => {
1308-
Ok((Type::MutableReference(Box::new(rhs_type.follow_bindings())), false))
1311+
crate::ast::UnaryOp::Reference { mutable } => {
1312+
let typ = Type::Reference(Box::new(rhs_type.follow_bindings()), *mutable);
1313+
Ok((typ, false))
13091314
}
13101315
crate::ast::UnaryOp::Dereference { implicitly_added: _ } => {
13111316
let element_type = self.interner.next_type_variable();
1312-
let expected = Type::MutableReference(Box::new(element_type.clone()));
1313-
self.unify(rhs_type, &expected, || TypeCheckError::TypeMismatch {
1314-
expr_typ: rhs_type.to_string(),
1315-
expected_typ: expected.to_string(),
1316-
expr_location: location,
1317-
});
1317+
let make_expected =
1318+
|mutable| Type::Reference(Box::new(element_type.clone()), mutable);
1319+
1320+
let immutable = make_expected(false);
1321+
let mutable = make_expected(true);
1322+
1323+
// Both `&mut T` and `&T` should coerce to an expected `&T`.
1324+
if !rhs_type.try_reference_coercion(&immutable) {
1325+
self.unify(rhs_type, &mutable, || TypeCheckError::TypeMismatch {
1326+
expr_typ: rhs_type.to_string(),
1327+
expected_typ: mutable.to_string(),
1328+
expr_location: location,
1329+
});
1330+
}
13181331
Ok((element_type, false))
13191332
}
13201333
}
@@ -1437,9 +1450,9 @@ impl Elaborator<'_> {
14371450
Type::NamedGeneric(_, _) => {
14381451
self.lookup_method_in_trait_constraints(object_type, method_name, location)
14391452
}
1440-
// Mutable references to another type should resolve to methods of their element type.
1453+
// References to another type should resolve to methods of their element type.
14411454
// This may be a data type or a primitive type.
1442-
Type::MutableReference(element) => {
1455+
Type::Reference(element, _mutable) => {
14431456
self.lookup_method(&element, method_name, location, check_self_param)
14441457
}
14451458

@@ -1836,12 +1849,12 @@ impl Elaborator<'_> {
18361849
if let Some(expected_object_type) = expected_object_type {
18371850
let actual_type = object_type.follow_bindings();
18381851

1839-
if matches!(expected_object_type.follow_bindings(), Type::MutableReference(_)) {
1840-
if !matches!(actual_type, Type::MutableReference(_)) {
1852+
if let Type::Reference(_, mutable) = expected_object_type.follow_bindings() {
1853+
if !matches!(actual_type, Type::Reference(..)) {
18411854
let location = self.interner.id_location(*object);
18421855
self.check_can_mutate(*object, location);
18431856

1844-
let new_type = Type::MutableReference(Box::new(actual_type));
1857+
let new_type = Type::Reference(Box::new(actual_type), mutable);
18451858
*object_type = new_type.clone();
18461859

18471860
// First try to remove a dereference operator that may have been implicitly
@@ -1852,7 +1865,7 @@ impl Elaborator<'_> {
18521865
*object = new_object.unwrap_or_else(|| {
18531866
let new_object =
18541867
self.interner.push_expr(HirExpression::Prefix(HirPrefixExpression {
1855-
operator: UnaryOp::MutableReference,
1868+
operator: UnaryOp::Reference { mutable },
18561869
rhs: *object,
18571870
trait_method_id: None,
18581871
}));
@@ -1863,7 +1876,7 @@ impl Elaborator<'_> {
18631876
}
18641877
// Otherwise if the object type is a mutable reference and the method is not, insert as
18651878
// many dereferences as needed.
1866-
} else if matches!(actual_type, Type::MutableReference(_)) {
1879+
} else if matches!(actual_type, Type::Reference(..)) {
18671880
let (new_object, new_type) = self.insert_auto_dereferences(*object, actual_type);
18681881
*object_type = new_type;
18691882
*object = new_object;

compiler/noirc_frontend/src/hir/comptime/display.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ impl<'interner> TokenPrettyPrinter<'interner> {
260260
| Token::Slash
261261
| Token::Percent
262262
| Token::Ampersand
263+
| Token::SliceStart
263264
| Token::ShiftLeft
264265
| Token::ShiftRight => {
265266
self.last_was_op = true;
@@ -400,7 +401,13 @@ impl Display for ValuePrinter<'_, '_> {
400401
other => write!(f, "{other}(args)"),
401402
}
402403
}
403-
Value::Pointer(value, _) => write!(f, "&mut {}", value.borrow().display(self.interner)),
404+
Value::Pointer(value, _, mutable) => {
405+
if *mutable {
406+
write!(f, "&mut {}", value.borrow().display(self.interner))
407+
} else {
408+
write!(f, "&{}", value.borrow().display(self.interner))
409+
}
410+
}
404411
Value::Array(values, _) => {
405412
let values = vecmap(values, |value| value.display(self.interner).to_string());
406413
write!(f, "[{}]", values.join(", "))
@@ -875,8 +882,9 @@ fn remove_interned_in_unresolved_type_data(
875882
remove_interned_in_generic_type_args(interner, generic_type_args),
876883
)
877884
}
878-
UnresolvedTypeData::MutableReference(typ) => UnresolvedTypeData::MutableReference(
885+
UnresolvedTypeData::Reference(typ, mutable) => UnresolvedTypeData::Reference(
879886
Box::new(remove_interned_in_unresolved_type(interner, *typ)),
887+
mutable,
880888
),
881889
UnresolvedTypeData::Tuple(types) => UnresolvedTypeData::Tuple(vecmap(types, |typ| {
882890
remove_interned_in_unresolved_type(interner, typ)

compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -465,9 +465,9 @@ impl Type {
465465
let env = Box::new(env.to_display_ast());
466466
UnresolvedTypeData::Function(args, ret, env, *unconstrained)
467467
}
468-
Type::MutableReference(element) => {
468+
Type::Reference(element, mutable) => {
469469
let element = Box::new(element.to_display_ast());
470-
UnresolvedTypeData::MutableReference(element)
470+
UnresolvedTypeData::Reference(element, *mutable)
471471
}
472472
// Type::Forall is only for generic functions which don't store a type
473473
// in their Ast so they don't need to call to_display_ast for their Forall type.

0 commit comments

Comments
 (0)