Skip to content

Commit

Permalink
chore: revamp attributes (#6424)
Browse files Browse the repository at this point in the history
  • Loading branch information
asterite authored Nov 5, 2024
1 parent 2972db2 commit 8b5afec
Show file tree
Hide file tree
Showing 38 changed files with 1,146 additions and 620 deletions.
5 changes: 2 additions & 3 deletions compiler/noirc_driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -456,9 +456,8 @@ fn compile_contract_inner(
.secondary
.iter()
.filter_map(|attr| match attr {
SecondaryAttribute::Tag(attribute) | SecondaryAttribute::Meta(attribute) => {
Some(attribute.contents.clone())
}
SecondaryAttribute::Tag(attribute) => Some(attribute.contents.clone()),
SecondaryAttribute::Meta(attribute) => Some(attribute.to_string()),
_ => None,
})
.collect();
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/ast/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ impl FunctionDefinition {
}

pub fn is_test(&self) -> bool {
if let Some(attribute) = &self.attributes.function {
if let Some(attribute) = self.attributes.function() {
matches!(attribute, FunctionAttribute::Test(..))
} else {
false
Expand Down
4 changes: 2 additions & 2 deletions compiler/noirc_frontend/src/ast/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ impl NoirFunction {
&self.def.attributes
}
pub fn function_attribute(&self) -> Option<&FunctionAttribute> {
self.def.attributes.function.as_ref()
self.def.attributes.function()
}
pub fn secondary_attributes(&self) -> &[SecondaryAttribute] {
self.def.attributes.secondary.as_ref()
Expand Down Expand Up @@ -109,7 +109,7 @@ impl NoirFunction {
impl From<FunctionDefinition> for NoirFunction {
fn from(fd: FunctionDefinition) -> Self {
// The function type is determined by the existence of a function attribute
let kind = match fd.attributes.function {
let kind = match fd.attributes.function() {
Some(FunctionAttribute::Builtin(_)) => FunctionKind::Builtin,
Some(FunctionAttribute::Foreign(_)) => FunctionKind::LowLevel,
Some(FunctionAttribute::Test { .. }) => FunctionKind::Normal,
Expand Down
21 changes: 15 additions & 6 deletions compiler/noirc_frontend/src/ast/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::{
InternedUnresolvedTypeData, QuotedTypeId,
},
parser::{Item, ItemKind, ParsedSubModule},
token::{CustomAttribute, SecondaryAttribute, Tokens},
token::{MetaAttribute, SecondaryAttribute, Tokens},
ParsedModule, QuotedType,
};

Expand Down Expand Up @@ -474,7 +474,9 @@ pub trait Visitor {
true
}

fn visit_custom_attribute(&mut self, _: &CustomAttribute, _target: AttributeTarget) {}
fn visit_meta_attribute(&mut self, _: &MetaAttribute, _target: AttributeTarget) -> bool {
true
}
}

impl ParsedModule {
Expand Down Expand Up @@ -1441,15 +1443,22 @@ impl SecondaryAttribute {
}

pub fn accept_children(&self, target: AttributeTarget, visitor: &mut impl Visitor) {
if let SecondaryAttribute::Meta(custom) = self {
custom.accept(target, visitor);
if let SecondaryAttribute::Meta(meta_attribute) = self {
meta_attribute.accept(target, visitor);
}
}
}

impl CustomAttribute {
impl MetaAttribute {
pub fn accept(&self, target: AttributeTarget, visitor: &mut impl Visitor) {
visitor.visit_custom_attribute(self, target);
if visitor.visit_meta_attribute(self, target) {
self.accept_children(visitor);
}
}

pub fn accept_children(&self, visitor: &mut impl Visitor) {
self.name.accept(visitor);
visit_expressions(&self.arguments, visitor);
}
}

Expand Down
62 changes: 11 additions & 51 deletions compiler/noirc_frontend/src/elaborator/comptime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,9 @@ use crate::{
resolution::errors::ResolverError,
},
hir_def::expr::{HirExpression, HirIdent},
lexer::Lexer,
node_interner::{DefinitionKind, DependencyId, FuncId, NodeInterner, StructId, TraitId},
parser::{Item, ItemKind, Parser},
token::SecondaryAttribute,
parser::{Item, ItemKind},
token::{MetaAttribute, SecondaryAttribute},
Type, TypeBindings, UnificationError,
};

Expand Down Expand Up @@ -162,10 +161,9 @@ impl<'context> Elaborator<'context> {
if let SecondaryAttribute::Meta(attribute) = attribute {
self.elaborate_in_comptime_context(|this| {
if let Err(error) = this.run_comptime_attribute_name_on_item(
&attribute.contents,
attribute,
item.clone(),
span,
attribute.contents_span,
attribute_context,
generated_items,
) {
Expand All @@ -177,27 +175,21 @@ impl<'context> Elaborator<'context> {

fn run_comptime_attribute_name_on_item(
&mut self,
attribute: &str,
attribute: &MetaAttribute,
item: Value,
span: Span,
attribute_span: Span,
attribute_context: AttributeContext,
generated_items: &mut CollectedItems,
) -> Result<(), (CompilationError, FileId)> {
self.file = attribute_context.attribute_file;
self.local_module = attribute_context.attribute_module;

let location = Location::new(attribute_span, self.file);
let Some((function, arguments)) = Self::parse_attribute(attribute, location)? else {
return Err((
ResolverError::UnableToParseAttribute {
attribute: attribute.to_string(),
span: attribute_span,
}
.into(),
self.file,
));
let location = Location::new(attribute.span, self.file);
let function = Expression {
kind: ExpressionKind::Variable(attribute.name.clone()),
span: attribute.span,
};
let arguments = attribute.arguments.clone();

// Elaborate the function, rolling back any errors generated in case it is unknown
let error_count = self.errors.len();
Expand All @@ -211,7 +203,7 @@ impl<'context> Elaborator<'context> {
return Err((
ResolverError::AttributeFunctionIsNotAPath {
function: function_string,
span: attribute_span,
span: attribute.span,
}
.into(),
self.file,
Expand All @@ -223,7 +215,7 @@ impl<'context> Elaborator<'context> {
return Err((
ResolverError::AttributeFunctionNotInScope {
name: function_string,
span: attribute_span,
span: attribute.span,
}
.into(),
self.file,
Expand Down Expand Up @@ -269,38 +261,6 @@ impl<'context> Elaborator<'context> {
Ok(())
}

/// 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)])`)
#[allow(clippy::type_complexity)]
pub(crate) fn parse_attribute(
annotation: &str,
location: Location,
) -> Result<Option<(Expression, Vec<Expression>)>, (CompilationError, FileId)> {
let (tokens, mut lexing_errors) = Lexer::lex(annotation);
if !lexing_errors.is_empty() {
return Err((lexing_errors.swap_remove(0).into(), location.file));
}

let Some(expression) = Parser::for_tokens(tokens).parse_option(Parser::parse_expression)
else {
return Ok(None);
};

let (mut func, mut arguments) = match expression.kind {
ExpressionKind::Call(call) => (*call.func, call.arguments),
ExpressionKind::Variable(_) => (expression, Vec::new()),
_ => return Ok(None),
};

func.span = func.span.shift_by(location.span.start());

for argument in &mut arguments {
argument.span = argument.span.shift_by(location.span.start());
}

Ok(Some((func, arguments)))
}

fn handle_attribute_arguments(
interpreter: &mut Interpreter,
item: &Value,
Expand Down
8 changes: 3 additions & 5 deletions compiler/noirc_frontend/src/elaborator/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ pub(super) fn low_level_function_outside_stdlib(
crate_id: CrateId,
) -> Option<ResolverError> {
let is_low_level_function =
modifiers.attributes.function.as_ref().map_or(false, |func| func.is_low_level());
modifiers.attributes.function().map_or(false, |func| func.is_low_level());
if !crate_id.is_stdlib() && is_low_level_function {
let ident = func_meta_name_ident(func, modifiers);
Some(ResolverError::LowLevelFunctionOutsideOfStdlib { ident })
Expand All @@ -81,8 +81,7 @@ pub(super) fn oracle_not_marked_unconstrained(
func: &FuncMeta,
modifiers: &FunctionModifiers,
) -> Option<ResolverError> {
let is_oracle_function =
modifiers.attributes.function.as_ref().map_or(false, |func| func.is_oracle());
let is_oracle_function = modifiers.attributes.function().map_or(false, |func| func.is_oracle());
if is_oracle_function && !modifiers.is_unconstrained {
let ident = func_meta_name_ident(func, modifiers);
Some(ResolverError::OracleMarkedAsConstrained { ident })
Expand All @@ -105,8 +104,7 @@ pub(super) fn oracle_called_from_constrained_function(
}

let function_attributes = interner.function_attributes(called_func);
let is_oracle_call =
function_attributes.function.as_ref().map_or(false, |func| func.is_oracle());
let is_oracle_call = function_attributes.function().map_or(false, |func| func.is_oracle());
if is_oracle_call {
Some(ResolverError::UnconstrainedOracleReturnToConstrained { span })
} else {
Expand Down
8 changes: 1 addition & 7 deletions compiler/noirc_frontend/src/elaborator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ use crate::{
DefinitionKind, DependencyId, ExprId, FuncId, FunctionModifiers, GlobalId, NodeInterner,
ReferenceId, StructId, TraitId, TraitImplId, TypeAliasId,
},
token::{CustomAttribute, SecondaryAttribute},
token::SecondaryAttribute,
Shared, Type, TypeVariable,
};

Expand Down Expand Up @@ -839,11 +839,6 @@ impl<'context> Elaborator<'context> {
None
};

let attributes = func.secondary_attributes().iter();
let attributes =
attributes.filter_map(|secondary_attribute| secondary_attribute.as_custom());
let attributes: Vec<CustomAttribute> = attributes.cloned().collect();

let meta = FuncMeta {
name: name_ident,
kind: func.kind,
Expand All @@ -867,7 +862,6 @@ impl<'context> Elaborator<'context> {
function_body: FunctionBody::Unresolved(func.kind, body, func.def.span),
self_type: self.self_type.clone(),
source_file: self.file,
custom_attributes: attributes,
};

self.interner.push_fn_meta(meta, func_id);
Expand Down
3 changes: 1 addition & 2 deletions compiler/noirc_frontend/src/hir/comptime/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,7 @@ impl<'interner> TokenPrettyPrinter<'interner> {
| Token::Whitespace(_)
| Token::LineComment(..)
| Token::BlockComment(..)
| Token::Attribute(..)
| Token::InnerAttribute(..)
| Token::AttributeStart { .. }
| Token::Invalid(_) => {
if last_was_alphanumeric {
write!(f, " ")?;
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/hir/comptime/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> {
location: Location,
) -> IResult<Value> {
let attributes = self.elaborator.interner.function_attributes(&function);
let func_attrs = attributes.function.as_ref()
let func_attrs = attributes.function()
.expect("all builtin functions must contain a function attribute which contains the opcode which it links to");

if let Some(builtin) = func_attrs.builtin() {
Expand Down
51 changes: 10 additions & 41 deletions compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use crate::{
hir_def::{self},
node_interner::{DefinitionKind, NodeInterner, TraitImplKind},
parser::{Parser, StatementOrExpressionOrLValue},
token::{Attribute, SecondaryAttribute, Token},
token::{Attribute, Token},
Kind, QuotedType, ResolvedGeneric, Shared, Type, TypeVariable,
};

Expand Down Expand Up @@ -348,24 +348,9 @@ fn struct_def_add_attribute(
let (self_argument, attribute) = check_two_arguments(arguments, location)?;
let attribute_location = attribute.1;
let attribute = get_str(interner, attribute)?;

let mut tokens = lex(&format!("#[{}]", attribute));
if tokens.len() != 1 {
return Err(InterpreterError::InvalidAttribute {
attribute: attribute.to_string(),
location: attribute_location,
});
}

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

let Attribute::Secondary(attribute) = attribute else {
let attribute = format!("#[{}]", attribute);
let mut parser = Parser::for_str(&attribute);
let Some((Attribute::Secondary(attribute), _span)) = parser.parse_attribute() else {
return Err(InterpreterError::InvalidAttribute {
attribute: attribute.to_string(),
location: attribute_location,
Expand All @@ -374,7 +359,7 @@ fn struct_def_add_attribute(

let struct_id = get_struct(self_argument)?;
interner.update_struct_attributes(struct_id, |attributes| {
attributes.push(attribute.clone());
attributes.push(attribute);
});

Ok(Value::Unit)
Expand Down Expand Up @@ -2250,17 +2235,9 @@ fn function_def_add_attribute(
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 = lex(&format!("#[{}]", attribute));
if tokens.len() != 1 {
return Err(InterpreterError::InvalidAttribute {
attribute: attribute.to_string(),
location: attribute_location,
});
}

let token = tokens.remove(0);
let Token::Attribute(attribute) = token else {
let attribute = format!("#[{}]", attribute);
let mut parser = Parser::for_str(&attribute);
let Some((attribute, _span)) = parser.parse_attribute() else {
return Err(InterpreterError::InvalidAttribute {
attribute: attribute.to_string(),
location: attribute_location,
Expand All @@ -2274,21 +2251,13 @@ fn function_def_add_attribute(

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

if let Attribute::Secondary(
SecondaryAttribute::Tag(attribute) | SecondaryAttribute::Meta(attribute),
) = attribute
{
let func_meta = interpreter.elaborator.interner.function_meta_mut(&func_id);
func_meta.custom_attributes.push(attribute);
}

Ok(Value::Unit)
}

Expand Down Expand Up @@ -2320,7 +2289,7 @@ fn function_def_has_named_attribute(
let name = &*get_str(interner, name)?;

let modifiers = interner.function_modifiers(&func_id);
if let Some(attribute) = &modifiers.attributes.function {
if let Some(attribute) = modifiers.attributes.function() {
if name == attribute.name() {
return Ok(Value::Bool(true));
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/hir/def_map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ impl CrateDefMap {
module.value_definitions().filter_map(|id| {
if let Some(func_id) = id.as_function() {
let attributes = interner.function_attributes(&func_id);
match &attributes.function {
match attributes.function() {
Some(FunctionAttribute::Test(scope)) => {
let location = interner.function_meta(&func_id).name.location;
Some(TestFunction::new(func_id, scope.clone(), location))
Expand Down
Loading

0 comments on commit 8b5afec

Please sign in to comment.