Skip to content
Merged
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 @@ -1377,8 +1377,8 @@ impl Elaborator<'_> {
},
};

let typ = self.resolve_type(constraint.typ.clone());
let Some(trait_bound) = self.resolve_trait_bound(&constraint.trait_bound) else {
let typ = self.use_type(constraint.typ.clone());
let Some(trait_bound) = self.use_trait_bound(&constraint.trait_bound) else {
// resolve_trait_bound only returns None if it has already issued an error, so don't
// issue another here.
let error = self.interner.push_expr_full(HirExpression::Error, location, Type::Error);
Expand Down
24 changes: 18 additions & 6 deletions compiler/noirc_frontend/src/elaborator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ use noirc_errors::{Located, Location};
pub(crate) use options::ElaboratorOptions;
pub use options::{FrontendOptions, UnstableFeature};
pub use path_resolution::Turbofish;
use path_resolution::{PathResolution, PathResolutionItem};
use path_resolution::{PathResolution, PathResolutionItem, PathResolutionMode};
use types::bind_ordered_generics;

use self::traits::check_trait_impl_method_matches_declaration;
Expand Down Expand Up @@ -745,7 +745,7 @@ impl<'context> Elaborator<'context> {
if let UnresolvedGeneric::Numeric { ident, typ } = generic {
let unresolved_typ = typ.clone();
let typ = if unresolved_typ.is_type_expression() {
self.resolve_type_inner(
self.resolve_type_with_kind(
unresolved_typ.clone(),
&Kind::numeric(Type::default_int_type()),
)
Expand Down Expand Up @@ -922,12 +922,24 @@ impl<'context> Elaborator<'context> {
}

pub fn resolve_trait_bound(&mut self, bound: &TraitBound) -> Option<ResolvedTraitBound> {
self.resolve_trait_bound_inner(bound, PathResolutionMode::MarkAsReferenced)
}

pub fn use_trait_bound(&mut self, bound: &TraitBound) -> Option<ResolvedTraitBound> {
self.resolve_trait_bound_inner(bound, PathResolutionMode::MarkAsUsed)
}

fn resolve_trait_bound_inner(
&mut self,
bound: &TraitBound,
mode: PathResolutionMode,
) -> Option<ResolvedTraitBound> {
let the_trait = self.lookup_trait_or_error(bound.trait_path.clone())?;
let trait_id = the_trait.id;
let location = bound.trait_path.location;

let (ordered, named) =
self.resolve_type_args(bound.trait_generics.clone(), trait_id, location);
self.resolve_type_args_inner(bound.trait_generics.clone(), trait_id, location, mode);

let trait_generics = TraitGenerics { ordered, named };
Some(ResolvedTraitBound { trait_id, trait_generics, location })
Expand Down Expand Up @@ -995,7 +1007,7 @@ impl<'context> Elaborator<'context> {
self.desugar_impl_trait_arg(path, args, &mut generics, &mut trait_constraints)
}
// Function parameters have Kind::Normal
_ => self.resolve_type_inner(typ, &Kind::Normal),
_ => self.resolve_type_with_kind(typ, &Kind::Normal),
};

self.check_if_type_is_valid_for_program_input(
Expand All @@ -1021,7 +1033,7 @@ impl<'context> Elaborator<'context> {
parameter_types.push(typ);
}

let return_type = Box::new(self.resolve_type(func.return_type()));
let return_type = Box::new(self.use_type(func.return_type()));

let mut typ = Type::Function(
parameter_types,
Expand Down Expand Up @@ -1690,7 +1702,7 @@ impl<'context> Elaborator<'context> {

let generics = self.add_generics(&alias.type_alias_def.generics);
self.current_item = Some(DependencyId::Alias(alias_id));
let typ = self.resolve_type(alias.type_alias_def.typ);
let typ = self.use_type(alias.type_alias_def.typ);

if visibility != ItemVisibility::Private {
self.check_type_is_not_more_private_then_item(name, visibility, &typ, location);
Expand Down
87 changes: 81 additions & 6 deletions compiler/noirc_frontend/src/elaborator/path_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,59 @@ enum MethodLookupResult {
FoundMultipleTraitMethods(Vec<(TraitId, Ident)>),
}

/// Determines whether datatypes found along a path are to be marked as referenced
/// or used (see [`crate::usage_tracker::UsageTracker::mark_as_referenced`]
/// and [`crate::usage_tracker::UsageTracker::mark_as_used`])
///
/// For example, a struct `Foo` won't be marked as used (just as referenced) if it
/// mentioned in a function parameter:
///
/// ```noir
/// fn method(foo: Foo) {}
/// ```
///
/// However, if it's used in a return type it will be marked as used, even if
/// it's not explicitly constructed:
///
/// ```noir
/// fn method() -> Foo {
/// std::mem::zeroed()
/// }
/// ```
///
/// Or, for example, a struct used in a impl or trait impl won't be marked as used:
///
/// ```noir
/// impl Foo {}
/// impl Trait for Foo {}
/// ```
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(super) enum PathResolutionMode {
MarkAsReferenced,
MarkAsUsed,
}

impl Elaborator<'_> {
pub(super) fn resolve_path_or_error(
&mut self,
path: Path,
) -> Result<PathResolutionItem, ResolverError> {
let path_resolution = self.resolve_path(path)?;
self.resolve_path_or_error_inner(path, PathResolutionMode::MarkAsReferenced)
}

pub(super) fn use_path_or_error(
&mut self,
path: Path,
) -> Result<PathResolutionItem, ResolverError> {
self.resolve_path_or_error_inner(path, PathResolutionMode::MarkAsUsed)
}

pub(super) fn resolve_path_or_error_inner(
&mut self,
path: Path,
mode: PathResolutionMode,
) -> Result<PathResolutionItem, ResolverError> {
let path_resolution = self.resolve_path_inner(path, mode)?;

for error in path_resolution.errors {
self.push_err(error);
Expand All @@ -124,11 +171,23 @@ impl Elaborator<'_> {
Ok(path_resolution.item)
}

pub(super) fn resolve_path(&mut self, path: Path) -> PathResolutionResult {
self.resolve_path_inner(path, PathResolutionMode::MarkAsReferenced)
}

pub(super) fn use_path(&mut self, path: Path) -> PathResolutionResult {
self.resolve_path_inner(path, PathResolutionMode::MarkAsUsed)
}

/// Resolves a path in the current module.
/// If the referenced name can't be found, `Err` will be returned. If it can be found, `Ok`
/// will be returned with a potential list of errors if, for example, one of the segments
/// is not accessible from the current module (e.g. because it's private).
pub(super) fn resolve_path(&mut self, mut path: Path) -> PathResolutionResult {
pub(super) fn resolve_path_inner(
&mut self,
mut path: Path,
mode: PathResolutionMode,
) -> PathResolutionResult {
let mut module_id = self.module_id();
let mut intermediate_item = IntermediatePathResolutionItem::Module;

Expand All @@ -148,7 +207,7 @@ impl Elaborator<'_> {
}
}

self.resolve_path_in_module(path, module_id, intermediate_item)
self.resolve_path_in_module(path, module_id, intermediate_item, mode)
}

/// Resolves a path in `current_module`.
Expand All @@ -158,6 +217,7 @@ impl Elaborator<'_> {
path: Path,
importing_module: ModuleId,
intermediate_item: IntermediatePathResolutionItem,
mode: PathResolutionMode,
) -> PathResolutionResult {
let references_tracker = if self.interner.is_in_lsp_mode() {
Some(ReferencesTracker::new(self.interner))
Expand All @@ -166,7 +226,7 @@ impl Elaborator<'_> {
};
let (path, module_id, _) =
resolve_path_kind(path, importing_module, self.def_maps, references_tracker)?;
self.resolve_name_in_module(path, module_id, importing_module, intermediate_item)
self.resolve_name_in_module(path, module_id, importing_module, intermediate_item, mode)
}

/// Resolves a Path assuming we are inside `starting_module`.
Expand All @@ -177,6 +237,7 @@ impl Elaborator<'_> {
starting_module: ModuleId,
importing_module: ModuleId,
mut intermediate_item: IntermediatePathResolutionItem,
mode: PathResolutionMode,
) -> PathResolutionResult {
// There is a possibility that the import path is empty. In that case, early return.
if path.segments.is_empty() {
Expand All @@ -199,7 +260,14 @@ impl Elaborator<'_> {
return Err(PathResolutionError::Unresolved(first_segment.clone()));
}

self.usage_tracker.mark_as_referenced(current_module_id, first_segment);
match mode {
PathResolutionMode::MarkAsReferenced => {
self.usage_tracker.mark_as_referenced(current_module_id, first_segment);
}
PathResolutionMode::MarkAsUsed => {
self.usage_tracker.mark_as_used(current_module_id, first_segment);
}
}

let mut errors = Vec::new();
for (index, (last_segment, current_segment)) in
Expand Down Expand Up @@ -323,7 +391,14 @@ impl Elaborator<'_> {
return Err(PathResolutionError::Unresolved(current_ident.clone()));
}

self.usage_tracker.mark_as_referenced(current_module_id, current_ident);
match mode {
PathResolutionMode::MarkAsReferenced => {
self.usage_tracker.mark_as_referenced(current_module_id, current_ident);
}
PathResolutionMode::MarkAsUsed => {
self.usage_tracker.mark_as_used(current_module_id, current_ident);
}
}

current_ns = found_ns;
}
Expand Down
6 changes: 3 additions & 3 deletions compiler/noirc_frontend/src/elaborator/patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,7 @@ impl Elaborator<'_> {
) -> Vec<Type> {
let kinds_with_types = kinds.into_iter().zip(turbofish_generics);
vecmap(kinds_with_types, |(kind, unresolved_type)| {
self.resolve_type_inner(unresolved_type, &kind)
self.use_type_with_kind(unresolved_type, &kind)
})
}

Expand Down Expand Up @@ -904,7 +904,7 @@ impl Elaborator<'_> {
pub(super) fn elaborate_type_path(&mut self, path: TypePath) -> (ExprId, Type) {
let typ_location = path.typ.location;
let turbofish = path.turbofish;
let typ = self.resolve_type(path.typ);
let typ = self.use_type(path.typ);
self.elaborate_type_path_impl(typ, path.item, turbofish, typ_location)
}

Expand Down Expand Up @@ -936,7 +936,7 @@ impl Elaborator<'_> {
.expect("Expected trait function to be a DefinitionKind::Function");

let generics =
turbofish.map(|turbofish| self.resolve_type_args(turbofish, func_id, ident_location).0);
turbofish.map(|turbofish| self.use_type_args(turbofish, func_id, ident_location).0);

let id = self.interner.function_definition_id(func_id);

Expand Down
22 changes: 15 additions & 7 deletions compiler/noirc_frontend/src/elaborator/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::{
};
use crate::{Type, TypeAlias};

use super::path_resolution::PathResolutionItem;
use super::path_resolution::{PathResolutionItem, PathResolutionMode};
use super::types::SELF_TYPE_NAME;
use super::{Elaborator, ResolverMeta};

Expand Down Expand Up @@ -82,7 +82,7 @@ impl Elaborator<'_> {
path: Path,
) -> Result<(DefinitionId, PathResolutionItem), ResolverError> {
let location = path.location;
let item = self.resolve_path_or_error(path)?;
let item = self.use_path_or_error(path)?;

if let Some(function) = item.function_id() {
return Ok((self.interner.function_definition_id(function), item));
Expand Down Expand Up @@ -158,9 +158,13 @@ impl Elaborator<'_> {
}

/// Lookup a given struct type by name.
pub fn lookup_datatype_or_error(&mut self, path: Path) -> Option<Shared<DataType>> {
pub(super) fn lookup_datatype_or_error(
&mut self,
path: Path,
mode: PathResolutionMode,
) -> Option<Shared<DataType>> {
let location = path.location;
match self.resolve_path_or_error(path) {
match self.resolve_path_or_error_inner(path, mode) {
Ok(item) => {
if let PathResolutionItem::Type(struct_id) = item {
Some(self.get_type(struct_id))
Expand Down Expand Up @@ -191,7 +195,7 @@ impl Elaborator<'_> {
}

let location = path.location;
match self.resolve_path_or_error(path) {
match self.use_path_or_error(path) {
Ok(PathResolutionItem::Type(struct_id)) => {
let struct_type = self.get_type(struct_id);
let generics = struct_type.borrow().instantiate(self.interner);
Expand All @@ -217,8 +221,12 @@ impl Elaborator<'_> {
}
}

pub fn lookup_type_alias(&mut self, path: Path) -> Option<Shared<TypeAlias>> {
match self.resolve_path_or_error(path) {
pub(super) fn lookup_type_alias(
&mut self,
path: Path,
mode: PathResolutionMode,
) -> Option<Shared<TypeAlias>> {
match self.resolve_path_or_error_inner(path, mode) {
Ok(PathResolutionItem::TypeAlias(type_alias_id)) => {
Some(self.interner.get_type_alias(type_alias_id))
}
Expand Down
Loading
Loading