Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for attributes on structs #2733

Merged
merged 8 commits into from
Sep 21, 2023
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
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