Skip to content

Commit 226724e

Browse files
jfecherTomAFrenchvezenovm
authored
feat: Implement turbofish operator (#3542)
# Description ## Problem\* Resolves #3413 ## Summary\* Adds the turbofish operator `::<>` to variables and method calls in Noir. ## Additional Context I'm publishing this as a draft because I'm shelving this work for now after implementing the majority of it. The work that remains to be done is handling the special case of method calls on generic impls. A generic impl will implicitly add the impl generics to each method - which means the generic count on a method itself as seen by the turbofish operator will not match the actual generics on the function internally. We'll likely need to separate out these implicit generics internally. Such that `expected_generic_count = function_generics - impl_generics`. I've added this as a test case to the `generics` test to ensure it works when this is merged. ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [x] **[Exceptional Case]** Documentation to be submitted in a separate PR. - [x] No documentation is in this PR yet since it is still a draft. # PR Checklist\* - [x] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Tom French <[email protected]> Co-authored-by: Maxim Vezenov <[email protected]>
1 parent faf5bd8 commit 226724e

35 files changed

Lines changed: 486 additions & 125 deletions

File tree

aztec_macros/src/transforms/functions.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -680,7 +680,7 @@ fn add_struct_to_hasher(identifier: &Ident, hasher_name: &str) -> Statement {
680680
fn str_to_bytes(identifier: &Ident) -> (Statement, Ident) {
681681
// let identifier_as_bytes = identifier.as_bytes();
682682
let var = variable_ident(identifier.clone());
683-
let contents = if let ExpressionKind::Variable(p) = &var.kind {
683+
let contents = if let ExpressionKind::Variable(p, _) = &var.kind {
684684
p.segments.first().cloned().unwrap_or_else(|| panic!("No segments")).0.contents
685685
} else {
686686
panic!("Unexpected identifier type")

aztec_macros/src/utils/ast_utils.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@ pub fn expression(kind: ExpressionKind) -> Expression {
2727
}
2828

2929
pub fn variable(name: &str) -> Expression {
30-
expression(ExpressionKind::Variable(ident_path(name)))
30+
expression(ExpressionKind::Variable(ident_path(name), None))
3131
}
3232

3333
pub fn variable_ident(identifier: Ident) -> Expression {
34-
expression(ExpressionKind::Variable(path(identifier)))
34+
expression(ExpressionKind::Variable(path(identifier), None))
3535
}
3636

3737
pub fn variable_path(path: Path) -> Expression {
38-
expression(ExpressionKind::Variable(path))
38+
expression(ExpressionKind::Variable(path, None))
3939
}
4040

4141
pub fn method_call(
@@ -47,6 +47,7 @@ pub fn method_call(
4747
object,
4848
method_name: ident(method_name),
4949
arguments,
50+
generics: None,
5051
})))
5152
}
5253

compiler/noirc_frontend/src/ast/expression.rs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use acvm::FieldElement;
1010
use iter_extended::vecmap;
1111
use noirc_errors::{Span, Spanned};
1212

13+
use super::UnaryRhsMemberAccess;
14+
1315
#[derive(Debug, PartialEq, Eq, Clone)]
1416
pub enum ExpressionKind {
1517
Literal(Literal),
@@ -23,7 +25,9 @@ pub enum ExpressionKind {
2325
Cast(Box<CastExpression>),
2426
Infix(Box<InfixExpression>),
2527
If(Box<IfExpression>),
26-
Variable(Path),
28+
// The optional vec here is the optional list of generics
29+
// provided by the turbofish operator, if used
30+
Variable(Path, Option<Vec<UnresolvedType>>),
2731
Tuple(Vec<Expression>),
2832
Lambda(Box<Lambda>),
2933
Parenthesized(Box<Expression>),
@@ -39,7 +43,7 @@ pub type UnresolvedGenerics = Vec<Ident>;
3943
impl ExpressionKind {
4044
pub fn into_path(self) -> Option<Path> {
4145
match self {
42-
ExpressionKind::Variable(path) => Some(path),
46+
ExpressionKind::Variable(path, _) => Some(path),
4347
_ => None,
4448
}
4549
}
@@ -164,16 +168,19 @@ impl Expression {
164168

165169
pub fn member_access_or_method_call(
166170
lhs: Expression,
167-
(rhs, args): (Ident, Option<Vec<Expression>>),
171+
(rhs, args): UnaryRhsMemberAccess,
168172
span: Span,
169173
) -> Expression {
170174
let kind = match args {
171175
None => ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { lhs, rhs })),
172-
Some(arguments) => ExpressionKind::MethodCall(Box::new(MethodCallExpression {
173-
object: lhs,
174-
method_name: rhs,
175-
arguments,
176-
})),
176+
Some((generics, arguments)) => {
177+
ExpressionKind::MethodCall(Box::new(MethodCallExpression {
178+
object: lhs,
179+
method_name: rhs,
180+
generics,
181+
arguments,
182+
}))
183+
}
177184
};
178185
Expression::new(kind, span)
179186
}
@@ -435,6 +442,8 @@ pub struct CallExpression {
435442
pub struct MethodCallExpression {
436443
pub object: Expression,
437444
pub method_name: Ident,
445+
/// Method calls have an optional list of generics if the turbofish operator was used
446+
pub generics: Option<Vec<UnresolvedType>>,
438447
pub arguments: Vec<Expression>,
439448
}
440449

@@ -494,7 +503,14 @@ impl Display for ExpressionKind {
494503
Cast(cast) => cast.fmt(f),
495504
Infix(infix) => infix.fmt(f),
496505
If(if_expr) => if_expr.fmt(f),
497-
Variable(path) => path.fmt(f),
506+
Variable(path, generics) => {
507+
if let Some(generics) = generics {
508+
let generics = vecmap(generics, ToString::to_string);
509+
write!(f, "{path}::<{}>", generics.join(", "))
510+
} else {
511+
path.fmt(f)
512+
}
513+
}
498514
Constructor(constructor) => constructor.fmt(f),
499515
MemberAccess(access) => access.fmt(f),
500516
Tuple(elements) => {

compiler/noirc_frontend/src/ast/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ pub struct UnresolvedType {
132132
pub span: Option<Span>,
133133
}
134134

135+
/// Type wrapper for a member access
136+
pub(crate) type UnaryRhsMemberAccess =
137+
(Ident, Option<(Option<Vec<UnresolvedType>>, Vec<Expression>)>);
138+
135139
/// The precursor to TypeExpression, this is the type that the parser allows
136140
/// to be used in the length position of an array type. Only constants, variables,
137141
/// and numeric binary operators are allowed here.
@@ -310,7 +314,7 @@ impl UnresolvedTypeExpression {
310314
None => Err(expr),
311315
}
312316
}
313-
ExpressionKind::Variable(path) => Ok(UnresolvedTypeExpression::Variable(path)),
317+
ExpressionKind::Variable(path, _) => Ok(UnresolvedTypeExpression::Variable(path)),
314318
ExpressionKind::Prefix(prefix) if prefix.operator == UnaryOp::Minus => {
315319
let lhs = Box::new(UnresolvedTypeExpression::Constant(0, expr.span));
316320
let rhs = Box::new(UnresolvedTypeExpression::from_expr_helper(prefix.rhs)?);

compiler/noirc_frontend/src/ast/statement.rs

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -228,11 +228,10 @@ impl From<Ident> for Expression {
228228
fn from(i: Ident) -> Expression {
229229
Expression {
230230
span: i.0.span(),
231-
kind: ExpressionKind::Variable(Path {
232-
span: i.span(),
233-
segments: vec![i],
234-
kind: PathKind::Plain,
235-
}),
231+
kind: ExpressionKind::Variable(
232+
Path { span: i.span(), segments: vec![i], kind: PathKind::Plain },
233+
None,
234+
),
236235
}
237236
}
238237
}
@@ -509,7 +508,7 @@ impl Recoverable for Pattern {
509508
impl LValue {
510509
fn as_expression(&self) -> Expression {
511510
let kind = match self {
512-
LValue::Ident(ident) => ExpressionKind::Variable(Path::from_ident(ident.clone())),
511+
LValue::Ident(ident) => ExpressionKind::Variable(Path::from_ident(ident.clone()), None),
513512
LValue::MemberAccess { object, field_name, span: _ } => {
514513
ExpressionKind::MemberAccess(Box::new(MemberAccessExpression {
515514
lhs: object.as_expression(),
@@ -599,15 +598,15 @@ impl ForRange {
599598

600599
// array.len()
601600
let segments = vec![array_ident];
602-
let array_ident = ExpressionKind::Variable(Path {
603-
segments,
604-
kind: PathKind::Plain,
605-
span: array_span,
606-
});
601+
let array_ident = ExpressionKind::Variable(
602+
Path { segments, kind: PathKind::Plain, span: array_span },
603+
None,
604+
);
607605

608606
let end_range = ExpressionKind::MethodCall(Box::new(MethodCallExpression {
609607
object: Expression::new(array_ident.clone(), array_span),
610608
method_name: Ident::new("len".to_string(), array_span),
609+
generics: None,
611610
arguments: vec![],
612611
}));
613612
let end_range = Expression::new(end_range, array_span);
@@ -618,11 +617,10 @@ impl ForRange {
618617

619618
// array[i]
620619
let segments = vec![Ident::new(index_name, array_span)];
621-
let index_ident = ExpressionKind::Variable(Path {
622-
segments,
623-
kind: PathKind::Plain,
624-
span: array_span,
625-
});
620+
let index_ident = ExpressionKind::Variable(
621+
Path { segments, kind: PathKind::Plain, span: array_span },
622+
None,
623+
);
626624

627625
let loop_element = ExpressionKind::Index(Box::new(IndexExpression {
628626
collection: Expression::new(array_ident, array_span),

compiler/noirc_frontend/src/debug/mod.rs

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -171,11 +171,14 @@ impl DebugInstrumenter {
171171
let last_stmt = if has_ret_expr {
172172
ast::Statement {
173173
kind: ast::StatementKind::Expression(ast::Expression {
174-
kind: ast::ExpressionKind::Variable(ast::Path {
175-
segments: vec![ident("__debug_expr", span)],
176-
kind: PathKind::Plain,
177-
span,
178-
}),
174+
kind: ast::ExpressionKind::Variable(
175+
ast::Path {
176+
segments: vec![ident("__debug_expr", span)],
177+
kind: PathKind::Plain,
178+
span,
179+
},
180+
None,
181+
),
179182
span,
180183
}),
181184
span,
@@ -568,11 +571,14 @@ fn build_assign_var_stmt(var_id: SourceVarId, expr: ast::Expression) -> ast::Sta
568571
let span = expr.span;
569572
let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression {
570573
func: Box::new(ast::Expression {
571-
kind: ast::ExpressionKind::Variable(ast::Path {
572-
segments: vec![ident("__debug_var_assign", span)],
573-
kind: PathKind::Plain,
574-
span,
575-
}),
574+
kind: ast::ExpressionKind::Variable(
575+
ast::Path {
576+
segments: vec![ident("__debug_var_assign", span)],
577+
kind: PathKind::Plain,
578+
span,
579+
},
580+
None,
581+
),
576582
span,
577583
}),
578584
arguments: vec![uint_expr(var_id.0 as u128, span), expr],
@@ -583,11 +589,14 @@ fn build_assign_var_stmt(var_id: SourceVarId, expr: ast::Expression) -> ast::Sta
583589
fn build_drop_var_stmt(var_id: SourceVarId, span: Span) -> ast::Statement {
584590
let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression {
585591
func: Box::new(ast::Expression {
586-
kind: ast::ExpressionKind::Variable(ast::Path {
587-
segments: vec![ident("__debug_var_drop", span)],
588-
kind: PathKind::Plain,
589-
span,
590-
}),
592+
kind: ast::ExpressionKind::Variable(
593+
ast::Path {
594+
segments: vec![ident("__debug_var_drop", span)],
595+
kind: PathKind::Plain,
596+
span,
597+
},
598+
None,
599+
),
591600
span,
592601
}),
593602
arguments: vec![uint_expr(var_id.0 as u128, span)],
@@ -607,11 +616,14 @@ fn build_assign_member_stmt(
607616
let span = expr.span;
608617
let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression {
609618
func: Box::new(ast::Expression {
610-
kind: ast::ExpressionKind::Variable(ast::Path {
611-
segments: vec![ident(&format!["__debug_member_assign_{arity}"], span)],
612-
kind: PathKind::Plain,
613-
span,
614-
}),
619+
kind: ast::ExpressionKind::Variable(
620+
ast::Path {
621+
segments: vec![ident(&format!["__debug_member_assign_{arity}"], span)],
622+
kind: PathKind::Plain,
623+
span,
624+
},
625+
None,
626+
),
615627
span,
616628
}),
617629
arguments: [
@@ -627,11 +639,14 @@ fn build_assign_member_stmt(
627639
fn build_debug_call_stmt(fname: &str, fn_id: DebugFnId, span: Span) -> ast::Statement {
628640
let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression {
629641
func: Box::new(ast::Expression {
630-
kind: ast::ExpressionKind::Variable(ast::Path {
631-
segments: vec![ident(&format!["__debug_fn_{fname}"], span)],
632-
kind: PathKind::Plain,
633-
span,
634-
}),
642+
kind: ast::ExpressionKind::Variable(
643+
ast::Path {
644+
segments: vec![ident(&format!["__debug_fn_{fname}"], span)],
645+
kind: PathKind::Plain,
646+
span,
647+
},
648+
None,
649+
),
635650
span,
636651
}),
637652
arguments: vec![uint_expr(fn_id.0 as u128, span)],
@@ -693,11 +708,10 @@ fn ident(s: &str, span: Span) -> ast::Ident {
693708

694709
fn id_expr(id: &ast::Ident) -> ast::Expression {
695710
ast::Expression {
696-
kind: ast::ExpressionKind::Variable(Path {
697-
segments: vec![id.clone()],
698-
kind: PathKind::Plain,
699-
span: id.span(),
700-
}),
711+
kind: ast::ExpressionKind::Variable(
712+
Path { segments: vec![id.clone()], kind: PathKind::Plain, span: id.span() },
713+
None,
714+
),
701715
span: id.span(),
702716
}
703717
}

compiler/noirc_frontend/src/elaborator/expressions.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,12 @@ impl<'context> Elaborator<'context> {
4848
ExpressionKind::Cast(cast) => self.elaborate_cast(*cast, expr.span),
4949
ExpressionKind::Infix(infix) => return self.elaborate_infix(*infix, expr.span),
5050
ExpressionKind::If(if_) => self.elaborate_if(*if_),
51-
ExpressionKind::Variable(variable) => return self.elaborate_variable(variable),
51+
ExpressionKind::Variable(variable, generics) => {
52+
let generics = generics.map(|option_inner| {
53+
option_inner.into_iter().map(|generic| self.resolve_type(generic)).collect()
54+
});
55+
return self.elaborate_variable(variable, generics);
56+
}
5257
ExpressionKind::Tuple(tuple) => self.elaborate_tuple(tuple),
5358
ExpressionKind::Lambda(lambda) => self.elaborate_lambda(*lambda),
5459
ExpressionKind::Parenthesized(expr) => return self.elaborate_expression(*expr),
@@ -185,7 +190,7 @@ impl<'context> Elaborator<'context> {
185190
let variable = scope_tree.find(ident_name);
186191
if let Some((old_value, _)) = variable {
187192
old_value.num_times_used += 1;
188-
let ident = HirExpression::Ident(old_value.ident.clone());
193+
let ident = HirExpression::Ident(old_value.ident.clone(), None);
189194
let expr_id = self.interner.push_expr(ident);
190195
self.interner.push_expr_location(expr_id, call_expr_span, self.file);
191196
let ident = old_value.ident.clone();
@@ -314,7 +319,11 @@ impl<'context> Elaborator<'context> {
314319

315320
let location = Location::new(span, self.file);
316321
let method = method_call.method_name;
317-
let method_call = HirMethodCallExpression { method, object, arguments, location };
322+
let generics = method_call.generics.map(|option_inner| {
323+
option_inner.into_iter().map(|generic| self.resolve_type(generic)).collect()
324+
});
325+
let method_call =
326+
HirMethodCallExpression { method, object, arguments, location, generics };
318327

319328
// Desugar the method call into a normal, resolved function call
320329
// so that the backend doesn't need to worry about methods

compiler/noirc_frontend/src/elaborator/patterns.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,10 +323,14 @@ impl<'context> Elaborator<'context> {
323323
}
324324
}
325325

326-
pub(super) fn elaborate_variable(&mut self, variable: Path) -> (ExprId, Type) {
326+
pub(super) fn elaborate_variable(
327+
&mut self,
328+
variable: Path,
329+
generics: Option<Vec<Type>>,
330+
) -> (ExprId, Type) {
327331
let span = variable.span;
328332
let expr = self.resolve_variable(variable);
329-
let id = self.interner.push_expr(HirExpression::Ident(expr.clone()));
333+
let id = self.interner.push_expr(HirExpression::Ident(expr.clone(), generics));
330334
self.interner.push_expr_location(id, span, self.file);
331335
let typ = self.type_check_variable(expr, id);
332336
self.interner.push_expr_type(id, typ.clone());

compiler/noirc_frontend/src/elaborator/types.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,7 @@ impl<'context> Elaborator<'context> {
494494
HirExpression::Literal(HirLiteral::Integer(int, false)) => {
495495
int.try_into_u128().ok_or(Some(ResolverError::IntegerTooLarge { span }))
496496
}
497-
HirExpression::Ident(ident) => {
497+
HirExpression::Ident(ident, _) => {
498498
let definition = self.interner.definition(ident.id);
499499
match definition.kind {
500500
DefinitionKind::Global(global_id) => {
@@ -1249,7 +1249,7 @@ impl<'context> Elaborator<'context> {
12491249
}
12501250

12511251
fn check_if_deprecated(&mut self, expr: ExprId) {
1252-
if let HirExpression::Ident(HirIdent { location, id, impl_kind: _ }) =
1252+
if let HirExpression::Ident(HirIdent { location, id, impl_kind: _ }, _) =
12531253
self.interner.expression(&expr)
12541254
{
12551255
if let Some(DefinitionKind::Function(func_id)) =
@@ -1268,7 +1268,7 @@ impl<'context> Elaborator<'context> {
12681268
}
12691269

12701270
fn is_unconstrained_call(&self, expr: ExprId) -> bool {
1271-
if let HirExpression::Ident(HirIdent { id, .. }) = self.interner.expression(&expr) {
1271+
if let HirExpression::Ident(HirIdent { id, .. }, _) = self.interner.expression(&expr) {
12721272
if let Some(DefinitionKind::Function(func_id)) =
12731273
self.interner.try_definition(id).map(|def| &def.kind)
12741274
{

0 commit comments

Comments
 (0)