Skip to content

Commit

Permalink
feat: let has_named_attribute work for built-in attributes (#6024)
Browse files Browse the repository at this point in the history
# Description

## Problem

Resolves #6009

## Summary

Also made it a bit more efficient.

## Additional Context

## Documentation

Check one:
- [x] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist

- [x] I have tested the changes locally.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
  • Loading branch information
asterite authored Sep 12, 2024
1 parent 114903d commit a09646b
Show file tree
Hide file tree
Showing 11 changed files with 98 additions and 68 deletions.
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 crate::{
},
};

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 @@ -868,6 +870,18 @@ impl FunctionAttribute {
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 @@ -903,15 +917,6 @@ pub enum SecondaryAttribute {
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> {
if let Self::Custom(attribute) = self {
Expand All @@ -920,6 +925,20 @@ impl SecondaryAttribute {
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 @@ impl fmt::Display for SecondaryAttribute {
}
}

#[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 CustomAtrribute {
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

0 comments on commit a09646b

Please sign in to comment.