diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index aa1fb7b85e3..74e5dea1797 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -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); diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 5bf90baadd5..23b776e4b18 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -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; @@ -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()), ) @@ -922,12 +922,24 @@ impl<'context> Elaborator<'context> { } pub fn resolve_trait_bound(&mut self, bound: &TraitBound) -> Option { + self.resolve_trait_bound_inner(bound, PathResolutionMode::MarkAsReferenced) + } + + pub fn use_trait_bound(&mut self, bound: &TraitBound) -> Option { + self.resolve_trait_bound_inner(bound, PathResolutionMode::MarkAsUsed) + } + + fn resolve_trait_bound_inner( + &mut self, + bound: &TraitBound, + mode: PathResolutionMode, + ) -> Option { 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 }) @@ -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( @@ -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, @@ -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); diff --git a/compiler/noirc_frontend/src/elaborator/path_resolution.rs b/compiler/noirc_frontend/src/elaborator/path_resolution.rs index a5bfdcd72dd..f1428b09dbc 100644 --- a/compiler/noirc_frontend/src/elaborator/path_resolution.rs +++ b/compiler/noirc_frontend/src/elaborator/path_resolution.rs @@ -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 { - 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 { + self.resolve_path_or_error_inner(path, PathResolutionMode::MarkAsUsed) + } + + pub(super) fn resolve_path_or_error_inner( + &mut self, + path: Path, + mode: PathResolutionMode, + ) -> Result { + let path_resolution = self.resolve_path_inner(path, mode)?; for error in path_resolution.errors { self.push_err(error); @@ -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; @@ -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`. @@ -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)) @@ -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`. @@ -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() { @@ -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 @@ -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; } diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 04740dd4074..1e29d8db9bc 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -531,7 +531,7 @@ impl Elaborator<'_> { ) -> Vec { 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) }) } @@ -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) } @@ -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); diff --git a/compiler/noirc_frontend/src/elaborator/scope.rs b/compiler/noirc_frontend/src/elaborator/scope.rs index 307be03c1bd..942785964c3 100644 --- a/compiler/noirc_frontend/src/elaborator/scope.rs +++ b/compiler/noirc_frontend/src/elaborator/scope.rs @@ -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}; @@ -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)); @@ -158,9 +158,13 @@ impl Elaborator<'_> { } /// Lookup a given struct type by name. - pub fn lookup_datatype_or_error(&mut self, path: Path) -> Option> { + pub(super) fn lookup_datatype_or_error( + &mut self, + path: Path, + mode: PathResolutionMode, + ) -> Option> { 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)) @@ -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); @@ -217,8 +221,12 @@ impl Elaborator<'_> { } } - pub fn lookup_type_alias(&mut self, path: Path) -> Option> { - match self.resolve_path_or_error(path) { + pub(super) fn lookup_type_alias( + &mut self, + path: Path, + mode: PathResolutionMode, + ) -> Option> { + match self.resolve_path_or_error_inner(path, mode) { Ok(PathResolutionItem::TypeAlias(type_alias_id)) => { Some(self.interner.get_type_alias(type_alias_id)) } diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index e7c008b1bba..27f5bbd2459 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -41,7 +41,8 @@ use crate::{ }; use super::{ - Elaborator, FunctionContext, UnsafeBlockStatus, lints, path_resolution::PathResolutionItem, + Elaborator, FunctionContext, UnsafeBlockStatus, lints, + path_resolution::{PathResolutionItem, PathResolutionMode}, }; pub const SELF_TYPE_NAME: &str = "Self"; @@ -53,19 +54,40 @@ pub(super) struct TraitPathResolution { } impl Elaborator<'_> { - /// Translates an UnresolvedType to a Type with a `TypeKind::Normal` pub(crate) fn resolve_type(&mut self, typ: UnresolvedType) -> Type { + self.resolve_type_inner(typ, PathResolutionMode::MarkAsReferenced) + } + + pub(crate) fn use_type(&mut self, typ: UnresolvedType) -> Type { + self.resolve_type_inner(typ, PathResolutionMode::MarkAsUsed) + } + + /// Translates an UnresolvedType to a Type with a `TypeKind::Normal` + fn resolve_type_inner(&mut self, typ: UnresolvedType, mode: PathResolutionMode) -> Type { let location = typ.location; - let resolved_type = self.resolve_type_inner(typ, &Kind::Normal); + let resolved_type = self.resolve_type_with_kind_inner(typ, &Kind::Normal, mode); if resolved_type.is_nested_slice() { self.push_err(ResolverError::NestedSlices { location }); } resolved_type } + pub(crate) fn resolve_type_with_kind(&mut self, typ: UnresolvedType, kind: &Kind) -> Type { + self.resolve_type_with_kind_inner(typ, kind, PathResolutionMode::MarkAsReferenced) + } + + pub(crate) fn use_type_with_kind(&mut self, typ: UnresolvedType, kind: &Kind) -> Type { + self.resolve_type_with_kind_inner(typ, kind, PathResolutionMode::MarkAsUsed) + } + /// Translates an UnresolvedType into a Type and appends any /// freshly created TypeVariables created to new_variables. - pub fn resolve_type_inner(&mut self, typ: UnresolvedType, kind: &Kind) -> Type { + fn resolve_type_with_kind_inner( + &mut self, + typ: UnresolvedType, + kind: &Kind, + mode: PathResolutionMode, + ) -> Type { use crate::ast::UnresolvedTypeData::*; let location = typ.location; @@ -83,12 +105,12 @@ impl Elaborator<'_> { let resolved_type = match typ.typ { FieldElement => Type::FieldElement, Array(size, elem) => { - let elem = Box::new(self.resolve_type_inner(*elem, kind)); + let elem = Box::new(self.resolve_type_with_kind_inner(*elem, kind, mode)); let size = self.convert_expression_type(size, &Kind::u32(), location); Type::Array(Box::new(size), elem) } Slice(elem) => { - let elem = Box::new(self.resolve_type_inner(*elem, kind)); + let elem = Box::new(self.resolve_type_with_kind_inner(*elem, kind, mode)); Type::Slice(elem) } Expression(expr) => self.convert_expression_type(expr, kind, location), @@ -100,7 +122,7 @@ impl Elaborator<'_> { } FormatString(size, fields) => { let resolved_size = self.convert_expression_type(size, &Kind::u32(), location); - let fields = self.resolve_type_inner(*fields, kind); + let fields = self.resolve_type_with_kind_inner(*fields, kind, mode); Type::FmtString(Box::new(resolved_size), Box::new(fields)) } Quoted(quoted) => { @@ -119,18 +141,18 @@ impl Elaborator<'_> { Type::Error } Error => Type::Error, - Named(path, args, _) => self.resolve_named_type(path, args), - TraitAsType(path, args) => self.resolve_trait_as_type(path, args), + Named(path, args, _) => self.resolve_named_type(path, args, mode), + TraitAsType(path, args) => self.resolve_trait_as_type(path, args, mode), - Tuple(fields) => { - Type::Tuple(vecmap(fields, |field| self.resolve_type_inner(field, kind))) - } + Tuple(fields) => Type::Tuple(vecmap(fields, |field| { + self.resolve_type_with_kind_inner(field, kind, mode) + })), Function(args, ret, env, unconstrained) => { - let args = vecmap(args, |arg| self.resolve_type_inner(arg, kind)); - let ret = Box::new(self.resolve_type_inner(*ret, kind)); + let args = vecmap(args, |arg| self.resolve_type_with_kind_inner(arg, kind, mode)); + let ret = Box::new(self.resolve_type_with_kind_inner(*ret, kind, mode)); let env_location = env.location; - let env = Box::new(self.resolve_type_inner(*env, kind)); + let env = Box::new(self.resolve_type_with_kind_inner(*env, kind, mode)); match *env { Type::Unit | Type::Tuple(_) | Type::NamedGeneric(_, _) => { @@ -149,14 +171,21 @@ impl Elaborator<'_> { if !mutable { self.use_unstable_feature(UnstableFeature::Ownership, location); } - Type::Reference(Box::new(self.resolve_type_inner(*element, kind)), mutable) + Type::Reference( + Box::new(self.resolve_type_with_kind_inner(*element, kind, mode)), + mutable, + ) } - Parenthesized(typ) => self.resolve_type_inner(*typ, kind), + Parenthesized(typ) => self.resolve_type_with_kind_inner(*typ, kind, mode), Resolved(id) => self.interner.get_quoted_type(id).clone(), AsTraitPath(path) => self.resolve_as_trait_path(*path), Interned(id) => { let typ = self.interner.get_unresolved_type_data(id).clone(); - return self.resolve_type_inner(UnresolvedType { typ, location }, kind); + return self.resolve_type_with_kind_inner( + UnresolvedType { typ, location }, + kind, + mode, + ); } }; @@ -216,9 +245,14 @@ impl Elaborator<'_> { None } - fn resolve_named_type(&mut self, path: Path, args: GenericTypeArgs) -> Type { + fn resolve_named_type( + &mut self, + path: Path, + args: GenericTypeArgs, + mode: PathResolutionMode, + ) -> Type { if args.is_empty() { - if let Some(typ) = self.lookup_generic_or_global_type(&path) { + if let Some(typ) = self.lookup_generic_or_global_type(&path, mode) { return typ; } } @@ -249,9 +283,9 @@ impl Elaborator<'_> { let location = path.location; - if let Some(type_alias) = self.lookup_type_alias(path.clone()) { + if let Some(type_alias) = self.lookup_type_alias(path.clone(), mode) { let id = type_alias.borrow().id; - let (args, _) = self.resolve_type_args(args, id, location); + let (args, _) = self.resolve_type_args_inner(args, id, location, mode); if let Some(item) = self.current_item { self.interner.add_type_alias_dependency(item, id); @@ -269,7 +303,7 @@ impl Elaborator<'_> { return Type::Alias(type_alias, args); } - match self.lookup_datatype_or_error(path) { + match self.lookup_datatype_or_error(path, mode) { Some(data_type) => { if self.resolving_ids.contains(&data_type.borrow().id) { self.push_err(ResolverError::SelfReferentialType { @@ -291,7 +325,8 @@ impl Elaborator<'_> { }); } - let (args, _) = self.resolve_type_args(args, data_type.borrow(), location); + let (args, _) = + self.resolve_type_args_inner(args, data_type.borrow(), location, mode); if let Some(current_item) = self.current_item { let dependency_id = data_type.borrow().id; @@ -304,14 +339,19 @@ impl Elaborator<'_> { } } - fn resolve_trait_as_type(&mut self, path: Path, args: GenericTypeArgs) -> Type { + fn resolve_trait_as_type( + &mut self, + path: Path, + args: GenericTypeArgs, + mode: PathResolutionMode, + ) -> Type { // Fetch information needed from the trait as the closure for resolving all the `args` // requires exclusive access to `self` let location = path.location; let trait_as_type_info = self.lookup_trait_or_error(path).map(|t| t.id); if let Some(id) = trait_as_type_info { - let (ordered, named) = self.resolve_type_args(args, id, location); + let (ordered, named) = self.resolve_type_args_inner(args, id, location, mode); let name = self.interner.get_trait(id).name.to_string(); let generics = TraitGenerics { ordered, named }; Type::TraitAsType(id, Rc::new(name), generics) @@ -328,24 +368,37 @@ impl Elaborator<'_> { item: TraitId, location: Location, ) -> (Vec, Vec) { - self.resolve_type_args_inner(args, item, location, false) + let mode = PathResolutionMode::MarkAsReferenced; + self.resolve_type_or_trait_args_inner(args, item, location, false, mode) } - pub(super) fn resolve_type_args( + pub(super) fn use_type_args( &mut self, args: GenericTypeArgs, item: impl Generic, location: Location, ) -> (Vec, Vec) { - self.resolve_type_args_inner(args, item, location, true) + let mode = PathResolutionMode::MarkAsUsed; + self.resolve_type_args_inner(args, item, location, mode) } pub(super) fn resolve_type_args_inner( + &mut self, + args: GenericTypeArgs, + item: impl Generic, + location: Location, + mode: PathResolutionMode, + ) -> (Vec, Vec) { + self.resolve_type_or_trait_args_inner(args, item, location, true, mode) + } + + pub(super) fn resolve_type_or_trait_args_inner( &mut self, mut args: GenericTypeArgs, item: impl Generic, location: Location, allow_implicit_named_args: bool, + mode: PathResolutionMode, ) -> (Vec, Vec) { let expected_kinds = item.generics(self.interner); @@ -361,8 +414,9 @@ impl Elaborator<'_> { } let ordered_args = expected_kinds.iter().zip(args.ordered_args); - let ordered = - vecmap(ordered_args, |(generic, typ)| self.resolve_type_inner(typ, &generic.kind())); + let ordered = vecmap(ordered_args, |(generic, typ)| { + self.resolve_type_with_kind_inner(typ, &generic.kind(), mode) + }); let mut associated = Vec::new(); @@ -372,6 +426,7 @@ impl Elaborator<'_> { item, location, allow_implicit_named_args, + mode, ); } else if !args.named_args.is_empty() { let item_kind = item.item_kind(); @@ -387,6 +442,7 @@ impl Elaborator<'_> { item: impl Generic, location: Location, allow_implicit_named_args: bool, + mode: PathResolutionMode, ) -> Vec { let mut seen_args = HashMap::default(); let mut required_args = item.named_generics(self.interner); @@ -411,7 +467,7 @@ impl Elaborator<'_> { let expected = required_args.remove(index); seen_args.insert(name.to_string(), name.location()); - let typ = self.resolve_type_inner(typ, &expected.kind()); + let typ = self.resolve_type_with_kind_inner(typ, &expected.kind(), mode); resolved.push(NamedType { name, typ }); } @@ -433,7 +489,11 @@ impl Elaborator<'_> { resolved } - pub fn lookup_generic_or_global_type(&mut self, path: &Path) -> Option { + fn lookup_generic_or_global_type( + &mut self, + path: &Path, + mode: PathResolutionMode, + ) -> Option { if path.segments.len() == 1 { let name = path.last_name(); if let Some(generic) = self.find_generic(name) { @@ -445,7 +505,7 @@ impl Elaborator<'_> { } // If we cannot find a local generic of the same name, try to look up a global - match self.resolve_path_or_error(path.clone()) { + match self.resolve_path_or_error_inner(path.clone(), mode) { Ok(PathResolutionItem::Global(id)) => { if let Some(current_item) = self.current_item { self.interner.add_global_dependency(current_item, id); @@ -461,7 +521,7 @@ impl Elaborator<'_> { let Some(stmt) = self.interner.get_global_let_statement(id) else { if self.elaborate_global_if_unresolved(&id) { - return self.lookup_generic_or_global_type(path); + return self.lookup_generic_or_global_type(path, mode); } else { let path = path.clone(); self.push_err(ResolverError::NoSuchNumericTypeVariable { path }); @@ -514,7 +574,8 @@ impl Elaborator<'_> { ) -> Type { match length { UnresolvedTypeExpression::Variable(path) => { - let typ = self.resolve_named_type(path, GenericTypeArgs::default()); + let mode = PathResolutionMode::MarkAsReferenced; + let typ = self.resolve_named_type(path, GenericTypeArgs::default(), mode); self.check_kind(typ, expected_kind, location) } UnresolvedTypeExpression::Constant(int, _span) => { @@ -579,9 +640,8 @@ impl Elaborator<'_> { return Type::Error; }; - let (ordered, named) = - self.resolve_type_args(path.trait_generics.clone(), trait_id, location); - let object_type = self.resolve_type(path.typ.clone()); + let (ordered, named) = self.use_type_args(path.trait_generics.clone(), trait_id, location); + let object_type = self.use_type(path.typ.clone()); match self.interner.lookup_trait_implementation(&object_type, trait_id, &ordered, &named) { Ok(impl_kind) => self.get_associated_type_from_trait_impl(path, impl_kind), @@ -657,7 +717,7 @@ impl Elaborator<'_> { // Returns the trait method, trait constraint, and whether the impl is assumed to exist by a where clause or not // E.g. `t.method()` with `where T: Foo` in scope will return `(Foo::method, T, vec![Bar])` fn resolve_trait_static_method(&mut self, path: &Path) -> Option { - let path_resolution = self.resolve_path(path.clone()).ok()?; + let path_resolution = self.use_path(path.clone()).ok()?; let func_id = path_resolution.item.function_id()?; let meta = self.interner.try_function_meta(&func_id)?; let the_trait = self.interner.get_trait(meta.trait_id?); @@ -714,7 +774,7 @@ impl Elaborator<'_> { let last_segment = path.pop(); let before_last_segment = path.last_segment(); - let path_resolution = self.resolve_path(path).ok()?; + let path_resolution = self.use_path(path).ok()?; let PathResolutionItem::Type(type_id) = path_resolution.item else { return None; }; @@ -850,7 +910,7 @@ impl Elaborator<'_> { UnresolvedTypeData::Unspecified => { self.interner.next_type_variable_with_kind(Kind::Any) } - _ => self.resolve_type(typ), + _ => self.use_type(typ), } } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index b1fddd6ede1..fa6d190ebd5 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -863,7 +863,7 @@ fn quoted_as_trait_constraint( Some(ElaborateReason::EvaluatingComptimeCall("Quoted::as_trait_constraint", location)); let bound = interpreter .elaborate_in_function(interpreter.current_function, reason, |elaborator| { - elaborator.resolve_trait_bound(&trait_bound) + elaborator.use_trait_bound(&trait_bound) }) .ok_or(InterpreterError::FailedToResolveTraitBound { trait_bound, location })?; @@ -881,7 +881,7 @@ fn quoted_as_type( let reason = Some(ElaborateReason::EvaluatingComptimeCall("Quoted::as_type", location)); let typ = interpreter.elaborate_in_function(interpreter.current_function, reason, |elaborator| { - elaborator.resolve_type(typ) + elaborator.use_type(typ) }); Ok(Value::Type(typ)) } diff --git a/compiler/noirc_frontend/src/tests/traits.rs b/compiler/noirc_frontend/src/tests/traits.rs index 247d3050bf1..4e4a1a2a6f2 100644 --- a/compiler/noirc_frontend/src/tests/traits.rs +++ b/compiler/noirc_frontend/src/tests/traits.rs @@ -134,7 +134,6 @@ fn trait_inheritance_missing_parent_implementation() { ~~~~~~ The trait `Foo` is not implemented for `Struct` fn main() { - let _ = Struct {}; // silence Struct never constructed warning } "#; check_errors!(src); diff --git a/compiler/noirc_frontend/src/tests/unused_items.rs b/compiler/noirc_frontend/src/tests/unused_items.rs index e0738d9c4af..65aa92a1d4d 100644 --- a/compiler/noirc_frontend/src/tests/unused_items.rs +++ b/compiler/noirc_frontend/src/tests/unused_items.rs @@ -289,3 +289,103 @@ fn resolves_trait_where_clause_in_the_correct_module() { "#; assert_no_errors!(src); } + +#[named] +#[test] +fn considers_struct_as_constructed_if_impl_method_is_called() { + let src = " + struct Bar {} + + impl Bar { + fn foo() {} + } + + pub fn main() { + Bar::foo() + } + "; + assert_no_errors!(src); +} + +#[named] +#[test] +fn considers_struct_as_constructed_if_trait_method_is_called() { + let src = " + struct Bar {} + + pub trait Foo { + fn foo(); + } + + impl Foo for Bar { + fn foo() {} + } + + pub fn main() { + Bar::foo() + } + "; + assert_no_errors!(src); +} + +#[named] +#[test] +fn considers_struct_as_constructed_if_mentioned_in_let_type() { + let src = " + struct Bar {} + + pub fn main() { + let array = []; + let _: Bar = array[0]; + } + "; + assert_no_errors!(src); +} + +#[named] +#[test] +fn considers_struct_as_constructed_if_mentioned_in_return_type() { + let src = " + struct Bar {} + + fn main() { + let _ = foo(); + } + + fn foo() -> Bar { + let array = []; + array[0] + } + "; + assert_no_errors!(src); +} + +#[named] +#[test] +fn considers_struct_as_constructed_if_passed_in_generic_args_in_constructor() { + let src = " + struct Bar {} + + struct Generic {} + + fn main() { + let _ = Generic:: {}; + } + "; + assert_no_errors!(src); +} + +#[named] +#[test] +fn considers_struct_as_constructed_if_passed_in_generic_args_in_function_call() { + let src = " + struct Bar {} + + fn foo() {} + + fn main() { + let _ = foo::(); + } + "; + assert_no_errors!(src); +} diff --git a/test_programs/compile_failure/noirc_frontend_tests_traits_trait_inheritance_missing_parent_implementation/src/main.nr b/test_programs/compile_failure/noirc_frontend_tests_traits_trait_inheritance_missing_parent_implementation/src/main.nr index 13fb7f451f6..41f00f39e8b 100644 --- a/test_programs/compile_failure/noirc_frontend_tests_traits_trait_inheritance_missing_parent_implementation/src/main.nr +++ b/test_programs/compile_failure/noirc_frontend_tests_traits_trait_inheritance_missing_parent_implementation/src/main.nr @@ -8,6 +8,5 @@ impl Bar for Struct {} fn main() { - let _ = Struct {}; // silence Struct never constructed warning } \ No newline at end of file diff --git a/test_programs/compile_failure/noirc_frontend_tests_traits_trait_inheritance_missing_parent_implementation/src_hash.txt b/test_programs/compile_failure/noirc_frontend_tests_traits_trait_inheritance_missing_parent_implementation/src_hash.txt index 0b74963b7fb..bec750a9229 100644 --- a/test_programs/compile_failure/noirc_frontend_tests_traits_trait_inheritance_missing_parent_implementation/src_hash.txt +++ b/test_programs/compile_failure/noirc_frontend_tests_traits_trait_inheritance_missing_parent_implementation/src_hash.txt @@ -1 +1 @@ -5297256794125326093 \ No newline at end of file +7024564493620083231 \ No newline at end of file diff --git a/test_programs/compile_failure/regression_5065_failure/stderr.txt b/test_programs/compile_failure/regression_5065_failure/stderr.txt index c476ae81670..32753252a22 100644 --- a/test_programs/compile_failure/regression_5065_failure/stderr.txt +++ b/test_programs/compile_failure/regression_5065_failure/stderr.txt @@ -1,10 +1,3 @@ -warning: struct `BadType` is never constructed - ┌─ src/main.nr:31:8 - │ -31 │ struct BadType {} - │ ------- struct is never constructed - │ - error: No matching impl found for `BadType: MyTrait` ┌─ src/main.nr:35:18 │ @@ -12,4 +5,4 @@ error: No matching impl found for `BadType: MyTrait` │ --- No impl for `BadType: MyTrait` │ -Aborting due to 1 previous error \ No newline at end of file +Aborting due to 1 previous error diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_impl_method_is_called/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_impl_method_is_called/Nargo.toml new file mode 100644 index 00000000000..0a81253cddb --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_impl_method_is_called/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_impl_method_is_called" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_impl_method_is_called/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_impl_method_is_called/src/main.nr new file mode 100644 index 00000000000..740760a2869 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_impl_method_is_called/src/main.nr @@ -0,0 +1,11 @@ + + struct Bar {} + + impl Bar { + fn foo() {} + } + + pub fn main() { + Bar::foo() + } + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_impl_method_is_called/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_impl_method_is_called/src_hash.txt new file mode 100644 index 00000000000..3f8d33f0657 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_impl_method_is_called/src_hash.txt @@ -0,0 +1 @@ +12157035749622789576 \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_mentioned_in_let_type/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_mentioned_in_let_type/Nargo.toml new file mode 100644 index 00000000000..1e135bdd6d2 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_mentioned_in_let_type/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_mentioned_in_let_type" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_mentioned_in_let_type/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_mentioned_in_let_type/src/main.nr new file mode 100644 index 00000000000..a50d226e876 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_mentioned_in_let_type/src/main.nr @@ -0,0 +1,8 @@ + + struct Bar {} + + pub fn main() { + let array = []; + let _: Bar = array[0]; + } + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_mentioned_in_let_type/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_mentioned_in_let_type/src_hash.txt new file mode 100644 index 00000000000..4e512c671a2 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_mentioned_in_let_type/src_hash.txt @@ -0,0 +1 @@ +12495805612126484999 \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_mentioned_in_return_type/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_mentioned_in_return_type/Nargo.toml new file mode 100644 index 00000000000..75cd8c94e4f --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_mentioned_in_return_type/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_mentioned_in_return_type" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_mentioned_in_return_type/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_mentioned_in_return_type/src/main.nr new file mode 100644 index 00000000000..cd8d0404bb4 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_mentioned_in_return_type/src/main.nr @@ -0,0 +1,12 @@ + + struct Bar {} + + fn main() { + let _ = foo(); + } + + fn foo() -> Bar { + let array = []; + array[0] + } + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_mentioned_in_return_type/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_mentioned_in_return_type/src_hash.txt new file mode 100644 index 00000000000..cff47d50e60 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_mentioned_in_return_type/src_hash.txt @@ -0,0 +1 @@ +6802476024805115768 \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_passed_in_generic_args_in_constructor/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_passed_in_generic_args_in_constructor/Nargo.toml new file mode 100644 index 00000000000..382086f3ffc --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_passed_in_generic_args_in_constructor/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_passed_in_generic_args_in_constructor" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_passed_in_generic_args_in_constructor/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_passed_in_generic_args_in_constructor/src/main.nr new file mode 100644 index 00000000000..ce4e5b21cb4 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_passed_in_generic_args_in_constructor/src/main.nr @@ -0,0 +1,9 @@ + + struct Bar {} + + struct Generic {} + + fn main() { + let _ = Generic:: {}; + } + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_passed_in_generic_args_in_constructor/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_passed_in_generic_args_in_constructor/src_hash.txt new file mode 100644 index 00000000000..8a1c19be290 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_passed_in_generic_args_in_constructor/src_hash.txt @@ -0,0 +1 @@ +9963543055637964444 \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_passed_in_generic_args_in_function_call/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_passed_in_generic_args_in_function_call/Nargo.toml new file mode 100644 index 00000000000..0a898c24093 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_passed_in_generic_args_in_function_call/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_passed_in_generic_args_in_function_call" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_passed_in_generic_args_in_function_call/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_passed_in_generic_args_in_function_call/src/main.nr new file mode 100644 index 00000000000..1aba6ea288b --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_passed_in_generic_args_in_function_call/src/main.nr @@ -0,0 +1,9 @@ + + struct Bar {} + + fn foo() {} + + fn main() { + let _ = foo::(); + } + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_passed_in_generic_args_in_function_call/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_passed_in_generic_args_in_function_call/src_hash.txt new file mode 100644 index 00000000000..bf019772919 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_passed_in_generic_args_in_function_call/src_hash.txt @@ -0,0 +1 @@ +8792428129164250099 \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_trait_method_is_called/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_trait_method_is_called/Nargo.toml new file mode 100644 index 00000000000..5117a438bcd --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_trait_method_is_called/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_trait_method_is_called" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_trait_method_is_called/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_trait_method_is_called/src/main.nr new file mode 100644 index 00000000000..f23ea5ec2c3 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_trait_method_is_called/src/main.nr @@ -0,0 +1,15 @@ + + struct Bar {} + + pub trait Foo { + fn foo(); + } + + impl Foo for Bar { + fn foo() {} + } + + pub fn main() { + Bar::foo() + } + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_trait_method_is_called/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_trait_method_is_called/src_hash.txt new file mode 100644 index 00000000000..d354367d879 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_trait_method_is_called/src_hash.txt @@ -0,0 +1 @@ +2362175844420958899 \ No newline at end of file