Skip to content

Commit

Permalink
chore: Remove the remainder of legacy code (#5525)
Browse files Browse the repository at this point in the history
# Description

## Problem\*

## Summary\*

Removes the remainder of legacy code: the Resolver and the TypeChecker.

## Additional Context

Removing this bit required updating aztec_macros a bit to use the
elaborator instead of the resolver/type checker when inserting the code,
hence why this was split into a separate PR. The change should be minor.

## Documentation\*

Check one:
- [x] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist\*

- [x] I have tested the changes locally.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
  • Loading branch information
jfecher authored Jul 16, 2024
1 parent e30b347 commit 0319be2
Show file tree
Hide file tree
Showing 15 changed files with 319 additions and 4,864 deletions.
42 changes: 14 additions & 28 deletions aztec_macros/src/utils/hir_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ use acvm::acir::AcirField;
use iter_extended::vecmap;
use noirc_errors::Location;
use noirc_frontend::ast;
use noirc_frontend::elaborator::Elaborator;
use noirc_frontend::hir::def_collector::dc_crate::{
CollectedItems, UnresolvedFunctions, UnresolvedGlobal,
};
use noirc_frontend::macros_api::{HirExpression, HirLiteral};
use noirc_frontend::node_interner::{NodeInterner, TraitImplKind};
use noirc_frontend::{
graph::CrateId,
hir::{
def_map::{LocalModuleId, ModuleId},
resolution::{path_resolver::StandardPathResolver, resolver::Resolver},
type_check::type_check_func,
},
hir::def_map::{LocalModuleId, ModuleId},
macros_api::{FileId, HirContext, MacroError, ModuleDefId, StructId},
node_interner::{FuncId, TraitId},
Shared, StructType, Type,
Expand Down Expand Up @@ -190,24 +190,17 @@ pub fn inject_fn(
span: None,
})?;

let def_maps = &mut context.def_maps;

let path_resolver =
StandardPathResolver::new(ModuleId { local_id: module_id, krate: *crate_id });

let resolver = Resolver::new(&mut context.def_interner, &path_resolver, def_maps, file_id);

let (hir_func, meta, _) = resolver.resolve_function(func, func_id);
let mut items = CollectedItems::default();
let functions = vec![(module_id, func_id, func)];
let trait_id = None;
items.functions.push(UnresolvedFunctions { file_id, functions, trait_id, self_type: None });

context.def_interner.push_fn_meta(meta, func_id);
context.def_interner.update_fn(func_id, hir_func);

let errors = type_check_func(&mut context.def_interner, func_id);
let errors = Elaborator::elaborate(context, *crate_id, items, None);

if !errors.is_empty() {
return Err(MacroError {
primary_message: "Failed to type check autogenerated function".to_owned(),
secondary_message: Some(errors.iter().map(|err| err.to_string()).collect::<String>()),
secondary_message: Some(errors.iter().map(|err| err.0.to_string()).collect::<String>()),
span: None,
});
}
Expand Down Expand Up @@ -243,17 +236,10 @@ pub fn inject_global(
)
});

let def_maps = &mut context.def_maps;

let path_resolver =
StandardPathResolver::new(ModuleId { local_id: module_id, krate: *crate_id });

let mut resolver = Resolver::new(&mut context.def_interner, &path_resolver, def_maps, file_id);

let hir_stmt = resolver.resolve_global_let(global, global_id);
let mut items = CollectedItems::default();
items.globals.push(UnresolvedGlobal { file_id, module_id, global_id, stmt_def: global });

let statement_id = context.def_interner.get_global(global_id).let_statement;
context.def_interner.replace_statement(statement_id, hir_stmt);
let _errors = Elaborator::elaborate(context, *crate_id, items, None);
}

pub fn fully_qualified_note_path(context: &HirContext, note_id: StructId) -> Option<String> {
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/ast/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use super::{
BlockExpression, Expression, ExpressionKind, IndexExpression, MemberAccessExpression,
MethodCallExpression, UnresolvedType,
};
use crate::hir::resolution::resolver::SELF_TYPE_NAME;
use crate::elaborator::types::SELF_TYPE_NAME;
use crate::lexer::token::SpannedToken;
use crate::macros_api::SecondaryAttribute;
use crate::parser::{ParserError, ParserErrorReason};
Expand Down
4 changes: 2 additions & 2 deletions compiler/noirc_frontend/src/elaborator/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
},
hir::{
comptime::{self, InterpreterError},
resolution::{errors::ResolverError, resolver::LambdaContext},
resolution::errors::ResolverError,
type_check::TypeCheckError,
},
hir_def::{
Expand All @@ -32,7 +32,7 @@ use crate::{
QuotedType, Shared, StructType, Type,
};

use super::Elaborator;
use super::{Elaborator, LambdaContext};

impl<'context> Elaborator<'context> {
pub(super) fn elaborate_expression(&mut self, expr: Expression) -> (ExprId, Type) {
Expand Down
17 changes: 13 additions & 4 deletions compiler/noirc_frontend/src/elaborator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ use crate::{
dc_mod,
errors::DuplicateType,
},
resolution::{errors::ResolverError, path_resolver::PathResolver, resolver::LambdaContext},
resolution::{errors::ResolverError, path_resolver::PathResolver},
scope::ScopeForest as GenericScopeForest,
type_check::{check_trait_impl_method_matches_declaration, TypeCheckError},
type_check::TypeCheckError,
},
hir_def::{
expr::HirIdent,
expr::{HirCapturedVar, HirIdent},
function::{FunctionBody, Parameters},
traits::TraitConstraint,
types::{Generics, Kind, ResolvedGeneric},
Expand Down Expand Up @@ -67,14 +67,16 @@ mod patterns;
mod scope;
mod statements;
mod traits;
mod types;
pub mod types;
mod unquote;

use fm::FileId;
use iter_extended::vecmap;
use noirc_errors::{Location, Span};
use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};

use self::traits::check_trait_impl_method_matches_declaration;

/// ResolverMetas are tagged onto each definition to track how many times they are used
#[derive(Debug, PartialEq, Eq)]
pub struct ResolverMeta {
Expand All @@ -85,6 +87,13 @@ pub struct ResolverMeta {

type ScopeForest = GenericScopeForest<String, ResolverMeta>;

pub struct LambdaContext {
pub captures: Vec<HirCapturedVar>,
/// the index in the scope tree
/// (sometimes being filled by ScopeTree's find method)
pub scope_index: usize,
}

pub struct Elaborator<'context> {
scopes: ScopeForest,

Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/elaborator/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use noirc_errors::{Location, Spanned};
use crate::ast::ERROR_IDENT;
use crate::hir::def_map::{LocalModuleId, ModuleId};
use crate::hir::resolution::path_resolver::{PathResolver, StandardPathResolver};
use crate::hir::resolution::resolver::SELF_TYPE_NAME;
use crate::hir::scope::{Scope as GenericScope, ScopeTree as GenericScopeTree};
use crate::macros_api::Ident;
use crate::{
Expand All @@ -21,6 +20,7 @@ use crate::{
};
use crate::{Type, TypeAlias};

use super::types::SELF_TYPE_NAME;
use super::{Elaborator, ResolverMeta};

type Scope = GenericScope<String, ResolverMeta>;
Expand Down
172 changes: 167 additions & 5 deletions compiler/noirc_frontend/src/elaborator/traits.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
use std::{collections::BTreeMap, rc::Rc};

use iter_extended::vecmap;
use noirc_errors::Location;
use noirc_errors::{Location, Span};

use crate::{
ast::{
FunctionKind, TraitItem, UnresolvedGeneric, UnresolvedGenerics, UnresolvedTraitConstraint,
},
hir::def_collector::dc_crate::{CollectedItems, UnresolvedTrait},
hir_def::traits::{TraitConstant, TraitFunction, TraitType},
hir::{
def_collector::dc_crate::{CollectedItems, UnresolvedTrait},
type_check::TypeCheckError,
},
hir_def::{
function::Parameters,
traits::{TraitConstant, TraitFunction, TraitType},
},
macros_api::{
BlockExpression, FunctionDefinition, FunctionReturnType, Ident, ItemVisibility,
NoirFunction, Param, Pattern, UnresolvedType, Visibility,
NodeInterner, NoirFunction, Param, Pattern, UnresolvedType, Visibility,
},
node_interner::{FuncId, TraitId},
token::Attributes,
Kind, ResolvedGeneric, Type, TypeVariableKind,
Kind, ResolvedGeneric, Type, TypeBindings, TypeVariableKind,
};

use super::Elaborator;
Expand Down Expand Up @@ -204,3 +210,159 @@ impl<'context> Elaborator<'context> {
self.generics.truncate(old_generic_count);
}
}

/// Checks that the type of a function in a trait impl matches the type
/// of the corresponding function declaration in the trait itself.
///
/// To do this, given a trait such as:
/// `trait Foo<A> { fn foo<B>(...); }`
///
/// And an impl such as:
/// `impl<C> Foo<D> for Bar<E> { fn foo<F>(...); } `
///
/// We have to substitute:
/// - Self for Bar<E>
/// - A for D
/// - B for F
///
/// Before we can type check. Finally, we must also check that the unification
/// result does not introduce any new bindings. This can happen if the impl
/// function's type is more general than that of the trait function. E.g.
/// `fn baz<A, B>(a: A, b: B)` when the impl required `fn baz<A>(a: A, b: A)`.
///
/// This does not type check the body of the impl function.
pub(crate) fn check_trait_impl_method_matches_declaration(
interner: &mut NodeInterner,
function: FuncId,
) -> Vec<TypeCheckError> {
let meta = interner.function_meta(&function);
let method_name = interner.function_name(&function);
let mut errors = Vec::new();

let definition_type = meta.typ.as_monotype();

let impl_ =
meta.trait_impl.expect("Trait impl function should have a corresponding trait impl");

// If the trait implementation is not defined in the interner then there was a previous
// error in resolving the trait path and there is likely no trait for this impl.
let Some(impl_) = interner.try_get_trait_implementation(impl_) else {
return errors;
};

let impl_ = impl_.borrow();
let trait_info = interner.get_trait(impl_.trait_id);

let mut bindings = TypeBindings::new();
bindings.insert(
trait_info.self_type_typevar_id,
(trait_info.self_type_typevar.clone(), impl_.typ.clone()),
);

if trait_info.generics.len() != impl_.trait_generics.len() {
let expected = trait_info.generics.len();
let found = impl_.trait_generics.len();
let span = impl_.ident.span();
let item = trait_info.name.to_string();
errors.push(TypeCheckError::GenericCountMismatch { item, expected, found, span });
}

// Substitute each generic on the trait with the corresponding generic on the impl
for (generic, arg) in trait_info.generics.iter().zip(&impl_.trait_generics) {
bindings.insert(generic.type_var.id(), (generic.type_var.clone(), arg.clone()));
}

// If this is None, the trait does not have the corresponding function.
// This error should have been caught in name resolution already so we don't
// issue an error for it here.
if let Some(trait_fn_id) = trait_info.method_ids.get(method_name) {
let trait_fn_meta = interner.function_meta(trait_fn_id);

if trait_fn_meta.direct_generics.len() != meta.direct_generics.len() {
let expected = trait_fn_meta.direct_generics.len();
let found = meta.direct_generics.len();
let span = meta.name.location.span;
let item = method_name.to_string();
errors.push(TypeCheckError::GenericCountMismatch { item, expected, found, span });
}

// Substitute each generic on the trait function with the corresponding generic on the impl function
for (
ResolvedGeneric { type_var: trait_fn_generic, .. },
ResolvedGeneric { name, type_var: impl_fn_generic, .. },
) in trait_fn_meta.direct_generics.iter().zip(&meta.direct_generics)
{
let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone(), Kind::Normal);
bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), arg));
}

let (declaration_type, _) = trait_fn_meta.typ.instantiate_with_bindings(bindings, interner);

check_function_type_matches_expected_type(
&declaration_type,
definition_type,
method_name,
&meta.parameters,
meta.name.location.span,
&trait_info.name.0.contents,
&mut errors,
);
}

errors
}

fn check_function_type_matches_expected_type(
expected: &Type,
actual: &Type,
method_name: &str,
actual_parameters: &Parameters,
span: Span,
trait_name: &str,
errors: &mut Vec<TypeCheckError>,
) {
let mut bindings = TypeBindings::new();
// Shouldn't need to unify envs, they should always be equal since they're both free functions
if let (Type::Function(params_a, ret_a, _env_a), Type::Function(params_b, ret_b, _env_b)) =
(expected, actual)
{
if params_a.len() == params_b.len() {
for (i, (a, b)) in params_a.iter().zip(params_b.iter()).enumerate() {
if a.try_unify(b, &mut bindings).is_err() {
errors.push(TypeCheckError::TraitMethodParameterTypeMismatch {
method_name: method_name.to_string(),
expected_typ: a.to_string(),
actual_typ: b.to_string(),
parameter_span: actual_parameters.0[i].0.span(),
parameter_index: i + 1,
});
}
}

if ret_b.try_unify(ret_a, &mut bindings).is_err() {
errors.push(TypeCheckError::TypeMismatch {
expected_typ: ret_a.to_string(),
expr_typ: ret_b.to_string(),
expr_span: span,
});
}
} else {
errors.push(TypeCheckError::MismatchTraitImplNumParameters {
actual_num_parameters: params_b.len(),
expected_num_parameters: params_a.len(),
trait_name: trait_name.to_string(),
method_name: method_name.to_string(),
span,
});
}
}

// If result bindings is not empty, a type variable was bound which means the two
// signatures were not a perfect match. Note that this relies on us already binding
// all the expected generics to each other prior to this check.
if !bindings.is_empty() {
let expected_typ = expected.to_string();
let expr_typ = actual.to_string();
errors.push(TypeCheckError::TypeMismatch { expected_typ, expr_typ, expr_span: span });
}
}
Loading

0 comments on commit 0319be2

Please sign in to comment.