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(experimental): Implement macro calls & splicing into Expr values #5203

Merged
merged 32 commits into from
Jun 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
ec4d2c5
Parse '$' and '!'
jfecher Jun 7, 2024
894241a
Call macro functions
jfecher Jun 7, 2024
6bf9706
Get macros working
jfecher Jun 7, 2024
9ab8c79
Fill in remainder of pass
jfecher Jun 7, 2024
f999355
Merge branch 'master' into jf/unquote
jfecher Jun 7, 2024
c599964
Clippy lints
jfecher Jun 7, 2024
86bf9c7
Merge branch 'master' into jf/unquote
jfecher Jun 10, 2024
698c7e9
Fix Expr type formatting
jfecher Jun 10, 2024
dcdd3ec
Fix fmt tests
jfecher Jun 10, 2024
0cba397
Fix trait impl type resolved in wrong module id
jfecher Jun 10, 2024
04e3f1b
Re-add commented line
jfecher Jun 10, 2024
8dfce88
Merge branch 'master' into jf/unquote
jfecher Jun 12, 2024
46262ba
Add test
jfecher Jun 12, 2024
e9558b7
Merge branch 'master' into jf/unquote
jfecher Jun 12, 2024
31aae03
Exclude 'macros' test from brillig tests
jfecher Jun 12, 2024
cd3d1f9
Port over parsing changes from macros PR
jfecher Jun 12, 2024
edc811e
Migrate over ast-creation changes
jfecher Jun 12, 2024
6d0ab3b
Add macros to ignored debugger tests
jfecher Jun 12, 2024
bf9e367
Update fmt files
jfecher Jun 12, 2024
cbe9e2f
Fix formatter
jfecher Jun 12, 2024
d4e9948
Merge branch 'jf/use-unused-parser-file' into jf/unquote
jfecher Jun 12, 2024
f2abe5c
Do not display plain:: in front of every path
jfecher Jun 12, 2024
e484191
Fix frontend test
jfecher Jun 12, 2024
81c6040
Remove plain:: prefix from parser tests
jfecher Jun 12, 2024
5d18638
Move macros test to compile_success_empty; it broke the gates report
jfecher Jun 12, 2024
0421b7c
Merge branch 'master' into jf/unquote
jfecher Jun 12, 2024
a423668
Merge branch 'master' into jf/unquote
jfecher Jun 14, 2024
796c25f
Update compiler/noirc_frontend/src/elaborator/expressions.rs
jfecher Jun 14, 2024
bd12537
Update compiler/noirc_frontend/src/elaborator/unquote.rs
jfecher Jun 14, 2024
b6dddaf
Update compiler/noirc_frontend/src/elaborator/mod.rs
jfecher Jun 14, 2024
303b827
Code review
jfecher Jun 14, 2024
8ec3870
Merge branch 'jf/unquote' of https://github.com/noir-lang/noir into j…
jfecher Jun 14, 2024
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
2 changes: 1 addition & 1 deletion compiler/noirc_driver/src/abi_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ pub(super) fn abi_type_from_hir_type(context: &Context, typ: &Type) -> AbiType {
| Type::TypeVariable(_, _)
| Type::NamedGeneric(..)
| Type::Forall(..)
| Type::Code
| Type::Expr
| Type::Slice(_)
| Type::Function(_, _, _) => unreachable!("{typ} cannot be used in the abi"),
Type::FmtString(_, _) => unreachable!("format strings cannot be used in the abi"),
Expand Down
11 changes: 11 additions & 0 deletions compiler/noirc_frontend/src/ast/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,17 @@ pub enum ExpressionKind {
Lambda(Box<Lambda>),
Parenthesized(Box<Expression>),
Quote(BlockExpression, Span),
Unquote(Box<Expression>),
Comptime(BlockExpression, Span),

/// Unquote expressions are replaced with UnquoteMarkers when Quoted
/// expressions are resolved. Since the expression being quoted remains an
/// ExpressionKind (rather than a resolved ExprId), the UnquoteMarker must be
/// here in the AST even though it is technically HIR-only.
/// Each usize in an UnquoteMarker is an index which corresponds to the index of the
/// expression in the Hir::Quote expression list to replace it with.
UnquoteMarker(usize),

// This variant is only emitted when inlining the result of comptime
// code. It is used to translate function values back into the AST while
// guaranteeing they have the same instantiated type and definition id without resolving again.
Expand Down Expand Up @@ -552,6 +561,8 @@ impl Display for ExpressionKind {
Comptime(block, _) => write!(f, "comptime {block}"),
Error => write!(f, "Error"),
Resolved(_) => write!(f, "?Resolved"),
Unquote(expr) => write!(f, "$({expr})"),
UnquoteMarker(index) => write!(f, "${index}"),
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/noirc_frontend/src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@
/*env:*/ Box<UnresolvedType>,
),

// The type of quoted code for metaprogramming

Check warning on line 119 in compiler/noirc_frontend/src/ast/mod.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (metaprogramming)
Code,
Expr,

Unspecified, // This is for when the user declares a variable without specifying it's type
Error,
Expand Down Expand Up @@ -216,7 +216,7 @@
}
}
MutableReference(element) => write!(f, "&mut {element}"),
Code => write!(f, "Code"),
Expr => write!(f, "Expr"),
Unit => write!(f, "()"),
Error => write!(f, "error"),
Unspecified => write!(f, "unspecified"),
Expand Down
6 changes: 5 additions & 1 deletion compiler/noirc_frontend/src/ast/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,11 @@ impl Display for LValue {
impl Display for Path {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let segments = vecmap(&self.segments, ToString::to_string);
write!(f, "{}::{}", self.kind, segments.join("::"))
if self.kind == PathKind::Plain {
write!(f, "{}", segments.join("::"))
} else {
write!(f, "{}::{}", self.kind, segments.join("::"))
}
}
}

Expand Down
104 changes: 98 additions & 6 deletions compiler/noirc_frontend/src/elaborator/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::{
HirArrayLiteral, HirBinaryOp, HirBlockExpression, HirCallExpression, HirCastExpression,
HirConstructorExpression, HirIfExpression, HirIndexExpression, HirInfixExpression,
HirLambda, HirMemberAccess, HirMethodCallExpression, HirMethodReference,
HirPrefixExpression,
HirPrefixExpression, HirQuoted,
},
traits::TraitConstraint,
},
Expand Down Expand Up @@ -64,6 +64,13 @@ impl<'context> Elaborator<'context> {
}
ExpressionKind::Resolved(id) => return (id, self.interner.id_type(id)),
ExpressionKind::Error => (HirExpression::Error, Type::Error),
ExpressionKind::Unquote(_) => {
self.push_err(ResolverError::UnquoteUsedOutsideQuote { span: expr.span });
(HirExpression::Error, Type::Error)
}
ExpressionKind::UnquoteMarker(index) => {
unreachable!("UnquoteMarker({index}) remaining in runtime code")
}
};
let id = self.interner.push_expr(hir_expr);
self.interner.push_expr_location(id, expr.span, self.file);
Expand Down Expand Up @@ -280,10 +287,22 @@ impl<'context> Elaborator<'context> {
(typ, arg, span)
});

// Avoid cloning arguments unless this is a macro call
let mut comptime_args = Vec::new();
if call.is_macro_call {
comptime_args = arguments.clone();
}

let location = Location::new(span, self.file);
let call = HirCallExpression { func, arguments, location };
let typ = self.type_check_call(&call, func_type, args, span);
(HirExpression::Call(call), typ)
let hir_call = HirCallExpression { func, arguments, location };
let typ = self.type_check_call(&hir_call, func_type, args, span);

if call.is_macro_call {
self.call_macro(func, comptime_args, location, typ)
.unwrap_or_else(|| (HirExpression::Error, Type::Error))
} else {
(HirExpression::Call(hir_call), typ)
}
}

fn elaborate_method_call(
Expand Down Expand Up @@ -627,8 +646,11 @@ impl<'context> Elaborator<'context> {
(expr, Type::Function(arg_types, Box::new(body_type), Box::new(env_type)))
}

fn elaborate_quote(&mut self, block: BlockExpression) -> (HirExpression, Type) {
(HirExpression::Quote(block), Type::Code)
fn elaborate_quote(&mut self, mut block: BlockExpression) -> (HirExpression, Type) {
let mut unquoted_exprs = Vec::new();
self.find_unquoted_exprs_in_block(&mut block, &mut unquoted_exprs);
let quoted = HirQuoted { quoted_block: block, unquoted_exprs };
(HirExpression::Quote(quoted), Type::Expr)
}

fn elaborate_comptime_block(&mut self, block: BlockExpression, span: Span) -> (ExprId, Type) {
Expand Down Expand Up @@ -661,4 +683,74 @@ impl<'context> Elaborator<'context> {
Err(error) => make_error(self, error),
}
}

fn try_get_comptime_function(
&mut self,
func: ExprId,
location: Location,
) -> Result<FuncId, ResolverError> {
match self.interner.expression(&func) {
HirExpression::Ident(ident, _generics) => {
let definition = self.interner.definition(ident.id);
if let DefinitionKind::Function(function) = definition.kind {
let meta = self.interner.function_modifiers(&function);
if meta.is_comptime {
Ok(function)
} else {
Err(ResolverError::MacroIsNotComptime { span: location.span })
}
} else {
Err(ResolverError::InvalidSyntaxInMacroCall { span: location.span })
}
}
_ => Err(ResolverError::InvalidSyntaxInMacroCall { span: location.span }),
}
}

/// Call a macro function and inlines its code at the call site.
/// This will also perform a type check to ensure that the return type is an `Expr` value.
fn call_macro(
&mut self,
func: ExprId,
arguments: Vec<ExprId>,
location: Location,
return_type: Type,
) -> Option<(HirExpression, Type)> {
self.unify(&return_type, &Type::Expr, || TypeCheckError::MacroReturningNonExpr {
typ: return_type.clone(),
span: location.span,
});

let function = match self.try_get_comptime_function(func, location) {
Ok(function) => function,
Err(error) => {
self.push_err(error);
return None;
}
};

let mut interpreter = Interpreter::new(self.interner, &mut self.comptime_scopes);

let mut comptime_args = Vec::new();
let mut errors = Vec::new();

for argument in arguments {
match interpreter.evaluate(argument) {
Ok(arg) => {
let location = interpreter.interner.expr_location(&argument);
comptime_args.push((arg, location));
}
Err(error) => errors.push((error.into(), self.file)),
}
}

if !errors.is_empty() {
vezenovm marked this conversation as resolved.
Show resolved Hide resolved
self.errors.append(&mut errors);
return None;
}

let result = interpreter.call_function(function, comptime_args, location);
let (expr_id, typ) = self.inline_comptime_value(result, location.span);
Some((self.interner.expression(&expr_id), typ))
}
}
1 change: 1 addition & 0 deletions compiler/noirc_frontend/src/elaborator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ mod scope;
mod statements;
mod traits;
mod types;
mod unquote;

use fm::FileId;
use iter_extended::vecmap;
Expand Down
4 changes: 2 additions & 2 deletions compiler/noirc_frontend/src/elaborator/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ impl<'context> Elaborator<'context> {
let fields = self.resolve_type_inner(*fields);
Type::FmtString(Box::new(resolved_size), Box::new(fields))
}
Code => Type::Code,
Expr => Type::Expr,
Unit => Type::Unit,
Unspecified => Type::Error,
Error => Type::Error,
Expand Down Expand Up @@ -1396,7 +1396,7 @@ impl<'context> Elaborator<'context> {
| Type::TypeVariable(_, _)
| Type::Constant(_)
| Type::NamedGeneric(_, _)
| Type::Code
| Type::Expr
| Type::Forall(_, _) => (),

Type::TraitAsType(_, _, args) => {
Expand Down
Loading
Loading