Skip to content
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
6 changes: 4 additions & 2 deletions compiler/noirc_frontend/src/ast/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
},
parser::{Item, ItemKind, ParsedSubModule},
signed_field::SignedField,
token::{FmtStrFragment, MetaAttribute, SecondaryAttribute, Tokens},
token::{FmtStrFragment, MetaAttribute, MetaAttributeName, SecondaryAttribute, Tokens},
};

use super::{
Expand Down Expand Up @@ -1465,8 +1465,8 @@
UnresolvedTypeData::FieldElement => {
visitor.visit_field_element_type(self.location.span);
}
UnresolvedTypeData::Integer(signdness, size) => {

Check warning on line 1468 in compiler/noirc_frontend/src/ast/visitor.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (signdness)
visitor.visit_integer_type(*signdness, *size, self.location.span);

Check warning on line 1469 in compiler/noirc_frontend/src/ast/visitor.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (signdness)
}
UnresolvedTypeData::Bool => visitor.visit_bool_type(self.location.span),
UnresolvedTypeData::Unit => visitor.visit_unit_type(self.location.span),
Expand Down Expand Up @@ -1658,7 +1658,9 @@
}

pub fn accept_children(&self, visitor: &mut impl Visitor) {
self.name.accept(visitor);
if let MetaAttributeName::Path(path) = &self.name {
path.accept(visitor);
}
visit_expressions(&self.arguments, visitor);
}
}
Expand Down
10 changes: 7 additions & 3 deletions compiler/noirc_frontend/src/elaborator/comptime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use crate::{
hir_def::expr::{HirExpression, HirIdent},
node_interner::{DefinitionKind, DependencyId, FuncId, NodeInterner, TraitId, TypeId},
parser::{Item, ItemKind},
token::{MetaAttribute, SecondaryAttribute},
token::{MetaAttribute, MetaAttributeName, SecondaryAttribute},
};

use super::{ElaborateReason, Elaborator, FunctionContext, ResolverMeta};
Expand Down Expand Up @@ -185,8 +185,12 @@ impl<'context> Elaborator<'context> {
self.local_module = attribute_context.attribute_module;
let location = attribute.location;

let function =
Expression { kind: ExpressionKind::Variable(attribute.name.clone()), location };
let kind = match &attribute.name {
MetaAttributeName::Path(path) => ExpressionKind::Variable(path.clone()),
MetaAttributeName::Resolved(expr_id) => ExpressionKind::Resolved(*expr_id),
};

let function = Expression { kind, location };
let arguments = attribute.arguments.clone();

// Elaborate the function, rolling back any errors generated in case it is unknown
Expand Down
10 changes: 7 additions & 3 deletions compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,7 @@ fn type_def_has_named_attribute(

let name = get_str(interner, name)?;

Ok(Value::Bool(has_named_attribute(&name, interner.type_attributes(&type_id))))
Ok(Value::Bool(has_named_attribute(&name, interner.type_attributes(&type_id), interner)))
}

/// fn fields(self, generic_args: [Type]) -> [(Quoted, Type)]
Expand Down Expand Up @@ -2505,7 +2505,7 @@ fn function_def_has_named_attribute(
}
}

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

fn function_def_hash(arguments: Vec<(Value, Location)>, location: Location) -> IResult<Value> {
Expand Down Expand Up @@ -2878,7 +2878,11 @@ fn module_has_named_attribute(

let name = get_str(interpreter.elaborator.interner, name)?;

Ok(Value::Bool(has_named_attribute(&name, &module_data.attributes)))
Ok(Value::Bool(has_named_attribute(
&name,
&module_data.attributes,
interpreter.elaborator.interner,
)))
}

// fn is_contract(self) -> bool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ use crate::elaborator::Elaborator;
use crate::hir::comptime::display::tokens_to_string;
use crate::hir::comptime::value::unwrap_rc;
use crate::hir::def_collector::dc_crate::CompilationError;
use crate::hir_def::expr::HirExpression;
use crate::lexer::Lexer;
use crate::parser::{Parser, ParserError};
use crate::token::LocatedToken;
use crate::token::{LocatedToken, MetaAttributeName};
use crate::{DataType, Kind, Shared};
use crate::{
QuotedType, Type,
Expand Down Expand Up @@ -575,9 +576,13 @@ pub(super) fn block_expression_to_value(block_expr: BlockExpression) -> Value {
Value::Slice(statements, typ)
}

pub(super) fn has_named_attribute(name: &str, attributes: &[SecondaryAttribute]) -> bool {
pub(super) fn has_named_attribute(
name: &str,
attributes: &[SecondaryAttribute],
interner: &NodeInterner,
) -> bool {
for attribute in attributes {
if let Some(attribute_name) = attribute.name() {
if let Some(attribute_name) = secondary_attribute_name(attribute, interner) {
if name == attribute_name {
return true;
}
Expand All @@ -587,6 +592,32 @@ pub(super) fn has_named_attribute(name: &str, attributes: &[SecondaryAttribute])
false
}

fn secondary_attribute_name(
attribute: &SecondaryAttribute,
interner: &NodeInterner,
) -> Option<String> {
match attribute {
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::Tag(custom) => custom.name(),
SecondaryAttribute::Meta(meta) => match &meta.name {
MetaAttributeName::Path(path) => Some(path.last_name().to_string()),
MetaAttributeName::Resolved(expr_id) => {
let HirExpression::Ident(ident, _) = interner.expression(expr_id) else {
return None;
};
interner.try_definition(ident.id).map(|def| def.name.clone())
}
},
SecondaryAttribute::Abi(_) => Some("abi".to_string()),
SecondaryAttribute::Varargs => Some("varargs".to_string()),
SecondaryAttribute::UseCallersScope => Some("use_callers_scope".to_string()),
SecondaryAttribute::Allow(_) => Some("allow".to_string()),
}
}

pub(super) fn quote_ident(ident: &Ident, location: Location) -> Value {
Value::Quoted(ident_to_tokens(ident, location))
}
Expand Down
37 changes: 18 additions & 19 deletions compiler/noirc_frontend/src/lexer/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,7 @@
}
}

/// FuzzingScopr is used to specify additional annotations for fuzzing harnesses

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

View workflow job for this annotation

GitHub Actions / Code

Unknown word (Scopr)
#[derive(PartialEq, Eq, Hash, Debug, Clone, PartialOrd, Ord)]
pub enum FuzzingScope {
/// If a fuzzing harness has a scope of OnlyFailWith, then it will only detect an assert
Expand Down Expand Up @@ -1011,23 +1011,6 @@
}

impl SecondaryAttribute {
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::Tag(custom) => custom.name(),
SecondaryAttribute::Meta(meta) => Some(meta.name.last_name().to_string()),
SecondaryAttribute::Abi(_) => Some("abi".to_string()),
SecondaryAttribute::Varargs => Some("varargs".to_string()),
SecondaryAttribute::UseCallersScope => Some("use_callers_scope".to_string()),
SecondaryAttribute::Allow(_) => Some("allow".to_string()),
}
}

pub(crate) fn is_allow_unused_variables(&self) -> bool {
match self {
SecondaryAttribute::Allow(string) => string == "unused_variables",
Expand Down Expand Up @@ -1066,7 +1049,7 @@

#[derive(PartialEq, Eq, Debug, Clone)]
pub struct MetaAttribute {
pub name: Path,
pub name: MetaAttributeName,
pub arguments: Vec<Expression>,
pub location: Location,
}
Expand All @@ -1083,6 +1066,22 @@
}
}

#[derive(PartialEq, Eq, Debug, Clone)]
pub enum MetaAttributeName {
/// For example `foo::bar` in `#[foo::bar(...)]`
Path(Path),
/// For example `$expr` in `#[$expr(...)]` inside a `quote { ... }` expression.
Resolved(ExprId),
}

impl Display for MetaAttributeName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MetaAttributeName::Path(path) => path.fmt(f),
MetaAttributeName::Resolved(_) => write!(f, "(quoted)"),
}
}
}
#[derive(PartialEq, Eq, Hash, Debug, Clone, PartialOrd, Ord)]
pub struct CustomAttribute {
pub contents: String,
Expand All @@ -1093,7 +1092,7 @@
}

impl CustomAttribute {
fn name(&self) -> Option<String> {
pub(crate) fn name(&self) -> Option<String> {
let mut lexer = Lexer::new_with_dummy_file(&self.contents);
let token = lexer.next()?.ok()?;
if let Token::Ident(ident) = token.into_token() { Some(ident) } else { None }
Expand Down
14 changes: 14 additions & 0 deletions compiler/noirc_frontend/src/parser/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use noirc_errors::{Location, Span};
use crate::{
ast::{Ident, ItemVisibility},
lexer::{Lexer, lexer::LocatedTokenResult},
node_interner::ExprId,
token::{FmtStrFragment, IntType, Keyword, LocatedToken, Token, TokenKind, Tokens},
};

Expand Down Expand Up @@ -348,6 +349,19 @@ impl<'a> Parser<'a> {
}
}

fn eat_unquote_marker(&mut self) -> Option<ExprId> {
if let Some(token) = self.eat_kind(TokenKind::UnquoteMarker) {
match token.into_token() {
Token::UnquoteMarker(expr_id) => return Some(expr_id),
_ => {
unreachable!("Expected only `UnquoteMarker` to have `TokenKind::UnquoteMarker`")
}
}
}

None
}

fn eat_attribute_start(&mut self) -> Option<bool> {
if matches!(self.token.token(), Token::AttributeStart { is_inner: false, .. }) {
let token = self.bump();
Expand Down
22 changes: 17 additions & 5 deletions compiler/noirc_frontend/src/parser/parser/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use crate::ast::{Expression, ExpressionKind, Ident, Literal, Path};
use crate::lexer::errors::LexerErrorKind;
use crate::parser::ParserErrorReason;
use crate::parser::labels::ParsingRuleLabel;
use crate::token::{Attribute, FunctionAttribute, FuzzingScope, MetaAttribute, TestScope, Token};
use crate::token::{
Attribute, FunctionAttribute, FuzzingScope, MetaAttribute, MetaAttributeName, TestScope, Token,
};
use crate::token::{CustomAttribute, SecondaryAttribute};

use super::Parser;
Expand Down Expand Up @@ -138,8 +140,9 @@ impl Parser<'_> {
{
// This is a Meta attribute with the syntax `keyword(arg1, arg2, .., argN)`
let path = Path::from_single(self.token.to_string(), self.current_token_location);
let name = MetaAttributeName::Path(path);
self.bump();
self.parse_meta_attribute(path, start_location)
self.parse_meta_attribute(name, start_location)
} else if let Some(path) = self.parse_path_no_turbofish() {
if let Some(ident) = path.as_ident() {
if ident.as_str() == "test" {
Expand All @@ -156,15 +159,24 @@ impl Parser<'_> {
}
} else {
// This is a Meta attribute with the syntax `path(arg1, arg2, .., argN)`
self.parse_meta_attribute(path, start_location)
let name = MetaAttributeName::Path(path);
self.parse_meta_attribute(name, start_location)
}
} else if let Some(expr_id) = self.eat_unquote_marker() {
// This is a Meta attribute with the syntax `$expr(arg1, arg2, .., argN)`
let name = MetaAttributeName::Resolved(expr_id);
self.parse_meta_attribute(name, start_location)
} else {
self.expected_label(ParsingRuleLabel::Path);
self.parse_tag_attribute(start_location)
}
}

fn parse_meta_attribute(&mut self, name: Path, start_location: Location) -> Attribute {
fn parse_meta_attribute(
&mut self,
name: MetaAttributeName,
start_location: Location,
) -> Attribute {
let arguments = self.parse_arguments().unwrap_or_default();
self.skip_until_right_bracket();
Attribute::Secondary(SecondaryAttribute::Meta(MetaAttribute {
Expand Down Expand Up @@ -236,7 +248,7 @@ impl Parser<'_> {
self.parse_no_args_attribute(ident, arguments, attr)
}
_ => Attribute::Secondary(SecondaryAttribute::Meta(MetaAttribute {
name: Path::from_ident(ident.clone()),
name: MetaAttributeName::Path(Path::from_ident(ident.clone())),
arguments,
location: self.location_since(start_location),
})),
Expand Down
9 changes: 1 addition & 8 deletions compiler/noirc_frontend/src/parser/parser/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,14 +403,7 @@

/// ResolvedExpression = unquote_marker
fn parse_resolved_expr(&mut self) -> Option<ExpressionKind> {
if let Some(token) = self.eat_kind(TokenKind::UnquoteMarker) {
match token.into_token() {
Token::UnquoteMarker(expr_id) => return Some(ExpressionKind::Resolved(expr_id)),
_ => unreachable!(""),
}
}

None
Some(ExpressionKind::Resolved(self.eat_unquote_marker()?))
}

/// InternedExpression = interned_expr
Expand Down Expand Up @@ -711,7 +704,7 @@
/// = bool
/// | int
/// | str
/// | rawstr

Check warning on line 707 in compiler/noirc_frontend/src/parser/parser/expression.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (rawstr)
/// | fmtstr
/// | QuoteExpression
/// | ArrayExpression
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "comptime_type"
type = "bin"
authors = [""]
compiler_version = ">=0.31.0"

[dependencies]
22 changes: 22 additions & 0 deletions test_programs/compile_success_empty/comptime_attribute/src/main.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#[add_foo]
mod moo {
pub comptime fn add_fn(f: FunctionDefinition, name: Quoted) -> Quoted {
assert(f.has_named_attribute("add_fn"));
quote {
pub fn $name() {}
}
}
}

comptime fn add_foo(_: Module) -> Quoted {
let func = moo::add_fn;
quote {
#[$func(quote { bar })]
pub fn foo() {}
}
}

fn main() {
moo::foo();
moo::bar();
}
7 changes: 5 additions & 2 deletions tooling/lsp/src/attribute_reference_finder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use noirc_frontend::{
},
node_interner::ReferenceId,
parser::ParsedSubModule,
token::MetaAttribute,
token::{MetaAttribute, MetaAttributeName},
usage_tracker::UsageTracker,
};

Expand Down Expand Up @@ -94,7 +94,10 @@ impl Visitor for AttributeReferenceFinder<'_> {
return false;
}

let path = attribute.name.clone();
let MetaAttributeName::Path(path) = attribute.name.clone() else {
return false;
};

// The path here must resolve to a function and it's a simple path (can't have turbofish)
// so it can (and must) be solved as an import.
let Ok(Some((module_def_id, _, _))) = resolve_import(
Expand Down
12 changes: 8 additions & 4 deletions tooling/lsp/src/requests/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
hir_def::traits::Trait,
node_interner::{FuncId, NodeInterner, ReferenceId, TypeId},
parser::{Item, ItemKind, ParsedSubModule},
token::{MetaAttribute, Token, Tokens},
token::{MetaAttribute, MetaAttributeName, Token, Tokens},
};
use sort_text::underscore_sort_text;

Expand All @@ -47,7 +47,7 @@
use super::{TraitReexport, process_request};

mod auto_import;
mod builtins;

Check warning on line 50 in tooling/lsp/src/requests/completion.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (builtins)
mod completion_items;
mod kinds;
mod sort_text;
Expand Down Expand Up @@ -1830,11 +1830,15 @@
}

fn visit_meta_attribute(&mut self, attribute: &MetaAttribute, target: AttributeTarget) -> bool {
if self.byte_index == attribute.name.location.span.end() as usize {
self.suggest_builtin_attributes(&attribute.name.to_string(), target);
let MetaAttributeName::Path(path) = &attribute.name else {
return true;
};

if self.byte_index == path.location.span.end() as usize {
self.suggest_builtin_attributes(&path.to_string(), target);
}

self.find_in_path(&attribute.name, RequestedItems::OnlyAttributeFunctions(target));
self.find_in_path(path, RequestedItems::OnlyAttributeFunctions(target));

true
}
Expand Down Expand Up @@ -1943,8 +1947,8 @@
///
/// For example:
///
/// // "merk" and "ro" match "merkle" and "root" and are in order

Check warning on line 1950 in tooling/lsp/src/requests/completion.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (merk)
/// name_matches("compute_merkle_root", "merk_ro") == true

Check warning on line 1951 in tooling/lsp/src/requests/completion.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (merk)
///
/// // "ro" matches "root", but "merkle" comes before it, so no match
/// name_matches("compute_merkle_root", "ro_mer") == false
Expand Down
Loading
Loading