Skip to content

Commit

Permalink
feat: add FunctionDefinition::add_attribute (#5944)
Browse files Browse the repository at this point in the history
# Description

## Problem

Part of #5874

## Summary

This is fun 😄 


![fn-add-attribute](https://github.com/user-attachments/assets/3fa5bf97-b7f1-4c85-8c54-4198ee35efeb)

## Additional Context

I'll add similar methods to `StructDefinition` and `Module` in follow-up
PRs.

## Documentation\*

Check one:
- [ ] No documentation needed.
- [x] 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 5, 2024
1 parent 89ac6e0 commit c7479c4
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 2 deletions.
10 changes: 10 additions & 0 deletions compiler/noirc_frontend/src/hir/comptime/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ pub enum InterpreterError {
index: usize,
location: Location,
},
InvalidAttribute {
attribute: String,
location: Location,
},

// These cases are not errors, they are just used to prevent us from running more code
// until the loop can be resumed properly. These cases will never be displayed to users.
Expand Down Expand Up @@ -276,6 +280,7 @@ impl InterpreterError {
| InterpreterError::MultipleMatchingImpls { location, .. }
| InterpreterError::ExpectedIdentForStructField { location, .. }
| InterpreterError::TypeAnnotationsNeededForMethodCall { location } => *location,
InterpreterError::InvalidAttribute { location, .. } => *location,

InterpreterError::FailedToParseMacro { error, file, .. } => {
Location::new(error.span(), *file)
Expand Down Expand Up @@ -579,6 +584,11 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic {
let secondary = format!("`{value}` is not a valid field name for `set_fields`");
CustomDiagnostic::simple_error(msg, secondary, location.span)
}
InterpreterError::InvalidAttribute { attribute, location } => {
let msg = format!("`{attribute}` is not a valid attribute");
let secondary = "Note that this method expects attribute contents, without the leading `#[` or trailing `]`".to_string();
CustomDiagnostic::simple_error(msg, secondary, location.span)
}
}
}
}
56 changes: 54 additions & 2 deletions compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use builtin_helpers::{
get_u32, get_unresolved_type, hir_pattern_to_tokens, mutate_func_meta_type, parse,
replace_func_meta_parameters, replace_func_meta_return_type,
};
use chumsky::{prelude::choice, Parser};
use chumsky::{chain::Chain, prelude::choice, Parser};
use im::Vector;
use iter_extended::{try_vecmap, vecmap};
use noirc_errors::Location;
Expand All @@ -36,7 +36,7 @@ use crate::{
macros_api::{HirExpression, HirLiteral, Ident, ModuleDefId, NodeInterner, Signedness},
node_interner::{DefinitionKind, TraitImplKind},
parser::{self},
token::Token,
token::{Attribute, SecondaryAttribute, Token},
QuotedType, Shared, Type,
};

Expand Down Expand Up @@ -97,6 +97,7 @@ impl<'local, 'context> Interpreter<'local, 'context> {
"expr_resolve" => expr_resolve(self, arguments, location),
"is_unconstrained" => Ok(Value::Bool(true)),
"fmtstr_quoted_contents" => fmtstr_quoted_contents(interner, arguments, location),
"function_def_add_attribute" => function_def_add_attribute(self, arguments, location),
"function_def_body" => function_def_body(interner, arguments, location),
"function_def_has_named_attribute" => {
function_def_has_named_attribute(interner, arguments, location)
Expand Down Expand Up @@ -1666,6 +1667,57 @@ fn fmtstr_quoted_contents(
Ok(Value::Quoted(Rc::new(tokens)))
}

// fn add_attribute<let N: u32>(self, attribute: str<N>)
fn function_def_add_attribute(
interpreter: &mut Interpreter,
arguments: Vec<(Value, Location)>,
location: Location,
) -> IResult<Value> {
let (self_argument, attribute) = check_two_arguments(arguments, location)?;
let attribute_location = attribute.1;
let attribute = get_str(interpreter.elaborator.interner, attribute)?;

let mut tokens = Lexer::lex(&format!("#[{}]", attribute)).0 .0;
if let Some(Token::EOF) = tokens.last().map(|token| token.token()) {
tokens.pop();
}
if tokens.len() != 1 {
return Err(InterpreterError::InvalidAttribute {
attribute: attribute.to_string(),
location: attribute_location,
});
}

let token = tokens[0].token();
let Token::Attribute(attribute) = token else {
return Err(InterpreterError::InvalidAttribute {
attribute: attribute.to_string(),
location: attribute_location,
});
};

let func_id = get_function_def(self_argument)?;
check_function_not_yet_resolved(interpreter, func_id, location)?;

let function_modifiers = interpreter.elaborator.interner.function_modifiers_mut(&func_id);

match attribute {
Attribute::Function(attribute) => {
function_modifiers.attributes.function = Some(attribute.clone());
}
Attribute::Secondary(attribute) => {
function_modifiers.attributes.secondary.push(attribute.clone());
}
}

if let Attribute::Secondary(SecondaryAttribute::Custom(attribute)) = attribute {
let func_meta = interpreter.elaborator.interner.function_meta_mut(&func_id);
func_meta.custom_attributes.push(attribute.clone());
}

Ok(Value::Unit)
}

// fn body(self) -> Expr
fn function_def_body(
interner: &NodeInterner,
Expand Down
8 changes: 8 additions & 0 deletions docs/docs/noir/standard_library/meta/function_def.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ a function definition in the source program.

## Methods

### add_attribute

#include_code add_attribute noir_stdlib/src/meta/function_def.nr rust

Adds an attribute to the function. This is only valid
on functions in the current crate which have not yet been resolved.
This means any functions called at compile-time are invalid targets for this method.

### body

#include_code body noir_stdlib/src/meta/function_def.nr rust
Expand Down
5 changes: 5 additions & 0 deletions noir_stdlib/src/meta/function_def.nr
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
impl FunctionDefinition {
#[builtin(function_def_add_attribute)]
// docs:start:add_attribute
fn add_attribute<let N: u32>(self, attribute: str<N>) {}
// docs:end:add_attribute

#[builtin(function_def_body)]
// docs:start:body
fn body(self) -> Expr {}
Expand Down

0 comments on commit c7479c4

Please sign in to comment.