Skip to content

Commit

Permalink
Merge b557965 into c7479c4
Browse files Browse the repository at this point in the history
  • Loading branch information
asterite authored Sep 5, 2024
2 parents c7479c4 + b557965 commit 89f69cc
Show file tree
Hide file tree
Showing 11 changed files with 156 additions and 87 deletions.
87 changes: 49 additions & 38 deletions compiler/noirc_frontend/src/elaborator/lints.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
use crate::{
ast::FunctionKind,
ast::{FunctionKind, Ident},
graph::CrateId,
hir::{
resolution::errors::{PubPosition, ResolverError},
type_check::TypeCheckError,
},
hir_def::expr::HirIdent,
hir_def::{expr::HirIdent, function::FuncMeta},
macros_api::{
HirExpression, HirLiteral, NodeInterner, NoirFunction, Signedness, UnaryOp,
UnresolvedTypeData, Visibility,
HirExpression, HirLiteral, NodeInterner, NoirFunction, Signedness, UnaryOp, Visibility,
},
node_interner::{DefinitionKind, ExprId, FuncId},
node_interner::{DefinitionKind, ExprId, FuncId, FunctionModifiers},
Type,
};

use noirc_errors::Span;
use noirc_errors::{Span, Spanned};

pub(super) fn deprecated_function(interner: &NodeInterner, expr: ExprId) -> Option<TypeCheckError> {
let HirExpression::Ident(HirIdent { location, id, impl_kind: _ }, _) =
Expand All @@ -39,16 +38,17 @@ pub(super) fn deprecated_function(interner: &NodeInterner, expr: ExprId) -> Opti
/// Inline attributes are only relevant for constrained functions
/// as all unconstrained functions are not inlined and so
/// associated attributes are disallowed.
pub(super) fn inlining_attributes(func: &NoirFunction) -> Option<ResolverError> {
if func.def.is_unconstrained {
let attributes = func.attributes().clone();

if attributes.is_no_predicates() {
Some(ResolverError::NoPredicatesAttributeOnUnconstrained {
ident: func.name_ident().clone(),
})
} else if attributes.is_foldable() {
Some(ResolverError::FoldAttributeOnUnconstrained { ident: func.name_ident().clone() })
pub(super) fn inlining_attributes(
func: &FuncMeta,
modifiers: &FunctionModifiers,
) -> Option<ResolverError> {
if modifiers.is_unconstrained {
if modifiers.attributes.is_no_predicates() {
let ident = func_meta_name_ident(func, modifiers);
Some(ResolverError::NoPredicatesAttributeOnUnconstrained { ident })
} else if modifiers.attributes.is_foldable() {
let ident = func_meta_name_ident(func, modifiers);
Some(ResolverError::FoldAttributeOnUnconstrained { ident })
} else {
None
}
Expand All @@ -59,24 +59,30 @@ pub(super) fn inlining_attributes(func: &NoirFunction) -> Option<ResolverError>

/// Attempting to define new low level (`#[builtin]` or `#[foreign]`) functions outside of the stdlib is disallowed.
pub(super) fn low_level_function_outside_stdlib(
func: &NoirFunction,
func: &FuncMeta,
modifiers: &FunctionModifiers,
crate_id: CrateId,
) -> Option<ResolverError> {
let is_low_level_function =
func.attributes().function.as_ref().map_or(false, |func| func.is_low_level());
modifiers.attributes.function.as_ref().map_or(false, |func| func.is_low_level());
if !crate_id.is_stdlib() && is_low_level_function {
Some(ResolverError::LowLevelFunctionOutsideOfStdlib { ident: func.name_ident().clone() })
let ident = func_meta_name_ident(func, modifiers);
Some(ResolverError::LowLevelFunctionOutsideOfStdlib { ident })
} else {
None
}
}

/// Oracle definitions (functions with the `#[oracle]` attribute) must be marked as unconstrained.
pub(super) fn oracle_not_marked_unconstrained(func: &NoirFunction) -> Option<ResolverError> {
pub(super) fn oracle_not_marked_unconstrained(
func: &FuncMeta,
modifiers: &FunctionModifiers,
) -> Option<ResolverError> {
let is_oracle_function =
func.attributes().function.as_ref().map_or(false, |func| func.is_oracle());
if is_oracle_function && !func.def.is_unconstrained {
Some(ResolverError::OracleMarkedAsConstrained { ident: func.name_ident().clone() })
modifiers.attributes.function.as_ref().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 })
} else {
None
}
Expand Down Expand Up @@ -106,24 +112,26 @@ pub(super) fn oracle_called_from_constrained_function(
}

/// `pub` is required on return types for entry point functions
pub(super) fn missing_pub(func: &NoirFunction, is_entry_point: bool) -> Option<ResolverError> {
if is_entry_point
&& func.return_type().typ != UnresolvedTypeData::Unit
&& func.def.return_visibility == Visibility::Private
pub(super) fn missing_pub(func: &FuncMeta, modifiers: &FunctionModifiers) -> Option<ResolverError> {
if func.is_entry_point
&& func.return_type() != &Type::Unit
&& func.return_visibility == Visibility::Private
{
Some(ResolverError::NecessaryPub { ident: func.name_ident().clone() })
let ident = func_meta_name_ident(func, modifiers);
Some(ResolverError::NecessaryPub { ident })
} else {
None
}
}

/// `#[recursive]` attribute is only allowed for entry point functions
pub(super) fn recursive_non_entrypoint_function(
func: &NoirFunction,
is_entry_point: bool,
func: &FuncMeta,
modifiers: &FunctionModifiers,
) -> Option<ResolverError> {
if !is_entry_point && func.kind == FunctionKind::Recursive {
Some(ResolverError::MisplacedRecursiveAttribute { ident: func.name_ident().clone() })
if !func.is_entry_point && func.kind == FunctionKind::Recursive {
let ident = func_meta_name_ident(func, modifiers);
Some(ResolverError::MisplacedRecursiveAttribute { ident })
} else {
None
}
Expand Down Expand Up @@ -163,14 +171,13 @@ pub(super) fn unconstrained_function_return(
///
/// Application of `pub` to other functions is not meaningful and is a mistake.
pub(super) fn unnecessary_pub_return(
func: &NoirFunction,
func: &FuncMeta,
modifiers: &FunctionModifiers,
is_entry_point: bool,
) -> Option<ResolverError> {
if !is_entry_point && func.def.return_visibility == Visibility::Public {
Some(ResolverError::UnnecessaryPub {
ident: func.name_ident().clone(),
position: PubPosition::ReturnType,
})
if !is_entry_point && func.return_visibility == Visibility::Public {
let ident = func_meta_name_ident(func, modifiers);
Some(ResolverError::UnnecessaryPub { ident, position: PubPosition::ReturnType })
} else {
None
}
Expand Down Expand Up @@ -252,3 +259,7 @@ pub(crate) fn overflowing_int(

errors
}

fn func_meta_name_ident(func: &FuncMeta, modifiers: &FunctionModifiers) -> Ident {
Ident(Spanned::from(func.name.location.span, modifiers.name.clone()))
}
38 changes: 23 additions & 15 deletions compiler/noirc_frontend/src/elaborator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ use crate::{
SecondaryAttribute, StructId,
},
node_interner::{
DefinitionKind, DependencyId, ExprId, FuncId, GlobalId, ReferenceId, TraitId, TypeAliasId,
DefinitionKind, DependencyId, ExprId, FuncId, FunctionModifiers, GlobalId, ReferenceId,
TraitId, TypeAliasId,
},
token::CustomAtrribute,
Shared, Type, TypeVariable,
Expand Down Expand Up @@ -417,6 +418,10 @@ impl<'context> Elaborator<'context> {
self.trait_bounds = func_meta.trait_constraints.clone();
self.function_context.push(FunctionContext::default());

let modifiers = self.interner.function_modifiers(&id).clone();

self.run_function_lints(&func_meta, &modifiers);

self.introduce_generics_into_scope(func_meta.all_generics.clone());

// The DefinitionIds for each parameter were already created in define_function_meta
Expand Down Expand Up @@ -731,20 +736,6 @@ impl<'context> Elaborator<'context> {

let is_entry_point = self.is_entry_point_function(func, in_contract);

self.run_lint(|_| lints::inlining_attributes(func).map(Into::into));
self.run_lint(|_| lints::missing_pub(func, is_entry_point).map(Into::into));
self.run_lint(|elaborator| {
lints::unnecessary_pub_return(func, elaborator.pub_allowed(func, in_contract))
.map(Into::into)
});
self.run_lint(|_| lints::oracle_not_marked_unconstrained(func).map(Into::into));
self.run_lint(|elaborator| {
lints::low_level_function_outside_stdlib(func, elaborator.crate_id).map(Into::into)
});
self.run_lint(|_| {
lints::recursive_non_entrypoint_function(func, is_entry_point).map(Into::into)
});

// Both the #[fold] and #[no_predicates] alter a function's inline type and code generation in similar ways.
// In certain cases such as type checking (for which the following flag will be used) both attributes
// indicate we should code generate in the same way. Thus, we unify the attributes into one flag here.
Expand Down Expand Up @@ -858,6 +849,23 @@ impl<'context> Elaborator<'context> {
self.current_item = None;
}

fn run_function_lints(&mut self, func: &FuncMeta, modifiers: &FunctionModifiers) {
self.run_lint(|_| lints::inlining_attributes(func, modifiers).map(Into::into));
self.run_lint(|_| lints::missing_pub(func, modifiers).map(Into::into));
self.run_lint(|_| {
let pub_allowed = func.is_entry_point || modifiers.attributes.is_foldable();
lints::unnecessary_pub_return(func, modifiers, pub_allowed).map(Into::into)
});
self.run_lint(|_| lints::oracle_not_marked_unconstrained(func, modifiers).map(Into::into));
self.run_lint(|elaborator| {
lints::low_level_function_outside_stdlib(func, modifiers, elaborator.crate_id)
.map(Into::into)
});
self.run_lint(|_| {
lints::recursive_non_entrypoint_function(func, modifiers).map(Into::into)
});
}

/// Only sized types are valid to be used as main's parameters or the parameters to a contract
/// function. If the given type is not sized (e.g. contains a slice or NamedGeneric type), an
/// error is issued.
Expand Down
24 changes: 23 additions & 1 deletion compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{
use acvm::{AcirField, FieldElement};
use builtin_helpers::{
block_expression_to_value, check_argument_count, check_function_not_yet_resolved,
check_one_argument, check_three_arguments, check_two_arguments, get_expr, get_field,
check_one_argument, check_three_arguments, check_two_arguments, get_bool, get_expr, get_field,
get_format_string, get_function_def, get_module, get_quoted, get_slice, get_struct,
get_trait_constraint, get_trait_def, get_trait_impl, get_tuple, get_type, get_typed_expr,
get_u32, get_unresolved_type, hir_pattern_to_tokens, mutate_func_meta_type, parse,
Expand Down Expand Up @@ -110,6 +110,9 @@ impl<'local, 'context> Interpreter<'local, 'context> {
"function_def_set_return_type" => {
function_def_set_return_type(self, arguments, location)
}
"function_def_set_return_public" => {
function_def_set_return_public(self, arguments, location)
}
"module_functions" => module_functions(self, arguments, location),
"module_has_named_attribute" => module_has_named_attribute(self, arguments, location),
"module_is_contract" => module_is_contract(self, arguments, location),
Expand Down Expand Up @@ -1935,6 +1938,25 @@ fn function_def_set_return_type(
Ok(Value::Unit)
}

// fn set_return_public(self, public: bool)
fn function_def_set_return_public(
interpreter: &mut Interpreter,
arguments: Vec<(Value, Location)>,
location: Location,
) -> IResult<Value> {
let (self_argument, public) = check_two_arguments(arguments, location)?;

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

let public = get_bool(public)?;

let func_meta = interpreter.elaborator.interner.function_meta_mut(&func_id);
func_meta.return_visibility = if public { Visibility::Public } else { Visibility::Private };

Ok(Value::Unit)
}

// fn functions(self) -> [FunctionDefinition]
fn module_functions(
interpreter: &Interpreter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ pub(crate) fn get_array(
}
}

pub(crate) fn get_bool((value, location): (Value, Location)) -> IResult<bool> {
match value {
Value::Bool(value) => Ok(value),
value => type_mismatch(value, Type::Bool, location),
}
}

pub(crate) fn get_slice(
interner: &NodeInterner,
(value, location): (Value, Location),
Expand Down
3 changes: 2 additions & 1 deletion compiler/noirc_frontend/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ use noirc_errors::Span;
pub use parser::path::path_no_turbofish;
pub use parser::traits::trait_bound;
pub use parser::{
block, expression, fresh_statement, lvalue, parse_program, parse_type, pattern, top_level_items,
block, expression, fresh_statement, lvalue, parse_program, parse_type, pattern,
top_level_items, visibility,
};

#[derive(Debug, Clone)]
Expand Down
14 changes: 3 additions & 11 deletions compiler/noirc_frontend/src/parser/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ use self::primitives::{keyword, macro_quote_marker, mutable_reference, variable}
use self::types::{generic_type_args, maybe_comp_time};
use attributes::{attributes, inner_attribute, validate_secondary_attributes};
pub use types::parse_type;
use visibility::visibility_modifier;
use visibility::item_visibility;
pub use visibility::visibility;

use super::{
foldl_with_span, labels::ParsingRuleLabel, parameter_name_recovery, parameter_recovery,
Expand Down Expand Up @@ -459,7 +460,7 @@ fn module_declaration() -> impl NoirParser<TopLevelStatement> {
}

fn use_statement() -> impl NoirParser<TopLevelStatement> {
visibility_modifier()
item_visibility()
.then_ignore(keyword(Keyword::Use))
.then(use_tree())
.map(|(visibility, use_tree)| TopLevelStatement::Import(use_tree, visibility))
Expand Down Expand Up @@ -737,15 +738,6 @@ fn call_data() -> impl NoirParser<Visibility> {
})
}

fn optional_visibility() -> impl NoirParser<Visibility> {
keyword(Keyword::Pub)
.map(|_| Visibility::Public)
.or(call_data())
.or(keyword(Keyword::ReturnData).map(|_| Visibility::ReturnData))
.or_not()
.map(|opt| opt.unwrap_or(Visibility::Private))
}

pub fn expression() -> impl ExprParser {
recursive(|expr| {
expression_with_precedence(
Expand Down
28 changes: 11 additions & 17 deletions compiler/noirc_frontend/src/parser/parser/function.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use super::{
attributes::{attributes, validate_attributes},
block, fresh_statement, ident, keyword, maybe_comp_time, nothing, optional_visibility,
parameter_name_recovery, parameter_recovery, parenthesized, parse_type, pattern,
block, fresh_statement, ident, keyword, maybe_comp_time, nothing, parameter_name_recovery,
parameter_recovery, parenthesized, parse_type, pattern,
primitives::token_kind,
self_parameter,
visibility::visibility_modifier,
visibility::{item_visibility, visibility},
where_clause, NoirParser,
};
use crate::token::{Keyword, Token, TokenKind};
Expand Down Expand Up @@ -79,13 +79,9 @@ pub(super) fn function_definition(allow_self: bool) -> impl NoirParser<NoirFunct
///
/// returns (is_unconstrained, visibility) for whether each keyword was present
fn function_modifiers() -> impl NoirParser<(bool, ItemVisibility, bool)> {
keyword(Keyword::Unconstrained)
.or_not()
.then(visibility_modifier())
.then(maybe_comp_time())
.map(|((unconstrained, visibility), comptime)| {
(unconstrained.is_some(), visibility, comptime)
})
keyword(Keyword::Unconstrained).or_not().then(item_visibility()).then(maybe_comp_time()).map(
|((unconstrained, visibility), comptime)| (unconstrained.is_some(), visibility, comptime),
)
}

pub(super) fn numeric_generic() -> impl NoirParser<UnresolvedGeneric> {
Expand Down Expand Up @@ -142,14 +138,12 @@ pub(super) fn generics() -> impl NoirParser<UnresolvedGenerics> {

pub(super) fn function_return_type() -> impl NoirParser<(Visibility, FunctionReturnType)> {
#[allow(deprecated)]
just(Token::Arrow)
.ignore_then(optional_visibility())
.then(spanned(parse_type()))
.or_not()
.map_with_span(|ret, span| match ret {
just(Token::Arrow).ignore_then(visibility()).then(spanned(parse_type())).or_not().map_with_span(
|ret, span| match ret {
Some((visibility, (ty, _))) => (visibility, FunctionReturnType::Ty(ty)),
None => (Visibility::Private, FunctionReturnType::Default(span)),
})
},
)
}

fn function_parameters<'a>(allow_self: bool) -> impl NoirParser<Vec<Param>> + 'a {
Expand All @@ -158,7 +152,7 @@ fn function_parameters<'a>(allow_self: bool) -> impl NoirParser<Vec<Param>> + 'a
let full_parameter = pattern()
.recover_via(parameter_name_recovery())
.then_ignore(just(Token::Colon))
.then(optional_visibility())
.then(visibility())
.then(typ)
.map_with_span(|((pattern, visibility), typ), span| Param {
visibility,
Expand Down
Loading

0 comments on commit 89f69cc

Please sign in to comment.