Skip to content

Commit

Permalink
Merge branch 'master' into aztec-packages
Browse files Browse the repository at this point in the history
  • Loading branch information
TomAFrench authored Jun 27, 2024
2 parents 2e86623 + 554dd6b commit 1658d8d
Show file tree
Hide file tree
Showing 17 changed files with 380 additions and 153 deletions.
6 changes: 5 additions & 1 deletion compiler/noirc_errors/src/reporter.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::io::IsTerminal;

use crate::{FileDiagnostic, Location, Span};
use codespan_reporting::diagnostic::{Diagnostic, Label};
use codespan_reporting::files::Files;
Expand Down Expand Up @@ -148,7 +150,9 @@ pub fn report<'files>(
call_stack: &[Location],
deny_warnings: bool,
) -> bool {
let writer = StandardStream::stderr(ColorChoice::Always);
let color_choice =
if std::io::stderr().is_terminal() { ColorChoice::Auto } else { ColorChoice::Never };
let writer = StandardStream::stderr(color_choice);
let config = codespan_reporting::term::Config::default();

let stack_trace = stack_trace(files, call_stack);
Expand Down
18 changes: 14 additions & 4 deletions compiler/noirc_frontend/src/ast/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -757,22 +757,32 @@ impl Display for FunctionDefinition {
writeln!(f, "{:?}", self.attributes)?;

let parameters = vecmap(&self.parameters, |Param { visibility, pattern, typ, span: _ }| {
format!("{pattern}: {visibility} {typ}")
if *visibility == Visibility::Public {
format!("{pattern}: {visibility} {typ}")
} else {
format!("{pattern}: {typ}")
}
});

let where_clause = vecmap(&self.where_clause, ToString::to_string);
let where_clause_str = if !where_clause.is_empty() {
format!("where {}", where_clause.join(", "))
format!(" where {}", where_clause.join(", "))
} else {
"".to_string()
};

let return_type = if matches!(&self.return_type, FunctionReturnType::Default(_)) {
String::new()
} else {
format!(" -> {}", self.return_type)
};

write!(
f,
"fn {}({}) -> {} {} {}",
"fn {}({}){}{} {}",
self.name,
parameters.join(", "),
self.return_type,
return_type,
where_clause_str,
self.body
)
Expand Down
3 changes: 2 additions & 1 deletion compiler/noirc_frontend/src/elaborator/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -744,7 +744,8 @@ impl<'context> Elaborator<'context> {
return None;
}

let result = interpreter.call_function(function, comptime_args, location);
let bindings = interpreter.interner.get_instantiation_bindings(func).clone();
let result = interpreter.call_function(function, comptime_args, bindings, location);
let (expr_id, typ) = self.inline_comptime_value(result, location.span);
Some((self.interner.expression(&expr_id), typ))
}
Expand Down
170 changes: 147 additions & 23 deletions compiler/noirc_frontend/src/elaborator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ use std::{
use crate::{
ast::{FunctionKind, UnresolvedTraitConstraint},
hir::{
comptime::{self, Interpreter, Value},
comptime::{self, Interpreter, InterpreterError, Value},
def_collector::{
dc_crate::{
filter_literal_globals, CompilationError, ImplMap, UnresolvedGlobal,
UnresolvedStruct, UnresolvedTypeAlias,
},
dc_mod,
errors::DuplicateType,
},
resolution::{errors::ResolverError, path_resolver::PathResolver, resolver::LambdaContext},
Expand All @@ -30,7 +31,8 @@ use crate::{
node_interner::{
DefinitionId, DefinitionKind, DependencyId, ExprId, FuncId, GlobalId, TraitId, TypeAliasId,
},
Shared, Type, TypeVariable,
parser::TopLevelStatement,
Shared, Type, TypeBindings, TypeVariable,
};
use crate::{
ast::{TraitBound, UnresolvedGenerics},
Expand Down Expand Up @@ -239,6 +241,9 @@ impl<'context> Elaborator<'context> {
self.define_type_alias(alias_id, alias);
}

// Must resolve structs before we resolve globals.
let generated_items = self.collect_struct_definitions(items.types);

self.define_function_metas(&mut items.functions, &mut items.impls, &mut items.trait_impls);
self.collect_traits(items.traits);

Expand All @@ -265,6 +270,16 @@ impl<'context> Elaborator<'context> {
self.elaborate_global(global);
}

// After everything is collected, we can elaborate our generated items.
// It may be better to inline these within `items` entirely since elaborating them
// all here means any globals will not see these. Inlining them completely within `items`
// means we must be more careful about missing any additional items that need to be already
// elaborated. E.g. if a new struct is created, we've already passed the code path to
// elaborate them.
if !generated_items.is_empty() {
self.elaborate_items(generated_items);
}

for functions in items.functions {
self.elaborate_functions(functions);
}
Expand Down Expand Up @@ -1087,11 +1102,18 @@ impl<'context> Elaborator<'context> {
self.generics.clear();
}

fn collect_struct_definitions(&mut self, structs: BTreeMap<StructId, UnresolvedStruct>) {
fn collect_struct_definitions(
&mut self,
structs: BTreeMap<StructId, UnresolvedStruct>,
) -> CollectedItems {
// This is necessary to avoid cloning the entire struct map
// when adding checks after each struct field is resolved.
let struct_ids = structs.keys().copied().collect::<Vec<_>>();

// This will contain any additional top-level items that are generated at compile-time
// via macros. This often includes derived trait impls.
let mut generated_items = CollectedItems::default();

// Resolve each field in each struct.
// Each struct should already be present in the NodeInterner after def collection.
for (type_id, mut typ) in structs {
Expand All @@ -1107,7 +1129,7 @@ impl<'context> Elaborator<'context> {
struct_def.generics = generics;
});

self.run_comptime_attributes_on_struct(attributes, type_id, span);
self.run_comptime_attributes_on_struct(attributes, type_id, span, &mut generated_items);
}

// Check whether the struct fields have nested slices
Expand All @@ -1129,40 +1151,65 @@ impl<'context> Elaborator<'context> {
}
}
}

generated_items
}

fn run_comptime_attributes_on_struct(
&mut self,
attributes: Vec<SecondaryAttribute>,
struct_id: StructId,
span: Span,
generated_items: &mut CollectedItems,
) {
for attribute in attributes {
if let SecondaryAttribute::Custom(name) = attribute {
match self.lookup_global(Path::from_single(name, span)) {
Ok(id) => {
let definition = self.interner.definition(id);
if let DefinitionKind::Function(function) = &definition.kind {
let function = *function;
let mut interpreter =
Interpreter::new(self.interner, &mut self.comptime_scopes);

let location = Location::new(span, self.file);
let arguments = vec![(Value::TypeDefinition(struct_id), location)];
let result = interpreter.call_function(function, arguments, location);
if let Err(error) = result {
self.errors.push(error.into_compilation_error_pair());
}
} else {
self.push_err(ResolverError::NonFunctionInAnnotation { span });
}
}
Err(_) => self.push_err(ResolverError::UnknownAnnotation { span }),
if let Err(error) =
self.run_comptime_attribute_on_struct(name, struct_id, span, generated_items)
{
self.errors.push(error);

}
}
}
}

fn run_comptime_attribute_on_struct(
&mut self,
attribute: String,
struct_id: StructId,
span: Span,
generated_items: &mut CollectedItems,
) -> Result<(), (CompilationError, FileId)> {
let id = self
.lookup_global(Path::from_single(attribute, span))
.map_err(|_| (ResolverError::UnknownAnnotation { span }.into(), self.file))?;

let definition = self.interner.definition(id);
let DefinitionKind::Function(function) = definition.kind else {
return Err((ResolverError::NonFunctionInAnnotation { span }.into(), self.file));
};
let mut interpreter =
Interpreter::new(self.interner, &mut self.comptime_scopes, self.crate_id);

let location = Location::new(span, self.file);
let arguments = vec![(Value::TypeDefinition(struct_id), location)];

let value = interpreter
.call_function(function, arguments, TypeBindings::new(), location)
.map_err(|error| error.into_compilation_error_pair())?;

if value != Value::Unit {
let item = value
.into_top_level_item(location)
.map_err(|error| error.into_compilation_error_pair())?;

self.add_item(item, generated_items, location);
}

Ok(())
}

pub fn resolve_struct_fields(
&mut self,
unresolved: NoirStruct,
Expand Down Expand Up @@ -1358,4 +1405,81 @@ impl<'context> Elaborator<'context> {
items.functions = function_sets;
(comptime, items)
}

fn add_item(
&mut self,
item: TopLevelStatement,
generated_items: &mut CollectedItems,
location: Location,
) {
match item {
TopLevelStatement::Function(function) => {
let id = self.interner.push_empty_fn();
let module = self.module_id();
self.interner.push_function(id, &function.def, module, location);
let functions = vec![(self.local_module, id, function)];
generated_items.functions.push(UnresolvedFunctions {
file_id: self.file,
functions,
trait_id: None,
self_type: None,
});
}
TopLevelStatement::TraitImpl(mut trait_impl) => {
let methods = dc_mod::collect_trait_impl_functions(
self.interner,
&mut trait_impl,
self.crate_id,
self.file,
self.local_module,
);

generated_items.trait_impls.push(UnresolvedTraitImpl {
file_id: self.file,
module_id: self.local_module,
trait_generics: trait_impl.trait_generics,
trait_path: trait_impl.trait_name,
object_type: trait_impl.object_type,
methods,
generics: trait_impl.impl_generics,
where_clause: trait_impl.where_clause,

// These last fields are filled in later
trait_id: None,
impl_id: None,
resolved_object_type: None,
resolved_generics: Vec::new(),
resolved_trait_generics: Vec::new(),
});
}
TopLevelStatement::Global(global) => {
let (global, error) = dc_mod::collect_global(
self.interner,
self.def_maps.get_mut(&self.crate_id).unwrap(),
global,
self.file,
self.local_module,
);

generated_items.globals.push(global);
if let Some(error) = error {
self.errors.push(error);
}
}
// Assume that an error has already been issued
TopLevelStatement::Error => (),

TopLevelStatement::Module(_)
| TopLevelStatement::Import(_)
| TopLevelStatement::Struct(_)
| TopLevelStatement::Trait(_)
| TopLevelStatement::Impl(_)
| TopLevelStatement::TypeAlias(_)
| TopLevelStatement::SubModule(_) => {
let item = item.to_string();
let error = InterpreterError::UnsupportedTopLevelItemUnquote { item, location };
self.errors.push(error.into_compilation_error_pair());
}
}
}
}
21 changes: 20 additions & 1 deletion compiler/noirc_frontend/src/hir/comptime/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ pub enum InterpreterError {
CannotInlineMacro { value: Value, location: Location },
UnquoteFoundDuringEvaluation { location: Location },
FailedToParseMacro { error: ParserError, tokens: Rc<Tokens>, rule: &'static str, file: FileId },
UnsupportedTopLevelItemUnquote { item: String, location: Location },
NonComptimeFnCallInSameCrate { function: String, location: Location },

Unimplemented { item: String, location: Location },

Expand Down Expand Up @@ -101,6 +103,8 @@ impl InterpreterError {
| InterpreterError::NonStructInConstructor { location, .. }
| InterpreterError::CannotInlineMacro { location, .. }
| InterpreterError::UnquoteFoundDuringEvaluation { location, .. }
| InterpreterError::UnsupportedTopLevelItemUnquote { location, .. }
| InterpreterError::NonComptimeFnCallInSameCrate { location, .. }
| InterpreterError::Unimplemented { location, .. }
| InterpreterError::BreakNotInLoop { location, .. }
| InterpreterError::ContinueNotInLoop { location, .. } => *location,
Expand Down Expand Up @@ -259,7 +263,8 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic {
CustomDiagnostic::simple_error(msg, String::new(), location.span)
}
InterpreterError::CannotInlineMacro { value, location } => {
let msg = "Cannot inline value into runtime code if it contains references".into();
let typ = value.get_type();
let msg = format!("Cannot inline values of type `{typ}` into this position");
let secondary = format!("Cannot inline value {value:?}");
CustomDiagnostic::simple_error(msg, secondary, location.span)
}
Expand Down Expand Up @@ -293,6 +298,20 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic {
diagnostic.add_note(push_the_problem_on_the_library_author);
diagnostic
}
InterpreterError::UnsupportedTopLevelItemUnquote { item, location } => {
let msg = "Unsupported statement type to unquote".into();
let secondary =
"Only functions, globals, and trait impls can be unquoted here".into();
let mut error = CustomDiagnostic::simple_error(msg, secondary, location.span);
error.add_note(format!("Unquoted item was:\n{item}"));
error
}
InterpreterError::NonComptimeFnCallInSameCrate { function, location } => {
let msg = format!("`{function}` cannot be called in a `comptime` context here");
let secondary =
"This function must be `comptime` or in a separate crate to be called".into();
CustomDiagnostic::simple_error(msg, secondary, location.span)
}
InterpreterError::Unimplemented { item, location } => {
let msg = format!("{item} is currently unimplemented");
CustomDiagnostic::simple_error(msg, String::new(), location.span)
Expand Down
Loading

0 comments on commit 1658d8d

Please sign in to comment.