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

fix: NoMatchingImplFound in comptime code only #5617

Merged
merged 5 commits into from
Jul 29, 2024
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
64 changes: 61 additions & 3 deletions compiler/noirc_frontend/src/hir/comptime/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
undo_instantiation_bindings,
};
use crate::token::Tokens;
use crate::TypeVariable;
use crate::{
hir_def::{
expr::{
Expand Down Expand Up @@ -53,6 +54,12 @@
in_loop: bool,

current_function: Option<FuncId>,

/// Maps each bound generic to each binding it has in the current callstack.
/// Since the interpreter monomorphizes as it interprets, we can bind over the same generic
/// multiple times. Without this map, when one of these inner functions exits we would
/// unbind the generic completely instead of resetting it to its previous binding.
bound_generics: Vec<HashMap<TypeVariable, Type>>,
}

#[allow(unused)]
Expand All @@ -62,26 +69,41 @@
crate_id: CrateId,
current_function: Option<FuncId>,
) -> Self {
Self { elaborator, crate_id, current_function, in_loop: false }
let bound_generics = Vec::new();
Self { elaborator, crate_id, current_function, bound_generics, in_loop: false }
}

pub(crate) fn call_function(
&mut self,
function: FuncId,
arguments: Vec<(Value, Location)>,
instantiation_bindings: TypeBindings,
mut instantiation_bindings: TypeBindings,
location: Location,
) -> IResult<Value> {
let trait_method = self.elaborator.interner.get_trait_method_id(function);

// To match the monomorphizer, we need to call follow_bindings on each of
// the instantiation bindings before we unbind the generics from the previous function.
// This is because the instantiation bindings refer to variables from the call site.
for (_, binding) in instantiation_bindings.values_mut() {
*binding = binding.follow_bindings();
}

self.unbind_generics_from_previous_function();
perform_instantiation_bindings(&instantiation_bindings);
let impl_bindings =
let mut impl_bindings =
perform_impl_bindings(self.elaborator.interner, trait_method, function, location)?;

for (_, binding) in impl_bindings.values_mut() {
*binding = binding.follow_bindings();
}

self.remember_bindings(&instantiation_bindings, &impl_bindings);
let result = self.call_function_inner(function, arguments, location);

undo_instantiation_bindings(impl_bindings);
undo_instantiation_bindings(instantiation_bindings);
self.rebind_generics_from_previous_function();
result
}

Expand Down Expand Up @@ -183,7 +205,7 @@
}
} else {
let name = self.elaborator.interner.function_name(&function);
unreachable!("Non-builtin, lowlevel or oracle builtin fn '{name}'")

Check warning on line 208 in compiler/noirc_frontend/src/hir/comptime/interpreter.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (lowlevel)
}
}

Expand Down Expand Up @@ -250,6 +272,42 @@
self.elaborator.comptime_scopes.last_mut().unwrap()
}

fn unbind_generics_from_previous_function(&mut self) {
if let Some(bindings) = self.bound_generics.last() {
for var in bindings.keys() {
var.unbind(var.id());
}
}
// Push a new bindings list for the current function
self.bound_generics.push(HashMap::default());
}

fn rebind_generics_from_previous_function(&mut self) {
// Remove the currently bound generics first.
self.bound_generics.pop();

if let Some(bindings) = self.bound_generics.last() {
for (var, binding) in bindings {
var.force_bind(binding.clone());
}
}
}

fn remember_bindings(&mut self, main_bindings: &TypeBindings, impl_bindings: &TypeBindings) {
let bound_generics = self
.bound_generics
.last_mut()
.expect("remember_bindings called with no bound_generics on the stack");

for (var, binding) in main_bindings.values() {
bound_generics.insert(var.clone(), binding.follow_bindings());
}

for (var, binding) in impl_bindings.values() {
bound_generics.insert(var.clone(), binding.follow_bindings());
}
}

pub(super) fn define_pattern(
&mut self,
pattern: &HirPattern,
Expand Down Expand Up @@ -1177,8 +1235,8 @@
let expr = result.into_expression(self.elaborator.interner, location)?;
let expr = self
.elaborator
.elaborate_item_from_comptime(self.current_function, |elab| {

Check warning on line 1238 in compiler/noirc_frontend/src/hir/comptime/interpreter.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (elab)
elab.elaborate_expression(expr).0

Check warning on line 1239 in compiler/noirc_frontend/src/hir/comptime/interpreter.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (elab)
});
result = self.evaluate(expr)?;
}
Expand Down
7 changes: 7 additions & 0 deletions test_programs/execution_success/regression_5615/Nargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "regression_5615"
type = "bin"
authors = [""]
compiler_version = ">=0.32.0"

[dependencies]
12 changes: 12 additions & 0 deletions test_programs/execution_success/regression_5615/src/main.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use std::collections::umap::UHashMap;
use std::hash::BuildHasherDefault;
use std::hash::poseidon2::Poseidon2Hasher;

unconstrained fn main() {
comptime
{
let mut map: UHashMap<u32, Field, BuildHasherDefault<Poseidon2Hasher>> = UHashMap::default();

map.insert(1, 2);
}
}
Loading