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: let has_named_attribute work for built-in attributes #6024

Merged
merged 4 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
36 changes: 15 additions & 21 deletions compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ fn struct_def_eq(arguments: Vec<(Value, Location)>, location: Location) -> IResu
eq_item(arguments, location, get_struct)
}

// fn has_named_attribute(self, name: Quoted) -> bool
// fn has_named_attribute<let N: u32>(self, name: str<N>) -> bool {}
fn struct_def_has_named_attribute(
interner: &NodeInterner,
arguments: Vec<(Value, Location)>,
Expand All @@ -450,14 +450,9 @@ fn struct_def_has_named_attribute(
let (self_argument, name) = check_two_arguments(arguments, location)?;
let struct_id = get_struct(self_argument)?;

let name = get_quoted(name)?;
let name = name.iter().map(|token| token.to_string()).collect::<Vec<_>>().join("");
let name = get_str(interner, name)?;

let attributes = interner.struct_attributes(&struct_id);
let attributes = attributes.iter().filter_map(|attribute| attribute.as_custom());
let attributes = attributes.map(|attribute| &attribute.contents);

Ok(Value::Bool(has_named_attribute(&name, attributes, location)))
Ok(Value::Bool(has_named_attribute(&name, interner.struct_attributes(&struct_id))))
}

/// fn fields(self) -> [(Quoted, Type)]
Expand Down Expand Up @@ -1957,23 +1952,25 @@ fn function_def_body(
}
}

// fn has_named_attribute(self, name: Quoted) -> bool
// fn has_named_attribute<let N: u32>(self, name: str<N>) -> bool {}
fn function_def_has_named_attribute(
interner: &NodeInterner,
arguments: Vec<(Value, Location)>,
location: Location,
) -> IResult<Value> {
let (self_argument, name) = check_two_arguments(arguments, location)?;
let func_id = get_function_def(self_argument)?;
let func_meta = interner.function_meta(&func_id);

let name = get_quoted(name)?;
let name = name.iter().map(|token| token.to_string()).collect::<Vec<_>>().join("");
let name = &*get_str(interner, name)?;

let attributes = &func_meta.custom_attributes;
let attributes = attributes.iter().map(|attribute| &attribute.contents);
let modifiers = interner.function_modifiers(&func_id);
if let Some(attribute) = &modifiers.attributes.function {
if name == attribute.name() {
return Ok(Value::Bool(true));
}
}

Ok(Value::Bool(has_named_attribute(&name, attributes, location)))
Ok(Value::Bool(has_named_attribute(name, &modifiers.attributes.secondary)))
}

fn function_def_hash(arguments: Vec<(Value, Location)>, location: Location) -> IResult<Value> {
Expand Down Expand Up @@ -2306,7 +2303,7 @@ fn module_structs(
Ok(Value::Slice(struct_ids, slice_type))
}

// fn has_named_attribute(self, name: Quoted) -> bool
// fn has_named_attribute<let N: u32>(self, name: str<N>) -> bool {}
fn module_has_named_attribute(
interpreter: &Interpreter,
arguments: Vec<(Value, Location)>,
Expand All @@ -2316,12 +2313,9 @@ fn module_has_named_attribute(
let module_id = get_module(self_argument)?;
let module_data = interpreter.elaborator.get_module(module_id);

let name = get_quoted(name)?;
let name = name.iter().map(|token| token.to_string()).collect::<Vec<_>>().join("");

let attributes = module_data.outer_attributes.iter().chain(&module_data.inner_attributes);
let name = get_str(interpreter.elaborator.interner, name)?;

Ok(Value::Bool(has_named_attribute(&name, attributes, location)))
Ok(Value::Bool(has_named_attribute(&name, &module_data.attributes)))
}

// fn is_contract(self) -> bool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use crate::{
BlockExpression, ExpressionKind, IntegerBitSize, LValue, Pattern, Signedness,
StatementKind, UnresolvedTypeData,
},
elaborator::Elaborator,
hir::{
comptime::{
errors::IResult,
Expand All @@ -26,7 +25,7 @@ use crate::{
macros_api::{NodeInterner, StructId},
node_interner::{FuncId, TraitId, TraitImplId},
parser::NoirParser,
token::{Token, Tokens},
token::{SecondaryAttribute, Token, Tokens},
QuotedType, Type,
};

Expand Down Expand Up @@ -450,23 +449,12 @@ pub(super) fn block_expression_to_value(block_expr: BlockExpression) -> Value {
Value::Slice(statements, typ)
}

pub(super) fn has_named_attribute<'a>(
name: &'a str,
attributes: impl Iterator<Item = &'a String>,
location: Location,
) -> bool {
pub(super) fn has_named_attribute(name: &str, attributes: &[SecondaryAttribute]) -> bool {
for attribute in attributes {
let parse_result = Elaborator::parse_attribute(attribute, location);
let Ok(Some((function, _arguments))) = parse_result else {
continue;
};

let ExpressionKind::Variable(path) = function.kind else {
continue;
};

if path.last_name() == name {
return true;
if let Some(attribute_name) = attribute.name() {
if name == attribute_name {
return true;
}
}
}

Expand Down
13 changes: 4 additions & 9 deletions compiler/noirc_frontend/src/hir/def_map/module_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ pub struct ModuleData {
/// True if this module is a `contract Foo { ... }` module containing contract functions
pub is_contract: bool,

pub outer_attributes: Vec<String>,
pub inner_attributes: Vec<String>,
pub attributes: Vec<SecondaryAttribute>,
}

impl ModuleData {
Expand All @@ -38,11 +37,8 @@ impl ModuleData {
inner_attributes: Vec<SecondaryAttribute>,
is_contract: bool,
) -> ModuleData {
let outer_attributes = outer_attributes.iter().filter_map(|attr| attr.as_custom());
let outer_attributes = outer_attributes.map(|attr| attr.contents.to_string()).collect();

let inner_attributes = inner_attributes.iter().filter_map(|attr| attr.as_custom());
let inner_attributes = inner_attributes.map(|attr| attr.contents.to_string()).collect();
let mut attributes = outer_attributes;
attributes.extend(inner_attributes);

ModuleData {
parent,
Expand All @@ -51,8 +47,7 @@ impl ModuleData {
definitions: ItemScope::default(),
location,
is_contract,
outer_attributes,
inner_attributes,
attributes,
}
}

Expand Down
58 changes: 49 additions & 9 deletions compiler/noirc_frontend/src/lexer/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
},
};

use super::Lexer;

/// Represents a token in noir's grammar - a word, number,
/// or symbol that can be used in noir's syntax. This is the
/// smallest unit of grammar. A parser may (will) decide to parse
Expand Down Expand Up @@ -799,7 +801,7 @@
["varargs"] => Attribute::Secondary(SecondaryAttribute::Varargs),
tokens => {
tokens.iter().try_for_each(|token| validate(token))?;
Attribute::Secondary(SecondaryAttribute::Custom(CustomAtrribute {

Check warning on line 804 in compiler/noirc_frontend/src/lexer/token.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (Atrribute)
contents: word.to_owned(),
span,
contents_span,
Expand Down Expand Up @@ -868,6 +870,18 @@
pub fn is_no_predicates(&self) -> bool {
matches!(self, FunctionAttribute::NoPredicates)
}

pub fn name(&self) -> &'static str {
match self {
FunctionAttribute::Foreign(_) => "foreign",
FunctionAttribute::Builtin(_) => "builtin",
FunctionAttribute::Oracle(_) => "oracle",
FunctionAttribute::Test(_) => "test",
FunctionAttribute::Recursive => "recursive",
FunctionAttribute::Fold => "fold",
FunctionAttribute::NoPredicates => "no_predicates",
}
}
}

impl fmt::Display for FunctionAttribute {
Expand Down Expand Up @@ -896,30 +910,35 @@
ContractLibraryMethod,
Export,
Field(String),
Custom(CustomAtrribute),

Check warning on line 913 in compiler/noirc_frontend/src/lexer/token.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (Atrribute)
Abi(String),

/// A variable-argument comptime function.
Varargs,
}

#[derive(PartialEq, Eq, Hash, Debug, Clone, PartialOrd, Ord)]
pub struct CustomAtrribute {
pub contents: String,
// The span of the entire attribute, including leading `#[` and trailing `]`
pub span: Span,
// The span for the attribute contents (what's inside `#[...]`)
pub contents_span: Span,
}

impl SecondaryAttribute {
pub(crate) fn as_custom(&self) -> Option<&CustomAtrribute> {

Check warning on line 921 in compiler/noirc_frontend/src/lexer/token.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (Atrribute)
if let Self::Custom(attribute) = self {
Some(attribute)
} else {
None
}
}

pub(crate) fn name(&self) -> Option<String> {
match self {
SecondaryAttribute::Deprecated(_) => Some("deprecated".to_string()),
SecondaryAttribute::ContractLibraryMethod => {
Some("contract_library_method".to_string())
}
SecondaryAttribute::Export => Some("export".to_string()),
SecondaryAttribute::Field(_) => Some("field".to_string()),
SecondaryAttribute::Custom(custom) => custom.name(),
SecondaryAttribute::Abi(_) => Some("abi".to_string()),
SecondaryAttribute::Varargs => Some("varargs".to_string()),
}
}
}

impl fmt::Display for SecondaryAttribute {
Expand All @@ -939,6 +958,27 @@
}
}

#[derive(PartialEq, Eq, Hash, Debug, Clone, PartialOrd, Ord)]
pub struct CustomAtrribute {

Check warning on line 962 in compiler/noirc_frontend/src/lexer/token.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (Atrribute)
pub contents: String,
// The span of the entire attribute, including leading `#[` and trailing `]`
pub span: Span,
// The span for the attribute contents (what's inside `#[...]`)
pub contents_span: Span,
}

impl CustomAtrribute {

Check warning on line 970 in compiler/noirc_frontend/src/lexer/token.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (Atrribute)
fn name(&self) -> Option<String> {
let mut lexer = Lexer::new(&self.contents);
let token = lexer.next()?.ok()?;
if let Token::Ident(ident) = token.into_token() {
Some(ident)
} else {
None
}
}
}

impl AsRef<str> for FunctionAttribute {
fn as_ref(&self) -> &str {
match self {
Expand Down
2 changes: 1 addition & 1 deletion noir_stdlib/src/meta/function_def.nr
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ impl FunctionDefinition {

#[builtin(function_def_has_named_attribute)]
// docs:start:has_named_attribute
comptime fn has_named_attribute(self, name: Quoted) -> bool {}
comptime fn has_named_attribute<let N: u32>(self, name: str<N>) -> bool {}
// docs:end:has_named_attribute

#[builtin(function_def_is_unconstrained)]
Expand Down
2 changes: 1 addition & 1 deletion noir_stdlib/src/meta/module.nr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ impl Module {

#[builtin(module_has_named_attribute)]
// docs:start:has_named_attribute
comptime fn has_named_attribute(self, name: Quoted) -> bool {}
comptime fn has_named_attribute<let N: u32>(self, name: str<N>) -> bool {}
// docs:end:has_named_attribute

#[builtin(module_is_contract)]
Expand Down
2 changes: 1 addition & 1 deletion noir_stdlib/src/meta/struct_def.nr
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ impl StructDefinition {

#[builtin(struct_def_has_named_attribute)]
// docs:start:has_named_attribute
comptime fn has_named_attribute(self, name: Quoted) -> bool {}
comptime fn has_named_attribute<let N: u32>(self, name: str<N>) -> bool {}
// docs:end:has_named_attribute

/// Return each generic on this struct.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@ fn main() {}

// Check that add_attribute and has_named_attribute work well

#[abi(something)]
#[add_attribute]
struct Foo {

}

comptime fn add_attribute(s: StructDefinition) {
assert(!s.has_named_attribute(quote { foo }));
assert(!s.has_named_attribute("foo"));
s.add_attribute("foo");
assert(s.has_named_attribute(quote { foo }));
assert(s.has_named_attribute("foo"));

assert(s.has_named_attribute("abi"));
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ pub fn foo(w: i32, y: Field, Foo { x, field: some_field }: Foo, mut a: bool, (b,
1
}

#[test]
#[deprecated]
#[check_named_attribute]
fn some_test() {}

comptime fn function_attr(f: FunctionDefinition) {
// Check FunctionDefinition::parameters
let parameters = f.parameters();
Expand All @@ -33,7 +38,12 @@ comptime fn function_attr(f: FunctionDefinition) {
// Check FunctionDefinition::name
assert_eq(f.name(), quote { foo });

assert(f.has_named_attribute(quote { function_attr }));
assert(f.has_named_attribute("function_attr"));
}

comptime fn check_named_attribute(f: FunctionDefinition) {
assert(f.has_named_attribute("test"));
assert(f.has_named_attribute("deprecated"));
}

#[mutate_add_one]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@ fn main() {
assert_eq(bar.name(), quote { bar });

// Check Module::has_named_attribute
assert(foo.has_named_attribute(quote { some_attribute }));
assert(foo.has_named_attribute(quote { outer_attribute }));
assert(!bar.has_named_attribute(quote { some_attribute }));
assert(another_module.has_named_attribute(quote { some_attribute }));
assert(foo.has_named_attribute("some_attribute"));
assert(foo.has_named_attribute("outer_attribute"));
assert(!bar.has_named_attribute("some_attribute"));
assert(another_module.has_named_attribute("some_attribute"));
}

assert_eq(counter, 4);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ comptime fn mapping_function(expr: Expr, f: FunctionDefinition) -> Option<Expr>
let (name, arguments) = func_call;
name.resolve(Option::some(f)).as_function_definition().and_then(
|function_definition: FunctionDefinition| {
if function_definition.has_named_attribute(quote { inject_context }) {
if function_definition.has_named_attribute("inject_context") {
let arguments = arguments.push_front(quote { _context }.as_expr().unwrap());
let arguments = arguments.map(|arg: Expr| arg.quoted()).join(quote { , });
Option::some(quote { $name($arguments) }.as_expr().unwrap())
Expand Down
Loading