Skip to content

Commit

Permalink
feat: add support for attributes on structs (#2733)
Browse files Browse the repository at this point in the history
Co-authored-by: kevaundray <kevtheappdev@gmail.com>
  • Loading branch information
Maddiaa0 and kevaundray authored Sep 20, 2023
1 parent 87fea4b commit 7b3df8e
Show file tree
Hide file tree
Showing 14 changed files with 143 additions and 69 deletions.
20 changes: 10 additions & 10 deletions compiler/noirc_frontend/src/ast/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::fmt::Display;
use noirc_errors::Span;

use crate::{
token::{Attributes, PrimaryAttribute, SecondaryAttribute},
token::{Attributes, FunctionAttribute, SecondaryAttribute},
FunctionReturnType, Ident, Pattern, Visibility,
};

Expand Down Expand Up @@ -65,8 +65,8 @@ impl NoirFunction {
pub fn attributes(&self) -> &Attributes {
&self.def.attributes
}
pub fn primary_attribute(&self) -> Option<&PrimaryAttribute> {
self.def.attributes.primary.as_ref()
pub fn function_attribute(&self) -> Option<&FunctionAttribute> {
self.def.attributes.function.as_ref()
}
pub fn secondary_attributes(&self) -> &Vec<SecondaryAttribute> {
self.def.attributes.secondary.as_ref()
Expand All @@ -89,19 +89,19 @@ impl NoirFunction {
FunctionKind::LowLevel => {}
_ => return None,
}
assert!(self.primary_attribute().unwrap().is_foreign());
assert!(self.function_attribute().unwrap().is_foreign());
Some(&self.def)
}
}

impl From<FunctionDefinition> for NoirFunction {
fn from(fd: FunctionDefinition) -> Self {
// The function type is determined by the existence of a primary attribute
let kind = match fd.attributes.primary {
Some(PrimaryAttribute::Builtin(_)) => FunctionKind::Builtin,
Some(PrimaryAttribute::Foreign(_)) => FunctionKind::LowLevel,
Some(PrimaryAttribute::Test { .. }) => FunctionKind::Normal,
Some(PrimaryAttribute::Oracle(_)) => FunctionKind::Oracle,
// The function type is determined by the existence of a function attribute
let kind = match fd.attributes.function {
Some(FunctionAttribute::Builtin(_)) => FunctionKind::Builtin,
Some(FunctionAttribute::Foreign(_)) => FunctionKind::LowLevel,
Some(FunctionAttribute::Test { .. }) => FunctionKind::Normal,
Some(FunctionAttribute::Oracle(_)) => FunctionKind::Oracle,
None => FunctionKind::Normal,
};

Expand Down
6 changes: 4 additions & 2 deletions compiler/noirc_frontend/src/ast/structure.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use std::fmt::Display;

use crate::{Ident, UnresolvedGenerics, UnresolvedType};
use crate::{token::SecondaryAttribute, Ident, UnresolvedGenerics, UnresolvedType};
use iter_extended::vecmap;
use noirc_errors::Span;

/// Ast node for a struct
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NoirStruct {
pub name: Ident,
pub attributes: Vec<SecondaryAttribute>,
pub generics: UnresolvedGenerics,
pub fields: Vec<(Ident, UnresolvedType)>,
pub span: Span,
Expand All @@ -16,11 +17,12 @@ pub struct NoirStruct {
impl NoirStruct {
pub fn new(
name: Ident,
attributes: Vec<SecondaryAttribute>,
generics: Vec<Ident>,
fields: Vec<(Ident, UnresolvedType)>,
span: Span,
) -> NoirStruct {
NoirStruct { name, generics, fields, span }
NoirStruct { name, attributes, generics, fields, span }
}
}

Expand Down
6 changes: 3 additions & 3 deletions compiler/noirc_frontend/src/hir/def_map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::hir::def_collector::dc_crate::DefCollector;
use crate::hir::Context;
use crate::node_interner::{FuncId, NodeInterner};
use crate::parser::{parse_program, ParsedModule};
use crate::token::{PrimaryAttribute, TestScope};
use crate::token::{FunctionAttribute, TestScope};
use arena::{Arena, Index};
use fm::{FileId, FileManager};
use noirc_errors::{FileDiagnostic, Location};
Expand Down Expand Up @@ -140,8 +140,8 @@ impl CrateDefMap {
module.value_definitions().filter_map(|id| {
if let Some(func_id) = id.as_function() {
let func_meta = interner.function_meta(&func_id);
match func_meta.attributes.primary {
Some(PrimaryAttribute::Test(scope)) => {
match func_meta.attributes.function {
Some(FunctionAttribute::Test(scope)) => {
Some(TestFunction::new(func_id, scope, func_meta.name.location))
}
_ => None,
Expand Down
5 changes: 3 additions & 2 deletions compiler/noirc_frontend/src/hir/resolution/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ use crate::hir_def::expr::{
HirIfExpression, HirIndexExpression, HirInfixExpression, HirLambda, HirLiteral,
HirMemberAccess, HirMethodCallExpression, HirPrefixExpression,
};

use crate::token::FunctionAttribute;
use crate::hir_def::traits::{Trait, TraitConstraint};
use crate::token::PrimaryAttribute;
use regex::Regex;
use std::collections::{BTreeMap, HashSet};
use std::rc::Rc;
Expand Down Expand Up @@ -748,7 +749,7 @@ impl<'a> Resolver<'a> {
self.push_err(ResolverError::DistinctNotAllowed { ident: func.name_ident().clone() });
}

if matches!(attributes.primary, Some(PrimaryAttribute::Test { .. }))
if matches!(attributes.function, Some(FunctionAttribute::Test { .. }))
&& !parameters.is_empty()
{
self.push_err(ResolverError::TestFunctionHasParameters {
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/hir_def/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ pub struct FuncMeta {

/// A function's attributes are the `#[...]` items above the function
/// definition.
/// Primary Attributes will alter the function kind, secondary attributes do not
/// Function Attributes will alter the function kind, secondary attributes do not
pub attributes: Attributes,

/// This function's type in its contract.
Expand Down
20 changes: 12 additions & 8 deletions compiler/noirc_frontend/src/lexer/lexer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::token::Attribute;

use super::{
errors::LexerErrorKind,
token::{Attribute, IntType, Keyword, SpannedToken, Token, Tokens},
token::{IntType, Keyword, SpannedToken, Token, Tokens},
};
use acvm::FieldElement;
use noirc_errors::{Position, Span};
Expand Down Expand Up @@ -411,7 +413,7 @@ impl<'a> Iterator for Lexer<'a> {
#[cfg(test)]
mod tests {
use super::*;
use crate::token::{Attribute, PrimaryAttribute, SecondaryAttribute, TestScope};
use crate::token::{FunctionAttribute, SecondaryAttribute, TestScope};
#[test]
fn test_single_double_char() {
let input = "! != + ( ) { } [ ] | , ; : :: < <= > >= & - -> . .. % / * = == << >>";
Expand Down Expand Up @@ -499,9 +501,11 @@ mod tests {
let input = "#[foreign(sha256)]#[foreign(blake2s)]#[builtin(sum)]";

let expected = vec![
Token::Attribute(Attribute::Primary(PrimaryAttribute::Foreign("sha256".to_string()))),
Token::Attribute(Attribute::Primary(PrimaryAttribute::Foreign("blake2s".to_string()))),
Token::Attribute(Attribute::Primary(PrimaryAttribute::Builtin("sum".to_string()))),
Token::Attribute(Attribute::Function(FunctionAttribute::Foreign("sha256".to_string()))),
Token::Attribute(Attribute::Function(FunctionAttribute::Foreign(
"blake2s".to_string(),
))),
Token::Attribute(Attribute::Function(FunctionAttribute::Builtin("sum".to_string()))),
];

let mut lexer = Lexer::new(input);
Expand Down Expand Up @@ -533,7 +537,7 @@ mod tests {
let token = lexer.next().unwrap().unwrap();
assert_eq!(
token.token(),
&Token::Attribute(Attribute::Primary(PrimaryAttribute::Test(TestScope::None)))
&Token::Attribute(Attribute::Function(FunctionAttribute::Test(TestScope::None)))
);
}

Expand All @@ -557,7 +561,7 @@ mod tests {
let token = lexer.next().unwrap().unwrap();
assert_eq!(
token.token(),
&Token::Attribute(Attribute::Primary(PrimaryAttribute::Test(
&Token::Attribute(Attribute::Function(FunctionAttribute::Test(
TestScope::ShouldFailWith { reason: None }
)))
);
Expand All @@ -571,7 +575,7 @@ mod tests {
let token = lexer.next().unwrap().unwrap();
assert_eq!(
token.token(),
&Token::Attribute(Attribute::Primary(PrimaryAttribute::Test(
&Token::Attribute(Attribute::Function(FunctionAttribute::Test(
TestScope::ShouldFailWith { reason: Some("hello".to_owned()) }
)))
);
Expand Down
50 changes: 25 additions & 25 deletions compiler/noirc_frontend/src/lexer/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,14 +364,14 @@ impl fmt::Display for TestScope {
// Calls to functions which have the foreign attribute are executed in the host language
pub struct Attributes {
// Each function can have a single Primary Attribute
pub primary: Option<PrimaryAttribute>,
pub function: Option<FunctionAttribute>,
// Each function can have many Secondary Attributes
pub secondary: Vec<SecondaryAttribute>,
}

impl Attributes {
pub fn empty() -> Self {
Self { primary: None, secondary: Vec::new() }
Self { function: None, secondary: Vec::new() }
}

/// Returns true if one of the secondary attributes is `contract_library_method`
Expand All @@ -398,14 +398,14 @@ impl Attributes {
/// A secondary attribute has no effect and is either consumed by a library or used as a notice for the developer
#[derive(PartialEq, Eq, Hash, Debug, Clone, PartialOrd, Ord)]
pub enum Attribute {
Primary(PrimaryAttribute),
Function(FunctionAttribute),
Secondary(SecondaryAttribute),
}

impl fmt::Display for Attribute {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Attribute::Primary(attribute) => write!(f, "{}", attribute),
Attribute::Function(attribute) => write!(f, "{}", attribute),
Attribute::Secondary(attribute) => write!(f, "{}", attribute),
}
}
Expand Down Expand Up @@ -442,23 +442,23 @@ impl Attribute {
// Primary Attributes
["foreign", name] => {
validate(name)?;
Attribute::Primary(PrimaryAttribute::Foreign(name.to_string()))
Attribute::Function(FunctionAttribute::Foreign(name.to_string()))
}
["builtin", name] => {
validate(name)?;
Attribute::Primary(PrimaryAttribute::Builtin(name.to_string()))
Attribute::Function(FunctionAttribute::Builtin(name.to_string()))
}
["oracle", name] => {
validate(name)?;
Attribute::Primary(PrimaryAttribute::Oracle(name.to_string()))
Attribute::Function(FunctionAttribute::Oracle(name.to_string()))
}
["test"] => Attribute::Primary(PrimaryAttribute::Test(TestScope::None)),
["test"] => Attribute::Function(FunctionAttribute::Test(TestScope::None)),
["test", name] => {
validate(name)?;
let malformed_scope =
LexerErrorKind::MalformedFuncAttribute { span, found: word.to_owned() };
match TestScope::lookup_str(name) {
Some(scope) => Attribute::Primary(PrimaryAttribute::Test(scope)),
Some(scope) => Attribute::Function(FunctionAttribute::Test(scope)),
None => return Err(malformed_scope),
}
}
Expand Down Expand Up @@ -492,44 +492,44 @@ impl Attribute {
/// Primary Attributes are those which a function can only have one of.
/// They change the FunctionKind and thus have direct impact on the IR output
#[derive(PartialEq, Eq, Hash, Debug, Clone, PartialOrd, Ord)]
pub enum PrimaryAttribute {
pub enum FunctionAttribute {
Foreign(String),
Builtin(String),
Oracle(String),
Test(TestScope),
}

impl PrimaryAttribute {
impl FunctionAttribute {
pub fn builtin(self) -> Option<String> {
match self {
PrimaryAttribute::Builtin(name) => Some(name),
FunctionAttribute::Builtin(name) => Some(name),
_ => None,
}
}

pub fn foreign(self) -> Option<String> {
match self {
PrimaryAttribute::Foreign(name) => Some(name),
FunctionAttribute::Foreign(name) => Some(name),
_ => None,
}
}

pub fn is_foreign(&self) -> bool {
matches!(self, PrimaryAttribute::Foreign(_))
matches!(self, FunctionAttribute::Foreign(_))
}

pub fn is_low_level(&self) -> bool {
matches!(self, PrimaryAttribute::Foreign(_) | PrimaryAttribute::Builtin(_))
matches!(self, FunctionAttribute::Foreign(_) | FunctionAttribute::Builtin(_))
}
}

impl fmt::Display for PrimaryAttribute {
impl fmt::Display for FunctionAttribute {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PrimaryAttribute::Test(scope) => write!(f, "#[test{}]", scope),
PrimaryAttribute::Foreign(ref k) => write!(f, "#[foreign({k})]"),
PrimaryAttribute::Builtin(ref k) => write!(f, "#[builtin({k})]"),
PrimaryAttribute::Oracle(ref k) => write!(f, "#[oracle({k})]"),
FunctionAttribute::Test(scope) => write!(f, "#[test{}]", scope),
FunctionAttribute::Foreign(ref k) => write!(f, "#[foreign({k})]"),
FunctionAttribute::Builtin(ref k) => write!(f, "#[builtin({k})]"),
FunctionAttribute::Oracle(ref k) => write!(f, "#[oracle({k})]"),
}
}
}
Expand Down Expand Up @@ -560,13 +560,13 @@ impl fmt::Display for SecondaryAttribute {
}
}

impl AsRef<str> for PrimaryAttribute {
impl AsRef<str> for FunctionAttribute {
fn as_ref(&self) -> &str {
match self {
PrimaryAttribute::Foreign(string) => string,
PrimaryAttribute::Builtin(string) => string,
PrimaryAttribute::Oracle(string) => string,
PrimaryAttribute::Test { .. } => "",
FunctionAttribute::Foreign(string) => string,
FunctionAttribute::Builtin(string) => string,
FunctionAttribute::Oracle(string) => string,
FunctionAttribute::Test { .. } => "",
}
}
}
Expand Down
10 changes: 5 additions & 5 deletions compiler/noirc_frontend/src/monomorphization/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use crate::{
types,
},
node_interner::{self, DefinitionKind, NodeInterner, StmtId},
token::PrimaryAttribute,
token::FunctionAttribute,
ContractFunctionType, FunctionKind, Type, TypeBinding, TypeBindings, TypeVariableKind,
Visibility,
};
Expand Down Expand Up @@ -145,14 +145,14 @@ impl<'interner> Monomorphizer<'interner> {
let meta = self.interner.function_meta(&id);
match meta.kind {
FunctionKind::LowLevel => {
let attribute = meta.attributes.primary.expect("all low level functions must contain a primary attribute which contains the opcode which it links to");
let attribute = meta.attributes.function.expect("all low level functions must contain a function attribute which contains the opcode which it links to");
let opcode = attribute.foreign().expect(
"ice: function marked as foreign, but attribute kind does not match this",
);
Definition::LowLevel(opcode)
}
FunctionKind::Builtin => {
let attribute = meta.attributes.primary.expect("all low level functions must contain a primary attribute which contains the opcode which it links to");
let attribute = meta.attributes.function.expect("all low level functions must contain a primary attribute which contains the opcode which it links to");
let opcode = attribute.builtin().expect(
"ice: function marked as builtin, but attribute kind does not match this",
);
Expand All @@ -165,11 +165,11 @@ impl<'interner> Monomorphizer<'interner> {
FunctionKind::Oracle => {
let attr = meta
.attributes
.primary
.function
.expect("Oracle function must have an oracle attribute");

match attr {
PrimaryAttribute::Oracle(name) => Definition::Oracle(name),
FunctionAttribute::Oracle(name) => Definition::Oracle(name),
_ => unreachable!("Oracle function must have an oracle attribute"),
}
}
Expand Down
6 changes: 4 additions & 2 deletions compiler/noirc_frontend/src/parser/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ pub enum ParserErrorReason {
#[error("Where clauses are allowed only on functions with generic parameters")]
WhereClauseOnNonGenericFunction,
#[error(
"Multiple primary attributes found. Only one primary attribute is allowed per function."
"Multiple primary attributes found. Only one function attribute is allowed per function"
)]
MultiplePrimaryAttributesFound,
MultipleFunctionAttributesFound,
#[error("A function attribute cannot be placed on a struct")]
NoFunctionAttributesAllowedOnStruct,
#[error("Assert statements can only accept string literals")]
AssertMessageNotString,
}
Expand Down
Loading

0 comments on commit 7b3df8e

Please sign in to comment.