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

chore: Move turbofish changes to the elaborator #5094

Merged
merged 5 commits into from
May 23, 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
33 changes: 22 additions & 11 deletions compiler/noirc_frontend/src/elaborator/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ impl<'context> Elaborator<'context> {
let expr_id = self.interner.push_expr(ident);
self.interner.push_expr_location(expr_id, call_expr_span, self.file);
let ident = old_value.ident.clone();
let typ = self.type_check_variable(ident, expr_id);
let typ = self.type_check_variable(ident, expr_id, None);
self.interner.push_expr_type(expr_id, typ.clone());
capture_types.push(typ);
fmt_str_idents.push(expr_id);
Expand Down Expand Up @@ -291,16 +291,25 @@ impl<'context> Elaborator<'context> {
Some(method_ref) => {
// Automatically add `&mut` if the method expects a mutable reference and
// the object is not already one.
if let HirMethodReference::FuncId(func_id) = &method_ref {
if *func_id != FuncId::dummy_id() {
let function_type = self.interner.function_meta(func_id).typ.clone();

self.try_add_mutable_reference_to_object(
&function_type,
&mut object_type,
&mut object,
);
let func_id = match &method_ref {
HirMethodReference::FuncId(func_id) => *func_id,
HirMethodReference::TraitMethodId(method_id, _) => {
let id = self.interner.trait_method_id(*method_id);
let definition = self.interner.definition(id);
let DefinitionKind::Function(func_id) = definition.kind else {
unreachable!("Expected trait function to be a DefinitionKind::Function")
};
func_id
}
};

if func_id != FuncId::dummy_id() {
let function_type = self.interner.function_meta(&func_id).typ.clone();
self.try_add_mutable_reference_to_object(
&function_type,
&mut object_type,
&mut object,
);
}

// These arguments will be given to the desugared function call.
Expand All @@ -322,6 +331,7 @@ impl<'context> Elaborator<'context> {
let generics = method_call.generics.map(|option_inner| {
option_inner.into_iter().map(|generic| self.resolve_type(generic)).collect()
});
let turbofish_generics = generics.clone();
let method_call =
HirMethodCallExpression { method, object, arguments, location, generics };

Expand All @@ -335,7 +345,8 @@ impl<'context> Elaborator<'context> {
self.interner,
);

let func_type = self.type_check_variable(function_name, function_id);
let func_type =
self.type_check_variable(function_name, function_id, turbofish_generics);

// Type check the new call now that it has been changed from a method call
// to a function call. This way we avoid duplicating code.
Expand Down
70 changes: 65 additions & 5 deletions compiler/noirc_frontend/src/elaborator/patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use rustc_hash::FxHashSet as HashSet;
use crate::{
ast::ERROR_IDENT,
hir::{
def_collector::dc_crate::CompilationError,
resolution::errors::ResolverError,
type_check::{Source, TypeCheckError},
},
Expand Down Expand Up @@ -330,9 +331,9 @@ impl<'context> Elaborator<'context> {
) -> (ExprId, Type) {
let span = variable.span;
let expr = self.resolve_variable(variable);
let id = self.interner.push_expr(HirExpression::Ident(expr.clone(), generics));
let id = self.interner.push_expr(HirExpression::Ident(expr.clone(), generics.clone()));
self.interner.push_expr_location(id, span, self.file);
let typ = self.type_check_variable(expr, id);
let typ = self.type_check_variable(expr, id, generics);
self.interner.push_expr_type(id, typ.clone());
(id, typ)
}
Expand Down Expand Up @@ -384,14 +385,19 @@ impl<'context> Elaborator<'context> {
}
}

pub(super) fn type_check_variable(&mut self, ident: HirIdent, expr_id: ExprId) -> Type {
pub(super) fn type_check_variable(
&mut self,
ident: HirIdent,
expr_id: ExprId,
generics: Option<Vec<Type>>,
) -> Type {
let mut bindings = TypeBindings::new();

// Add type bindings from any constraints that were used.
// We need to do this first since otherwise instantiating the type below
// will replace each trait generic with a fresh type variable, rather than
// the type used in the trait constraint (if it exists). See #4088.
if let ImplKind::TraitMethod(_, constraint, _) = &ident.impl_kind {
if let ImplKind::TraitMethod(_, constraint, assumed) = &ident.impl_kind {
let the_trait = self.interner.get_trait(constraint.trait_id);
assert_eq!(the_trait.generics.len(), constraint.trait_generics.len());

Expand All @@ -401,6 +407,16 @@ impl<'context> Elaborator<'context> {
bindings.insert(param.id(), (param.clone(), arg.clone()));
}
}

// If the trait impl is already assumed to exist we should add any type bindings for `Self`.
// Otherwise `self` will be replaced with a fresh type variable, which will require the user
// to specify a redundant type annotation.
if *assumed {
bindings.insert(
the_trait.self_type_typevar_id,
(the_trait.self_type_typevar.clone(), constraint.typ.clone()),
);
}
}

// An identifiers type may be forall-quantified in the case of generic functions.
Expand All @@ -409,10 +425,21 @@ impl<'context> Elaborator<'context> {
// variable to handle generic functions.
let t = self.interner.id_type_substitute_trait_as_type(ident.id);

let definition = self.interner.try_definition(ident.id);
let function_generic_count = definition.map_or(0, |definition| match &definition.kind {
DefinitionKind::Function(function) => {
self.interner.function_modifiers(function).generic_count
}
_ => 0,
});

let span = self.interner.expr_span(&expr_id);
let location = self.interner.expr_location(&expr_id);
// This instantiates a trait's generics as well which need to be set
// when the constraint below is later solved for when the function is
// finished. How to link the two?
let (typ, bindings) = t.instantiate_with_bindings(bindings, self.interner);
let (typ, bindings) =
self.instantiate(t, bindings, generics, function_generic_count, span, location);

// Push any trait constraints required by this definition to the context
// to be checked later when the type of this variable is further constrained.
Expand Down Expand Up @@ -447,6 +474,39 @@ impl<'context> Elaborator<'context> {
typ
}

fn instantiate(
&mut self,
typ: Type,
bindings: TypeBindings,
turbofish_generics: Option<Vec<Type>>,
function_generic_count: usize,
span: Span,
location: Location,
) -> (Type, TypeBindings) {
match turbofish_generics {
Some(turbofish_generics) => {
if turbofish_generics.len() != function_generic_count {
let type_check_err = TypeCheckError::IncorrectTurbofishGenericCount {
expected_count: function_generic_count,
actual_count: turbofish_generics.len(),
span,
};
self.errors.push((CompilationError::TypeError(type_check_err), location.file));
typ.instantiate_with_bindings(bindings, self.interner)
} else {
// Fetch the count of any implicit generics on the function, such as
// for a method within a generic impl.
let implicit_generic_count = match &typ {
Type::Forall(generics, _) => generics.len() - function_generic_count,
_ => 0,
};
typ.instantiate_with(turbofish_generics, self.interner, implicit_generic_count)
}
}
None => typ.instantiate_with_bindings(bindings, self.interner),
}
}

fn get_ident_from_path(&mut self, path: Path) -> (HirIdent, usize) {
let location = Location::new(path.span(), self.file);

Expand Down
3 changes: 1 addition & 2 deletions compiler/noirc_frontend/src/hir/type_check/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,8 +399,6 @@ impl<'interner> TypeChecker<'interner> {
// variable to handle generic functions.
let t = self.interner.id_type_substitute_trait_as_type(ident.id);

let span = self.interner.expr_span(expr_id);

let definition = self.interner.try_definition(ident.id);
let function_generic_count = definition.map_or(0, |definition| match &definition.kind {
DefinitionKind::Function(function) => {
Expand All @@ -409,6 +407,7 @@ impl<'interner> TypeChecker<'interner> {
_ => 0,
});

let span = self.interner.expr_span(expr_id);
// This instantiates a trait's generics as well which need to be set
// when the constraint below is later solved for when the function is
// finished. How to link the two?
Expand Down
Loading