Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement turbofish operator #3542

Merged
merged 41 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
04ea5d0
Add turbofish operator; add stack overflow crash
jfecher Nov 21, 2023
3a95229
Fix parsing error
jfecher Nov 22, 2023
427cb95
Add test
jfecher Nov 22, 2023
3ab58b8
Add compiler error for incorrect generic count
jfecher Nov 22, 2023
8fc453a
Edit example to have a more problematic case
jfecher Nov 22, 2023
4050fe5
Merge branch 'master' into jf/turbofish
TomAFrench Dec 11, 2023
b9673b2
resolved merge conflicts
vezenovm May 14, 2024
b289b87
fixup remaining fmt stuff
vezenovm May 14, 2024
6127baf
cleanup
vezenovm May 14, 2024
9ebb3b5
cargo fmt
vezenovm May 14, 2024
c652541
working initial turbofish tests
vezenovm May 14, 2024
9660360
cargo fmt
vezenovm May 14, 2024
4cee817
nargo fmt
vezenovm May 15, 2024
e89cde2
clippy
vezenovm May 15, 2024
a2da705
fmt
vezenovm May 15, 2024
da23029
nargo fmt
vezenovm May 15, 2024
da26bc3
fix nargo fmt for turbofish on method calls
vezenovm May 15, 2024
77dfc19
clippy
vezenovm May 15, 2024
c17e834
working turbofish with implicit generics now
vezenovm May 16, 2024
8f2be2e
separate out function and implicit generic counts
vezenovm May 16, 2024
5ad8fb4
cleanup
vezenovm May 16, 2024
e7000d7
Merge branch 'master' into jf/turbofish
vezenovm May 16, 2024
384e26a
chore: add test for specifying types on function with turbofish
TomAFrench May 17, 2024
98c5d89
chore: add test for using turbofish with generic methods
TomAFrench May 17, 2024
32d1714
chore: add turbofish to cspell
TomAFrench May 17, 2024
9ab7351
nargo fmt tests
vezenovm May 17, 2024
b73d263
fix noirc_frontend tests
vezenovm May 17, 2024
a886946
chore: update formatter test outputs
TomAFrench May 20, 2024
4125804
Revert "chore: update formatter test outputs"
TomAFrench May 20, 2024
bc7abf8
Update compiler/noirc_frontend/src/hir_def/expr.rs
vezenovm May 20, 2024
8b1bbc4
have atom_or_right_unary accept a type parser
vezenovm May 20, 2024
cd1f59e
Merge remote-tracking branch 'origin/jf/turbofish' into jf/turbofish
vezenovm May 20, 2024
bd075fa
clippy and fmt
vezenovm May 20, 2024
c6aed8c
fetch implicit generic count from node interner
vezenovm May 20, 2024
9bf4ac2
remove unused method implicit generics map
vezenovm May 20, 2024
22e3cda
add new line to expected formatter tests
vezenovm May 20, 2024
bf07ee8
move where implciit_generic_count is computed
vezenovm May 21, 2024
a9cb6c4
switch to single loop in instantiate_with
vezenovm May 21, 2024
daf151b
rename to turbofish_generics
vezenovm May 21, 2024
0d887de
Merge branch 'master' into jf/turbofish
vezenovm May 21, 2024
a44782b
Merge branch 'master' into jf/turbofish
vezenovm May 21, 2024
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
2 changes: 1 addition & 1 deletion aztec_macros/src/transforms/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ fn add_struct_to_hasher(identifier: &Ident, hasher_name: &str) -> Statement {
fn str_to_bytes(identifier: &Ident) -> (Statement, Ident) {
// let identifier_as_bytes = identifier.as_bytes();
let var = variable_ident(identifier.clone());
let contents = if let ExpressionKind::Variable(p) = &var.kind {
let contents = if let ExpressionKind::Variable(p, _) = &var.kind {
p.segments.first().cloned().unwrap_or_else(|| panic!("No segments")).0.contents
} else {
panic!("Unexpected identifier type")
Expand Down
7 changes: 4 additions & 3 deletions aztec_macros/src/utils/ast_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ pub fn expression(kind: ExpressionKind) -> Expression {
}

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

pub fn variable_ident(identifier: Ident) -> Expression {
expression(ExpressionKind::Variable(path(identifier)))
expression(ExpressionKind::Variable(path(identifier), None))
}

pub fn variable_path(path: Path) -> Expression {
expression(ExpressionKind::Variable(path))
expression(ExpressionKind::Variable(path, None))
}

pub fn method_call(
Expand All @@ -47,6 +47,7 @@ pub fn method_call(
object,
method_name: ident(method_name),
arguments,
generics: None,
})))
}

Expand Down
34 changes: 25 additions & 9 deletions compiler/noirc_frontend/src/ast/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use acvm::FieldElement;
use iter_extended::vecmap;
use noirc_errors::{Span, Spanned};

use super::UnaryRhsMemberAccess;

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ExpressionKind {
Literal(Literal),
Expand All @@ -23,7 +25,9 @@ pub enum ExpressionKind {
Cast(Box<CastExpression>),
Infix(Box<InfixExpression>),
If(Box<IfExpression>),
Variable(Path),
// The optional vec here is the optional list of generics
// provided by the turbofish operator, if used
Variable(Path, Option<Vec<UnresolvedType>>),
Tuple(Vec<Expression>),
Lambda(Box<Lambda>),
Parenthesized(Box<Expression>),
Expand All @@ -39,7 +43,7 @@ pub type UnresolvedGenerics = Vec<Ident>;
impl ExpressionKind {
pub fn into_path(self) -> Option<Path> {
match self {
ExpressionKind::Variable(path) => Some(path),
ExpressionKind::Variable(path, _) => Some(path),
_ => None,
}
}
Expand Down Expand Up @@ -164,16 +168,19 @@ impl Expression {

pub fn member_access_or_method_call(
lhs: Expression,
(rhs, args): (Ident, Option<Vec<Expression>>),
(rhs, args): UnaryRhsMemberAccess,
span: Span,
) -> Expression {
let kind = match args {
None => ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { lhs, rhs })),
Some(arguments) => ExpressionKind::MethodCall(Box::new(MethodCallExpression {
object: lhs,
method_name: rhs,
arguments,
})),
Some((generics, arguments)) => {
ExpressionKind::MethodCall(Box::new(MethodCallExpression {
object: lhs,
method_name: rhs,
generics,
arguments,
}))
}
};
Expression::new(kind, span)
}
Expand Down Expand Up @@ -435,6 +442,8 @@ pub struct CallExpression {
pub struct MethodCallExpression {
pub object: Expression,
pub method_name: Ident,
/// Method calls have an optional list of generics if the turbofish operator was used
pub generics: Option<Vec<UnresolvedType>>,
pub arguments: Vec<Expression>,
}

Expand Down Expand Up @@ -494,7 +503,14 @@ impl Display for ExpressionKind {
Cast(cast) => cast.fmt(f),
Infix(infix) => infix.fmt(f),
If(if_expr) => if_expr.fmt(f),
Variable(path) => path.fmt(f),
Variable(path, generics) => {
if let Some(generics) = generics {
let generics = vecmap(generics, ToString::to_string);
write!(f, "{path}::<{}>", generics.join(", "))
} else {
path.fmt(f)
}
}
Constructor(constructor) => constructor.fmt(f),
MemberAccess(access) => access.fmt(f),
Tuple(elements) => {
Expand Down
6 changes: 5 additions & 1 deletion compiler/noirc_frontend/src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ pub struct UnresolvedType {
pub span: Option<Span>,
}

/// Type wrapper for a member access
pub(crate) type UnaryRhsMemberAccess =
(Ident, Option<(Option<Vec<UnresolvedType>>, Vec<Expression>)>);

/// The precursor to TypeExpression, this is the type that the parser allows
/// to be used in the length position of an array type. Only constants, variables,
/// and numeric binary operators are allowed here.
Expand Down Expand Up @@ -310,7 +314,7 @@ impl UnresolvedTypeExpression {
None => Err(expr),
}
}
ExpressionKind::Variable(path) => Ok(UnresolvedTypeExpression::Variable(path)),
ExpressionKind::Variable(path, _) => Ok(UnresolvedTypeExpression::Variable(path)),
ExpressionKind::Prefix(prefix) if prefix.operator == UnaryOp::Minus => {
let lhs = Box::new(UnresolvedTypeExpression::Constant(0, expr.span));
let rhs = Box::new(UnresolvedTypeExpression::from_expr_helper(prefix.rhs)?);
Expand Down
30 changes: 14 additions & 16 deletions compiler/noirc_frontend/src/ast/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,11 +228,10 @@ impl From<Ident> for Expression {
fn from(i: Ident) -> Expression {
Expression {
span: i.0.span(),
kind: ExpressionKind::Variable(Path {
span: i.span(),
segments: vec![i],
kind: PathKind::Plain,
}),
kind: ExpressionKind::Variable(
Path { span: i.span(), segments: vec![i], kind: PathKind::Plain },
None,
),
}
}
}
Expand Down Expand Up @@ -509,7 +508,7 @@ impl Recoverable for Pattern {
impl LValue {
fn as_expression(&self) -> Expression {
let kind = match self {
LValue::Ident(ident) => ExpressionKind::Variable(Path::from_ident(ident.clone())),
LValue::Ident(ident) => ExpressionKind::Variable(Path::from_ident(ident.clone()), None),
LValue::MemberAccess { object, field_name, span: _ } => {
ExpressionKind::MemberAccess(Box::new(MemberAccessExpression {
lhs: object.as_expression(),
Expand Down Expand Up @@ -599,15 +598,15 @@ impl ForRange {

// array.len()
let segments = vec![array_ident];
let array_ident = ExpressionKind::Variable(Path {
segments,
kind: PathKind::Plain,
span: array_span,
});
let array_ident = ExpressionKind::Variable(
Path { segments, kind: PathKind::Plain, span: array_span },
None,
);

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

// array[i]
let segments = vec![Ident::new(index_name, array_span)];
let index_ident = ExpressionKind::Variable(Path {
segments,
kind: PathKind::Plain,
span: array_span,
});
let index_ident = ExpressionKind::Variable(
Path { segments, kind: PathKind::Plain, span: array_span },
None,
);

let loop_element = ExpressionKind::Index(Box::new(IndexExpression {
collection: Expression::new(array_ident, array_span),
Expand Down
74 changes: 44 additions & 30 deletions compiler/noirc_frontend/src/debug/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,14 @@ impl DebugInstrumenter {
let last_stmt = if has_ret_expr {
ast::Statement {
kind: ast::StatementKind::Expression(ast::Expression {
kind: ast::ExpressionKind::Variable(ast::Path {
segments: vec![ident("__debug_expr", span)],
kind: PathKind::Plain,
span,
}),
kind: ast::ExpressionKind::Variable(
ast::Path {
segments: vec![ident("__debug_expr", span)],
kind: PathKind::Plain,
span,
},
None,
),
span,
}),
span,
Expand Down Expand Up @@ -568,11 +571,14 @@ fn build_assign_var_stmt(var_id: SourceVarId, expr: ast::Expression) -> ast::Sta
let span = expr.span;
let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression {
func: Box::new(ast::Expression {
kind: ast::ExpressionKind::Variable(ast::Path {
segments: vec![ident("__debug_var_assign", span)],
kind: PathKind::Plain,
span,
}),
kind: ast::ExpressionKind::Variable(
ast::Path {
segments: vec![ident("__debug_var_assign", span)],
kind: PathKind::Plain,
span,
},
None,
),
span,
}),
arguments: vec![uint_expr(var_id.0 as u128, span), expr],
Expand All @@ -583,11 +589,14 @@ fn build_assign_var_stmt(var_id: SourceVarId, expr: ast::Expression) -> ast::Sta
fn build_drop_var_stmt(var_id: SourceVarId, span: Span) -> ast::Statement {
let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression {
func: Box::new(ast::Expression {
kind: ast::ExpressionKind::Variable(ast::Path {
segments: vec![ident("__debug_var_drop", span)],
kind: PathKind::Plain,
span,
}),
kind: ast::ExpressionKind::Variable(
ast::Path {
segments: vec![ident("__debug_var_drop", span)],
kind: PathKind::Plain,
span,
},
None,
),
span,
}),
arguments: vec![uint_expr(var_id.0 as u128, span)],
Expand All @@ -607,11 +616,14 @@ fn build_assign_member_stmt(
let span = expr.span;
let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression {
func: Box::new(ast::Expression {
kind: ast::ExpressionKind::Variable(ast::Path {
segments: vec![ident(&format!["__debug_member_assign_{arity}"], span)],
kind: PathKind::Plain,
span,
}),
kind: ast::ExpressionKind::Variable(
ast::Path {
segments: vec![ident(&format!["__debug_member_assign_{arity}"], span)],
kind: PathKind::Plain,
span,
},
None,
),
span,
}),
arguments: [
Expand All @@ -627,11 +639,14 @@ fn build_assign_member_stmt(
fn build_debug_call_stmt(fname: &str, fn_id: DebugFnId, span: Span) -> ast::Statement {
let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression {
func: Box::new(ast::Expression {
kind: ast::ExpressionKind::Variable(ast::Path {
segments: vec![ident(&format!["__debug_fn_{fname}"], span)],
kind: PathKind::Plain,
span,
}),
kind: ast::ExpressionKind::Variable(
ast::Path {
segments: vec![ident(&format!["__debug_fn_{fname}"], span)],
kind: PathKind::Plain,
span,
},
None,
),
span,
}),
arguments: vec![uint_expr(fn_id.0 as u128, span)],
Expand Down Expand Up @@ -693,11 +708,10 @@ fn ident(s: &str, span: Span) -> ast::Ident {

fn id_expr(id: &ast::Ident) -> ast::Expression {
ast::Expression {
kind: ast::ExpressionKind::Variable(Path {
segments: vec![id.clone()],
kind: PathKind::Plain,
span: id.span(),
}),
kind: ast::ExpressionKind::Variable(
Path { segments: vec![id.clone()], kind: PathKind::Plain, span: id.span() },
None,
),
span: id.span(),
}
}
Expand Down
15 changes: 12 additions & 3 deletions compiler/noirc_frontend/src/elaborator/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,12 @@ impl<'context> Elaborator<'context> {
ExpressionKind::Cast(cast) => self.elaborate_cast(*cast, expr.span),
ExpressionKind::Infix(infix) => return self.elaborate_infix(*infix, expr.span),
ExpressionKind::If(if_) => self.elaborate_if(*if_),
ExpressionKind::Variable(variable) => return self.elaborate_variable(variable),
ExpressionKind::Variable(variable, generics) => {
let generics = generics.map(|option_inner| {
option_inner.into_iter().map(|generic| self.resolve_type(generic)).collect()
});
return self.elaborate_variable(variable, generics);
}
ExpressionKind::Tuple(tuple) => self.elaborate_tuple(tuple),
ExpressionKind::Lambda(lambda) => self.elaborate_lambda(*lambda),
ExpressionKind::Parenthesized(expr) => return self.elaborate_expression(*expr),
Expand Down Expand Up @@ -185,7 +190,7 @@ impl<'context> Elaborator<'context> {
let variable = scope_tree.find(ident_name);
if let Some((old_value, _)) = variable {
old_value.num_times_used += 1;
let ident = HirExpression::Ident(old_value.ident.clone());
let ident = HirExpression::Ident(old_value.ident.clone(), None);
let expr_id = self.interner.push_expr(ident);
self.interner.push_expr_location(expr_id, call_expr_span, self.file);
let ident = old_value.ident.clone();
Expand Down Expand Up @@ -314,7 +319,11 @@ impl<'context> Elaborator<'context> {

let location = Location::new(span, self.file);
let method = method_call.method_name;
let method_call = HirMethodCallExpression { method, object, arguments, location };
let generics = method_call.generics.map(|option_inner| {
option_inner.into_iter().map(|generic| self.resolve_type(generic)).collect()
});
let method_call =
HirMethodCallExpression { method, object, arguments, location, generics };

// Desugar the method call into a normal, resolved function call
// so that the backend doesn't need to worry about methods
Expand Down
8 changes: 6 additions & 2 deletions compiler/noirc_frontend/src/elaborator/patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,10 +323,14 @@ impl<'context> Elaborator<'context> {
}
}

pub(super) fn elaborate_variable(&mut self, variable: Path) -> (ExprId, Type) {
pub(super) fn elaborate_variable(
&mut self,
variable: Path,
generics: Option<Vec<Type>>,
) -> (ExprId, Type) {
let span = variable.span;
let expr = self.resolve_variable(variable);
let id = self.interner.push_expr(HirExpression::Ident(expr.clone()));
let id = self.interner.push_expr(HirExpression::Ident(expr.clone(), generics));
self.interner.push_expr_location(id, span, self.file);
let typ = self.type_check_variable(expr, id);
self.interner.push_expr_type(id, typ.clone());
Expand Down
6 changes: 3 additions & 3 deletions compiler/noirc_frontend/src/elaborator/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ impl<'context> Elaborator<'context> {
HirExpression::Literal(HirLiteral::Integer(int, false)) => {
int.try_into_u128().ok_or(Some(ResolverError::IntegerTooLarge { span }))
}
HirExpression::Ident(ident) => {
HirExpression::Ident(ident, _) => {
let definition = self.interner.definition(ident.id);
match definition.kind {
DefinitionKind::Global(global_id) => {
Expand Down Expand Up @@ -1249,7 +1249,7 @@ impl<'context> Elaborator<'context> {
}

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

fn is_unconstrained_call(&self, expr: ExprId) -> bool {
if let HirExpression::Ident(HirIdent { id, .. }) = self.interner.expression(&expr) {
if let HirExpression::Ident(HirIdent { id, .. }, _) = self.interner.expression(&expr) {
if let Some(DefinitionKind::Function(func_id)) =
self.interner.try_definition(id).map(|def| &def.kind)
{
Expand Down
Loading
Loading