From 14475d1764ad9812de0604f31f0acd958fa6d024 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 11 Jul 2024 10:29:48 -0500 Subject: [PATCH 1/5] start parsing attributes --- compiler/noirc_frontend/src/elaborator/mod.rs | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index e1d104c4971..de1de8a598e 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -35,7 +35,7 @@ use crate::{ TypeAliasId, }, parser::TopLevelStatement, - Shared, Type, TypeBindings, TypeVariable, + Shared, Type, TypeBindings, TypeVariable, lexer::Lexer, token::Tokens, }; use crate::{ ast::{TraitBound, UnresolvedGeneric, UnresolvedGenerics}, @@ -1284,18 +1284,23 @@ impl<'context> Elaborator<'context> { span: Span, generated_items: &mut CollectedItems, ) -> Result<(), (CompilationError, FileId)> { + let location = Location::new(span, self.file); + let (function_name, mut arguments) = Self::parse_attribute(&attribute, location) + .unwrap_or((attribute, Vec::new())); + let id = self - .lookup_global(Path::from_single(attribute, span)) + .lookup_global(Path::from_single(function_name, span)) .map_err(|_| (ResolverError::UnknownAnnotation { span }.into(), self.file))?; let definition = self.interner.definition(id); let DefinitionKind::Function(function) = definition.kind else { return Err((ResolverError::NonFunctionInAnnotation { span }.into(), self.file)); }; - let location = Location::new(span, self.file); + let mut interpreter_errors = vec![]; let mut interpreter = self.setup_interpreter(&mut interpreter_errors); - let arguments = vec![(Value::StructDefinition(struct_id), location)]; + + arguments.insert(0, (Value::StructDefinition(struct_id), location)); let value = interpreter .call_function(function, arguments, TypeBindings::new(), location) @@ -1313,6 +1318,30 @@ impl<'context> Elaborator<'context> { Ok(()) } + fn parse_attribute(annotation: &str, location: Location) -> Option<(String, Vec<(Value, Location)>)> { + let (tokens, errors) = Lexer::lex(annotation); + if !errors.is_empty() { + return None; + } + + let mut tokens = tokens.0; + if tokens.len() >= 3 { + // Remove the outer `ident ( )` wrapping the function arguments + let first = tokens.remove(0).into_token(); + let second = tokens.remove(0).into_token(); + let last = tokens.pop().unwrap().into_token(); + + use crate::lexer::token::Token::*; + if let (Ident(name), LeftParen, RightParen) = (first, second, last) { + let args = tokens.split(|token| matches!(token.token(), Comma)); + let args = vecmap(args, |arg| (Value::Code(Rc::new(Tokens(arg.to_vec()))), location)); + return Some((name, args)); + } + } + + None + } + pub fn resolve_struct_fields( &mut self, unresolved: NoirStruct, From 1b0bbf2b71e3e4dce71b0ebee9d60cec8ab92efb Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 11 Jul 2024 12:09:01 -0500 Subject: [PATCH 2/5] Add varargs --- compiler/noirc_frontend/src/elaborator/mod.rs | 48 +++++++++++++++---- .../src/hir/def_collector/dc_crate.rs | 2 +- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index de1de8a598e..7ffce8c2b94 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -26,6 +26,7 @@ use crate::{ traits::TraitConstraint, types::{Generics, Kind, ResolvedGeneric}, }, + lexer::Lexer, macros_api::{ BlockExpression, Ident, NodeInterner, NoirFunction, NoirStruct, Pattern, SecondaryAttribute, StructId, @@ -35,7 +36,8 @@ use crate::{ TypeAliasId, }, parser::TopLevelStatement, - Shared, Type, TypeBindings, TypeVariable, lexer::Lexer, token::Tokens, + token::Tokens, + Shared, Type, TypeBindings, TypeVariable, }; use crate::{ ast::{TraitBound, UnresolvedGeneric, UnresolvedGenerics}, @@ -1285,8 +1287,8 @@ impl<'context> Elaborator<'context> { generated_items: &mut CollectedItems, ) -> Result<(), (CompilationError, FileId)> { let location = Location::new(span, self.file); - let (function_name, mut arguments) = Self::parse_attribute(&attribute, location) - .unwrap_or((attribute, Vec::new())); + let (function_name, mut arguments) = + Self::parse_attribute(&attribute, location).unwrap_or((attribute, Vec::new())); let id = self .lookup_global(Path::from_single(function_name, span)) @@ -1297,11 +1299,12 @@ impl<'context> Elaborator<'context> { return Err((ResolverError::NonFunctionInAnnotation { span }.into(), self.file)); }; + self.handle_varargs_attribute(function, &mut arguments, location); + arguments.insert(0, (Value::StructDefinition(struct_id), location)); + let mut interpreter_errors = vec![]; let mut interpreter = self.setup_interpreter(&mut interpreter_errors); - arguments.insert(0, (Value::StructDefinition(struct_id), location)); - let value = interpreter .call_function(function, arguments, TypeBindings::new(), location) .map_err(|error| error.into_compilation_error_pair())?; @@ -1318,23 +1321,32 @@ impl<'context> Elaborator<'context> { Ok(()) } - fn parse_attribute(annotation: &str, location: Location) -> Option<(String, Vec<(Value, Location)>)> { + /// Parses an attribute in the form of a function call (e.g. `#[foo(a b, c d)]`) into + /// the function and quoted arguments called (e.g. `("foo", vec![(a b, location), (c d, location)])`) + fn parse_attribute( + annotation: &str, + location: Location, + ) -> Option<(String, Vec<(Value, Location)>)> { let (tokens, errors) = Lexer::lex(annotation); if !errors.is_empty() { return None; } let mut tokens = tokens.0; - if tokens.len() >= 3 { + if tokens.len() >= 4 { // Remove the outer `ident ( )` wrapping the function arguments let first = tokens.remove(0).into_token(); let second = tokens.remove(0).into_token(); + + // Last token is always an EndOfInput + let _ = tokens.pop().unwrap().into_token(); let last = tokens.pop().unwrap().into_token(); use crate::lexer::token::Token::*; if let (Ident(name), LeftParen, RightParen) = (first, second, last) { let args = tokens.split(|token| matches!(token.token(), Comma)); - let args = vecmap(args, |arg| (Value::Code(Rc::new(Tokens(arg.to_vec()))), location)); + let args = + vecmap(args, |arg| (Value::Code(Rc::new(Tokens(arg.to_vec()))), location)); return Some((name, args)); } } @@ -1342,6 +1354,26 @@ impl<'context> Elaborator<'context> { None } + /// Checks if the given attribute function is a varargs function. + /// If so, we should pass its arguments in one slice rather than as separate arguments. + fn handle_varargs_attribute( + &mut self, + function: FuncId, + arguments: &mut Vec<(Value, Location)>, + location: Location, + ) { + let meta = self.interner.function_meta(&function); + let parameters = &meta.parameters.0; + + // If the last parameter is a slice, this is a varargs function. + if parameters.last().map_or(false, |(_, typ, _)| matches!(typ, Type::Slice(_))) { + let typ = Type::Slice(Box::new(Type::Quoted(crate::QuotedType::Quoted))); + let slice_elements = arguments.drain(..).map(|(value, _)| value); + let slice = Value::Slice(slice_elements.collect(), typ); + arguments.push((slice, location)); + } + } + pub fn resolve_struct_fields( &mut self, unresolved: NoirStruct, diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index b474ccff0cc..c3e4a0747cc 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -202,7 +202,7 @@ pub enum CompilationError { } impl CompilationError { - fn is_error(&self) -> bool { + pub fn is_error(&self) -> bool { let diagnostic = CustomDiagnostic::from(self); diagnostic.is_error() } From 63a53df82e1044fc1c5ab0c1dfa60ba2ca20afa8 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 11 Jul 2024 12:09:28 -0500 Subject: [PATCH 3/5] Add test program --- .../attribute_args/Nargo.toml | 7 +++++++ .../attribute_args/src/main.nr | 20 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 test_programs/compile_success_empty/attribute_args/Nargo.toml create mode 100644 test_programs/compile_success_empty/attribute_args/src/main.nr diff --git a/test_programs/compile_success_empty/attribute_args/Nargo.toml b/test_programs/compile_success_empty/attribute_args/Nargo.toml new file mode 100644 index 00000000000..8efe5d203d1 --- /dev/null +++ b/test_programs/compile_success_empty/attribute_args/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "attribute_args" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] diff --git a/test_programs/compile_success_empty/attribute_args/src/main.nr b/test_programs/compile_success_empty/attribute_args/src/main.nr new file mode 100644 index 00000000000..430b8ca5b3c --- /dev/null +++ b/test_programs/compile_success_empty/attribute_args/src/main.nr @@ -0,0 +1,20 @@ +#[attr_with_args(a b, c d)] +#[varargs(one, two)] +#[varargs(one, two, three, four)] +struct Foo {} + +comptime fn attr_with_args(s: StructDefinition, a: Quoted, b: Quoted) { + // Ensure all variables are in scope. + // We can't print them since that breaks the test runner. + let _ = s; + let _ = a; + let _ = b; +} + +comptime fn varargs(s: StructDefinition, t: [Quoted]) { + let _ = s; + for _ in t {} + assert(t.len() < 5); +} + +fn main(){} From 2eaedb9a63e860abb95d7f40aad2143a7e983280 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 11 Jul 2024 12:22:25 -0500 Subject: [PATCH 4/5] Format test --- test_programs/compile_success_empty/attribute_args/src/main.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_programs/compile_success_empty/attribute_args/src/main.nr b/test_programs/compile_success_empty/attribute_args/src/main.nr index 430b8ca5b3c..44b9c20460f 100644 --- a/test_programs/compile_success_empty/attribute_args/src/main.nr +++ b/test_programs/compile_success_empty/attribute_args/src/main.nr @@ -17,4 +17,4 @@ comptime fn varargs(s: StructDefinition, t: [Quoted]) { assert(t.len() < 5); } -fn main(){} +fn main() {} From f8803ff03373c4a25e9556d30c2d9924c7c019eb Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 11 Jul 2024 13:51:46 -0500 Subject: [PATCH 5/5] Review feedback --- compiler/noirc_frontend/src/elaborator/mod.rs | 2 +- cspell.json | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 7ffce8c2b94..eeb3d866017 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -1344,7 +1344,7 @@ impl<'context> Elaborator<'context> { use crate::lexer::token::Token::*; if let (Ident(name), LeftParen, RightParen) = (first, second, last) { - let args = tokens.split(|token| matches!(token.token(), Comma)); + let args = tokens.split(|token| *token.token() == Comma); let args = vecmap(args, |arg| (Value::Code(Rc::new(Tokens(arg.to_vec()))), location)); return Some((name, args)); diff --git a/cspell.json b/cspell.json index 2a9bfb4b544..689b72435ef 100644 --- a/cspell.json +++ b/cspell.json @@ -206,6 +206,8 @@ "unoptimized", "urem", "USERPROFILE", + "vararg", + "varargs", "vecmap", "vitkov", "wasi",