From a1448fd323eaa8dbf0e882ed5b708979a8d56709 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 18 Sep 2024 15:27:45 -0500 Subject: [PATCH 1/3] Implement type paths --- aztec_macros/src/transforms/note_interface.rs | 1 + aztec_macros/src/transforms/storage.rs | 1 + aztec_macros/src/utils/parse_utils.rs | 6 +++ compiler/noirc_frontend/src/ast/expression.rs | 14 +++++- compiler/noirc_frontend/src/ast/statement.rs | 10 ++++ compiler/noirc_frontend/src/ast/visitor.rs | 20 +++++++- .../src/elaborator/expressions.rs | 19 +++---- .../noirc_frontend/src/elaborator/patterns.rs | 45 +++++++++++++++-- .../noirc_frontend/src/elaborator/types.rs | 11 +++-- .../src/hir/comptime/interpreter.rs | 3 +- .../noirc_frontend/src/hir/comptime/value.rs | 12 ++++- .../src/hir/type_check/generics.rs | 24 ++++++++- compiler/noirc_frontend/src/hir_def/expr.rs | 19 ++++++- compiler/noirc_frontend/src/hir_def/types.rs | 2 +- compiler/noirc_frontend/src/node_interner.rs | 49 +++++++++++++------ compiler/noirc_frontend/src/parser/parser.rs | 3 +- .../noirc_frontend/src/parser/parser/path.rs | 24 +++++++-- .../noirc_frontend/src/parser/parser/types.rs | 18 ++++--- .../type_path/Nargo.toml | 7 +++ .../type_path/src/main.nr | 16 ++++++ tooling/lsp/src/requests/inlay_hint.rs | 1 + tooling/nargo_fmt/src/rewrite/expr.rs | 15 +++++- 22 files changed, 268 insertions(+), 52 deletions(-) create mode 100644 test_programs/compile_success_empty/type_path/Nargo.toml create mode 100644 test_programs/compile_success_empty/type_path/src/main.nr diff --git a/aztec_macros/src/transforms/note_interface.rs b/aztec_macros/src/transforms/note_interface.rs index d9c4a594fc6..8587e08892b 100644 --- a/aztec_macros/src/transforms/note_interface.rs +++ b/aztec_macros/src/transforms/note_interface.rs @@ -776,6 +776,7 @@ pub fn inject_note_exports( note.borrow().id, "get_note_type_id", false, + true, ) .ok_or(( AztecMacroError::CouldNotExportStorageLayout { diff --git a/aztec_macros/src/transforms/storage.rs b/aztec_macros/src/transforms/storage.rs index d056dfc4412..e8ee1675546 100644 --- a/aztec_macros/src/transforms/storage.rs +++ b/aztec_macros/src/transforms/storage.rs @@ -360,6 +360,7 @@ pub fn assign_storage_slots( storage_struct.borrow().id, "init", false, + true, ) .ok_or(( AztecMacroError::CouldNotAssignStorageSlots { diff --git a/aztec_macros/src/utils/parse_utils.rs b/aztec_macros/src/utils/parse_utils.rs index 61410bbb5b2..efa31860b6e 100644 --- a/aztec_macros/src/utils/parse_utils.rs +++ b/aztec_macros/src/utils/parse_utils.rs @@ -295,6 +295,12 @@ fn empty_expression(expression: &mut Expression) { empty_unresolved_type(&mut path.typ); empty_path(&mut path.trait_path); empty_ident(&mut path.impl_item); + empty_type_args(&mut path.trait_generics); + } + ExpressionKind::TypePath(path) => { + empty_unresolved_type(&mut path.typ); + empty_ident(&mut path.item); + empty_type_args(&mut path.turbofish); } ExpressionKind::Quote(..) | ExpressionKind::Resolved(_) diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index cce8a50601e..c581ea9d62a 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -14,7 +14,7 @@ use acvm::{acir::AcirField, FieldElement}; use iter_extended::vecmap; use noirc_errors::{Span, Spanned}; -use super::{AsTraitPath, UnaryRhsMemberAccess}; +use super::{AsTraitPath, TypePath, UnaryRhsMemberAccess}; #[derive(Debug, PartialEq, Eq, Clone)] pub enum ExpressionKind { @@ -38,6 +38,7 @@ pub enum ExpressionKind { Comptime(BlockExpression, Span), Unsafe(BlockExpression, Span), AsTraitPath(AsTraitPath), + TypePath(TypePath), // This variant is only emitted when inlining the result of comptime // code. It is used to translate function values back into the AST while @@ -621,6 +622,7 @@ impl Display for ExpressionKind { write!(f, "quote {{ {} }}", tokens.join(" ")) } AsTraitPath(path) => write!(f, "{path}"), + TypePath(path) => write!(f, "{path}"), InternedStatement(_) => write!(f, "?InternedStatement"), } } @@ -787,6 +789,16 @@ impl Display for AsTraitPath { } } +impl Display for TypePath { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}::{}", self.typ, self.item)?; + if !self.turbofish.is_empty() { + write!(f, "::{}", self.turbofish)?; + } + Ok(()) + } +} + impl FunctionDefinition { pub fn normal( name: &Ident, diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 2317b96a1f1..d67501e932b 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -389,6 +389,16 @@ pub struct AsTraitPath { pub impl_item: Ident, } +/// A special kind of path in the form `Type::ident::` +/// Unlike normal paths, the type here can be a primitive type or +/// interned type. +#[derive(Debug, PartialEq, Eq, Clone, Hash)] +pub struct TypePath { + pub typ: UnresolvedType, + pub item: Ident, + pub turbofish: GenericTypeArgs, +} + // Note: Path deliberately doesn't implement Recoverable. // No matter which default value we could give in Recoverable::error, // it would most likely cause further errors during name resolution diff --git a/compiler/noirc_frontend/src/ast/visitor.rs b/compiler/noirc_frontend/src/ast/visitor.rs index abf76545f0a..9a2fb79ca88 100644 --- a/compiler/noirc_frontend/src/ast/visitor.rs +++ b/compiler/noirc_frontend/src/ast/visitor.rs @@ -22,7 +22,7 @@ use crate::{ use super::{ FunctionReturnType, GenericTypeArgs, IntegerBitSize, ItemVisibility, Pattern, Signedness, - TraitImplItemKind, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, + TraitImplItemKind, TypePath, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, }; @@ -340,6 +340,10 @@ pub trait Visitor { true } + fn visit_type_path(&mut self, _: &TypePath, _: Span) -> bool { + true + } + fn visit_unresolved_type(&mut self, _: &UnresolvedType) -> bool { true } @@ -838,6 +842,7 @@ impl Expression { ExpressionKind::AsTraitPath(as_trait_path) => { as_trait_path.accept(self.span, visitor); } + ExpressionKind::TypePath(path) => path.accept(self.span, visitor), ExpressionKind::Quote(tokens) => visitor.visit_quote(tokens), ExpressionKind::Resolved(expr_id) => visitor.visit_resolved_expression(*expr_id), ExpressionKind::Interned(id) => visitor.visit_interned_expression(*id), @@ -1208,6 +1213,19 @@ impl AsTraitPath { } } +impl TypePath { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_type_path(self, span) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + self.typ.accept(visitor); + self.turbofish.accept(visitor); + } +} + impl UnresolvedType { pub fn accept(&self, visitor: &mut impl Visitor) { if visitor.visit_unresolved_type(self) { diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index 6f81c889e77..a3b71f3e211 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -18,7 +18,7 @@ use crate::{ HirArrayLiteral, HirBinaryOp, HirBlockExpression, HirCallExpression, HirCastExpression, HirConstructorExpression, HirExpression, HirIfExpression, HirIndexExpression, HirInfixExpression, HirLambda, HirMemberAccess, HirMethodCallExpression, - HirMethodReference, HirPrefixExpression, + HirPrefixExpression, }, traits::TraitConstraint, }, @@ -76,6 +76,7 @@ impl<'context> Elaborator<'context> { (HirExpression::Error, Type::Error) } ExpressionKind::AsTraitPath(_) => todo!("Implement AsTraitPath"), + ExpressionKind::TypePath(path) => return self.elaborate_type_path(path), }; let id = self.interner.push_expr(hir_expr); self.interner.push_expr_location(id, expr.span, self.file); @@ -406,21 +407,13 @@ impl<'context> Elaborator<'context> { let method_name_span = method_call.method_name.span(); let method_name = method_call.method_name.0.contents.as_str(); - match self.lookup_method(&object_type, method_name, span) { + match self.lookup_method(&object_type, method_name, span, true) { Some(method_ref) => { // Automatically add `&mut` if the method expects a mutable reference and // the object is not already one. - 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 - } - }; + let func_id = method_ref + .func_id(self.interner) + .expect("Expected trait function to be a DefinitionKind::Function"); let generics = if func_id != FuncId::dummy_id() { let function_type = self.interner.function_meta(&func_id).typ.clone(); diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index c2e42b7574c..7afa3215566 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -3,17 +3,17 @@ use noirc_errors::{Location, Span}; use rustc_hash::FxHashSet as HashSet; use crate::{ - ast::{UnresolvedType, ERROR_IDENT}, + ast::{TypePath, UnresolvedType, ERROR_IDENT}, hir::{ def_collector::dc_crate::CompilationError, resolution::errors::ResolverError, type_check::{Source, TypeCheckError}, }, hir_def::{ - expr::{HirIdent, ImplKind}, + expr::{HirIdent, HirMethodReference, ImplKind}, stmt::HirPattern, }, - macros_api::{HirExpression, Ident, Path, Pattern}, + macros_api::{Expression, ExpressionKind, HirExpression, Ident, Path, Pattern}, node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, GlobalId, TraitImplKind}, ResolvedGeneric, Shared, StructType, Type, TypeBindings, }; @@ -697,4 +697,43 @@ impl<'context> Elaborator<'context> { let id = DefinitionId::dummy_id(); (HirIdent::non_trait_method(id, location), 0) } + + pub(super) fn elaborate_type_path(&mut self, path: TypePath) -> (ExprId, Type) { + let span = path.item.span(); + let typ = self.resolve_type(path.typ); + + let Some(method) = self.lookup_method(&typ, &path.item.0.contents, span, false) else { + let error = Expression::new(ExpressionKind::Error, span); + return self.elaborate_expression(error); + }; + + let func_id = method + .func_id(self.interner) + .expect("Expected trait function to be a DefinitionKind::Function"); + + let generics = self.resolve_type_args(path.turbofish, func_id, span).0; + let generics = (!generics.is_empty()).then_some(generics); + + let location = Location::new(span, self.file); + let id = self.interner.function_definition_id(func_id); + + let impl_kind = match method { + HirMethodReference::FuncId(_) => ImplKind::NotATraitMethod, + HirMethodReference::TraitMethodId(method_id, generics) => { + let mut constraint = + self.interner.get_trait(method_id.trait_id).as_constraint(span); + constraint.trait_generics = generics; + ImplKind::TraitMethod(method_id, constraint, false) + } + }; + + let ident = HirIdent { location, id, impl_kind }; + let id = self.interner.push_expr(HirExpression::Ident(ident.clone(), generics.clone())); + self.interner.push_expr_location(id, location.span, location.file); + + let typ = self.type_check_variable(ident, id, generics); + self.interner.push_expr_type(id, typ.clone()); + + (id, typ) + } } diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index c11fc98fb1c..f40b62f2f31 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -1306,11 +1306,13 @@ impl<'context> Elaborator<'context> { object_type: &Type, method_name: &str, span: Span, + has_self_arg: bool, ) -> Option { match object_type.follow_bindings() { Type::Struct(typ, _args) => { let id = typ.borrow().id; - match self.interner.lookup_method(object_type, id, method_name, false) { + match self.interner.lookup_method(object_type, id, method_name, false, has_self_arg) + { Some(method_id) => Some(HirMethodReference::FuncId(method_id)), None => { self.push_err(TypeCheckError::UnresolvedMethodCall { @@ -1339,9 +1341,9 @@ impl<'context> Elaborator<'context> { // This may be a struct or a primitive type. Type::MutableReference(element) => self .interner - .lookup_primitive_trait_method_mut(element.as_ref(), method_name) + .lookup_primitive_trait_method_mut(element.as_ref(), method_name, has_self_arg) .map(HirMethodReference::FuncId) - .or_else(|| self.lookup_method(&element, method_name, span)), + .or_else(|| self.lookup_method(&element, method_name, span, has_self_arg)), // If we fail to resolve the object to a struct type, we have no way of type // checking its arguments as we can't even resolve the name of the function @@ -1353,7 +1355,8 @@ impl<'context> Elaborator<'context> { None } - other => match self.interner.lookup_primitive_method(&other, method_name) { + other => match self.interner.lookup_primitive_method(&other, method_name, has_self_arg) + { Some(method_id) => Some(HirMethodReference::FuncId(method_id)), None => { // It could be that this type is a composite type that is bound to a trait, diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index ceaeb9393b1..b5ed8126e33 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -1352,8 +1352,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { struct_def.borrow().id, method_name, false, + true, ), - _ => self.elaborator.interner.lookup_primitive_method(&typ, method_name), + _ => self.elaborator.interner.lookup_primitive_method(&typ, method_name, true), }; if let Some(method) = method { diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index b027d069536..4d0787628cd 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -862,7 +862,17 @@ fn remove_interned_in_expression_kind( vecmap(block.statements, |stmt| remove_interned_in_statement(interner, stmt)); ExpressionKind::Unsafe(BlockExpression { statements }, span) } - ExpressionKind::AsTraitPath(_) => expr, + ExpressionKind::AsTraitPath(mut path) => { + path.typ = remove_interned_in_unresolved_type(interner, path.typ); + path.trait_generics = + remove_interned_in_generic_type_args(interner, path.trait_generics); + ExpressionKind::AsTraitPath(path) + } + ExpressionKind::TypePath(mut path) => { + path.typ = remove_interned_in_unresolved_type(interner, path.typ); + path.turbofish = remove_interned_in_generic_type_args(interner, path.turbofish); + ExpressionKind::TypePath(path) + } ExpressionKind::Resolved(id) => { let expr = interner.expression(&id); expr.to_display_ast(interner, Span::default()).kind diff --git a/compiler/noirc_frontend/src/hir/type_check/generics.rs b/compiler/noirc_frontend/src/hir/type_check/generics.rs index 697c78745f9..b86e2350279 100644 --- a/compiler/noirc_frontend/src/hir/type_check/generics.rs +++ b/compiler/noirc_frontend/src/hir/type_check/generics.rs @@ -5,7 +5,7 @@ use iter_extended::vecmap; use crate::{ hir_def::traits::NamedType, macros_api::NodeInterner, - node_interner::{TraitId, TypeAliasId}, + node_interner::{FuncId, TraitId, TypeAliasId}, ResolvedGeneric, StructType, Type, }; @@ -97,6 +97,28 @@ impl Generic for Ref<'_, StructType> { } } +impl Generic for FuncId { + fn item_kind(&self) -> &'static str { + "function" + } + + fn item_name(&self, interner: &NodeInterner) -> String { + interner.function_name(self).to_string() + } + + fn generics(&self, interner: &NodeInterner) -> Vec { + interner.function_meta(self).direct_generics.clone() + } + + fn accepts_named_type_args(&self) -> bool { + false + } + + fn named_generics(&self, _interner: &NodeInterner) -> Vec { + Vec::new() + } +} + /// TraitGenerics are different from regular generics in that they can /// also contain associated type arguments. #[derive(Default, PartialEq, Eq, Clone, Hash, Ord, PartialOrd)] diff --git a/compiler/noirc_frontend/src/hir_def/expr.rs b/compiler/noirc_frontend/src/hir_def/expr.rs index 40c16d00356..063b960863c 100644 --- a/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/compiler/noirc_frontend/src/hir_def/expr.rs @@ -4,7 +4,9 @@ use noirc_errors::Location; use crate::ast::{BinaryOp, BinaryOpKind, Ident, UnaryOp}; use crate::hir::type_check::generics::TraitGenerics; -use crate::node_interner::{DefinitionId, ExprId, FuncId, NodeInterner, StmtId, TraitMethodId}; +use crate::node_interner::{ + DefinitionId, DefinitionKind, ExprId, FuncId, NodeInterner, StmtId, TraitMethodId, +}; use crate::token::Tokens; use crate::Shared; @@ -203,6 +205,21 @@ pub enum HirMethodReference { TraitMethodId(TraitMethodId, TraitGenerics), } +impl HirMethodReference { + pub fn func_id(&self, interner: &NodeInterner) -> Option { + match self { + HirMethodReference::FuncId(func_id) => Some(*func_id), + HirMethodReference::TraitMethodId(method_id, _) => { + let id = interner.trait_method_id(*method_id); + match &interner.try_definition(id)?.kind { + DefinitionKind::Function(func_id) => Some(*func_id), + _ => None, + } + } + } + } +} + impl HirMethodCallExpression { /// Converts a method call into a function call /// diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 7bb9fb83e70..c170d2cc08f 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -1680,7 +1680,7 @@ impl Type { if let (Type::Array(_size, element1), Type::Slice(element2)) = (&this, &target) { // We can only do the coercion if the `as_slice` method exists. // This is usually true, but some tests don't have access to the standard library. - if let Some(as_slice) = interner.lookup_primitive_method(&this, "as_slice") { + if let Some(as_slice) = interner.lookup_primitive_method(&this, "as_slice", true) { // Still have to ensure the element types match. // Don't need to issue an error here if not, it will be done in unify_with_coercions let mut bindings = TypeBindings::new(); diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 065e33608ba..d3e5e270b3c 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -1373,7 +1373,8 @@ impl NodeInterner { Type::Struct(struct_type, _generics) => { let id = struct_type.borrow().id; - if let Some(existing) = self.lookup_method(self_type, id, &method_name, true) { + if let Some(existing) = self.lookup_method(self_type, id, &method_name, true, true) + { return Some(existing); } @@ -1770,6 +1771,7 @@ impl NodeInterner { id: StructId, method_name: &str, force_type_check: bool, + has_self_arg: bool, ) -> Option { let methods = self.struct_methods.get(&id).and_then(|h| h.get(method_name)); @@ -1781,7 +1783,7 @@ impl NodeInterner { } } - self.find_matching_method(typ, methods, method_name) + self.find_matching_method(typ, methods, method_name, has_self_arg) } /// Select the 1 matching method with an object type matching `typ` @@ -1790,32 +1792,40 @@ impl NodeInterner { typ: &Type, methods: Option<&Methods>, method_name: &str, + has_self_arg: bool, ) -> Option { - if let Some(method) = methods.and_then(|m| m.find_matching_method(typ, self)) { + if let Some(method) = methods.and_then(|m| m.find_matching_method(typ, has_self_arg, self)) + { Some(method) } else { // Failed to find a match for the type in question, switch to looking at impls // for all types `T`, e.g. `impl Foo for T` let global_methods = self.primitive_methods.get(&TypeMethodKey::Generic)?.get(method_name)?; - global_methods.find_matching_method(typ, self) + global_methods.find_matching_method(typ, has_self_arg, self) } } /// Looks up a given method name on the given primitive type. - pub fn lookup_primitive_method(&self, typ: &Type, method_name: &str) -> Option { + pub fn lookup_primitive_method( + &self, + typ: &Type, + method_name: &str, + has_self_arg: bool, + ) -> Option { let key = get_type_method_key(typ)?; let methods = self.primitive_methods.get(&key)?.get(method_name)?; - self.find_matching_method(typ, Some(methods), method_name) + self.find_matching_method(typ, Some(methods), method_name, has_self_arg) } pub fn lookup_primitive_trait_method_mut( &self, typ: &Type, method_name: &str, + has_self_arg: bool, ) -> Option { let typ = Type::MutableReference(Box::new(typ.clone())); - self.lookup_primitive_method(&typ, method_name) + self.lookup_primitive_method(&typ, method_name, has_self_arg) } /// Returns what the next trait impl id is expected to be. @@ -2256,19 +2266,30 @@ impl Methods { } /// Select the 1 matching method with an object type matching `typ` - fn find_matching_method(&self, typ: &Type, interner: &NodeInterner) -> Option { + fn find_matching_method( + &self, + typ: &Type, + has_self_param: bool, + interner: &NodeInterner, + ) -> Option { // When adding methods we always check they do not overlap, so there should be // at most 1 matching method in this list. for method in self.iter() { match interner.function_meta(&method).typ.instantiate(interner).0 { Type::Function(args, _, _, _) => { - if let Some(object) = args.first() { - let mut bindings = TypeBindings::new(); - - if object.try_unify(typ, &mut bindings).is_ok() { - Type::apply_type_bindings(bindings); - return Some(method); + if has_self_param { + if let Some(object) = args.first() { + let mut bindings = TypeBindings::new(); + + if object.try_unify(typ, &mut bindings).is_ok() { + Type::apply_type_bindings(bindings); + return Some(method); + } } + } else { + // Just return the first method whose name matches since we + // can't match object types on static methods. + return Some(method); } } Type::Error => (), diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 5852ef7b68c..337563213e5 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -23,7 +23,7 @@ //! prevent other parsers from being tried afterward since there is no longer an error. Thus, they should //! be limited to cases like the above `fn` example where it is clear we shouldn't back out of the //! current parser to try alternative parsers in a `choice` expression. -use self::path::as_trait_path; +use self::path::{as_trait_path, type_path}; use self::primitives::{ interned_statement, interned_statement_expr, keyword, macro_quote_marker, mutable_reference, variable, @@ -1150,6 +1150,7 @@ where variable(), literal(), as_trait_path(parse_type()).map(ExpressionKind::AsTraitPath), + type_path(parse_type()), macro_quote_marker(), interned_expr(), interned_statement_expr(), diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index ea121c6f6db..3e964d271a0 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -1,4 +1,5 @@ -use crate::ast::{AsTraitPath, Path, PathKind, PathSegment, UnresolvedType}; +use crate::ast::{AsTraitPath, Path, PathKind, PathSegment, TypePath, UnresolvedType}; +use crate::macros_api::ExpressionKind; use crate::parser::{NoirParser, ParserError, ParserErrorReason}; use crate::token::{Keyword, Token}; @@ -6,8 +7,8 @@ use crate::token::{Keyword, Token}; use chumsky::prelude::*; use super::keyword; -use super::primitives::{ident, path_segment, path_segment_no_turbofish}; -use super::types::generic_type_args; +use super::primitives::{ident, path_segment, path_segment_no_turbofish, turbofish}; +use super::types::{generic_type_args, primitive_type}; pub(super) fn path<'a>( type_parser: impl NoirParser + 'a, @@ -68,6 +69,23 @@ pub(super) fn as_trait_path<'a>( }) } +/// Parses `MyType::path_segment` +/// These paths only support exactly two segments. +/// Unlike normal paths `MyType` here can also be a primitive type or interned type +/// in addition to a named type. +pub(super) fn type_path<'a>( + type_parser: impl NoirParser + 'a, +) -> impl NoirParser + 'a { + primitive_type() + .then_ignore(just(Token::DoubleColon)) + .then(ident()) + .then(turbofish(type_parser)) + .map(|((typ, item), turbofish)| { + let turbofish = turbofish.unwrap_or_default(); + ExpressionKind::TypePath(TypePath { typ, item, turbofish }) + }) +} + fn empty_path() -> impl NoirParser { let make_path = |kind| move |_, span| Path { segments: Vec::new(), kind, span }; let path_kind = |key, kind| keyword(key).map_with_span(make_path(kind)); diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index d4458190d03..9dd41d1a288 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -24,12 +24,7 @@ pub(super) fn parse_type_inner<'a>( recursive_type_parser: impl NoirParser + 'a, ) -> impl NoirParser + 'a { choice(( - field_type(), - int_type(), - bool_type(), - string_type(), - comptime_type(), - resolved_type(), + primitive_type(), format_string_type(recursive_type_parser.clone()), named_type(recursive_type_parser.clone()), named_trait(recursive_type_parser.clone()), @@ -40,6 +35,17 @@ pub(super) fn parse_type_inner<'a>( function_type(recursive_type_parser.clone()), mutable_reference_type(recursive_type_parser.clone()), as_trait_path_type(recursive_type_parser), + )) +} + +pub(super) fn primitive_type() -> impl NoirParser { + choice(( + field_type(), + int_type(), + bool_type(), + string_type(), + comptime_type(), + resolved_type(), interned_unresolved_type(), )) } diff --git a/test_programs/compile_success_empty/type_path/Nargo.toml b/test_programs/compile_success_empty/type_path/Nargo.toml new file mode 100644 index 00000000000..0e76437d15f --- /dev/null +++ b/test_programs/compile_success_empty/type_path/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "type_path" +type = "bin" +authors = [""] +compiler_version = ">=0.34.0" + +[dependencies] diff --git a/test_programs/compile_success_empty/type_path/src/main.nr b/test_programs/compile_success_empty/type_path/src/main.nr new file mode 100644 index 00000000000..6fc33820afc --- /dev/null +++ b/test_programs/compile_success_empty/type_path/src/main.nr @@ -0,0 +1,16 @@ +fn main() { + comptime { + let foo = quote { Foo }.as_type(); + quote { + $foo::static() + } + } +} + +struct Foo {} + +impl Foo { + fn static() { + println("Foo::static called"); + } +} diff --git a/tooling/lsp/src/requests/inlay_hint.rs b/tooling/lsp/src/requests/inlay_hint.rs index f05680903b8..2eef4f6e262 100644 --- a/tooling/lsp/src/requests/inlay_hint.rs +++ b/tooling/lsp/src/requests/inlay_hint.rs @@ -522,6 +522,7 @@ fn get_expression_name(expression: &Expression) -> Option { ExpressionKind::Cast(cast) => get_expression_name(&cast.lhs), ExpressionKind::Parenthesized(expr) => get_expression_name(expr), ExpressionKind::AsTraitPath(path) => Some(path.impl_item.to_string()), + ExpressionKind::TypePath(path) => Some(path.item.to_string()), ExpressionKind::Constructor(..) | ExpressionKind::Infix(..) | ExpressionKind::Index(..) diff --git a/tooling/nargo_fmt/src/rewrite/expr.rs b/tooling/nargo_fmt/src/rewrite/expr.rs index 81a7a219e06..873b5c87056 100644 --- a/tooling/nargo_fmt/src/rewrite/expr.rs +++ b/tooling/nargo_fmt/src/rewrite/expr.rs @@ -192,7 +192,20 @@ pub(crate) fn rewrite( } ExpressionKind::AsTraitPath(path) => { let trait_path = rewrite_path(visitor, shape, path.trait_path); - format!("<{} as {}>::{}", path.typ, trait_path, path.impl_item) + + if path.trait_generics.is_empty() { + format!("<{} as {}>::{}", path.typ, trait_path, path.impl_item) + } else { + let generics = path.trait_generics; + format!("<{} as {}::{}>::{}", path.typ, trait_path, generics, path.impl_item) + } + } + ExpressionKind::TypePath(path) => { + if path.turbofish.is_empty() { + format!("{}::{}", path.typ, path.item) + } else { + format!("{}::{}::{}", path.typ, path.item, path.turbofish) + } } } } From bf9f42f0e2293706358f1dc04ff286d9e6ed68bc Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 18 Sep 2024 15:30:15 -0500 Subject: [PATCH 2/3] Format test --- test_programs/compile_success_empty/type_path/src/main.nr | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test_programs/compile_success_empty/type_path/src/main.nr b/test_programs/compile_success_empty/type_path/src/main.nr index 6fc33820afc..215fad41736 100644 --- a/test_programs/compile_success_empty/type_path/src/main.nr +++ b/test_programs/compile_success_empty/type_path/src/main.nr @@ -1,5 +1,6 @@ fn main() { - comptime { + comptime + { let foo = quote { Foo }.as_type(); quote { $foo::static() From 035ecb3d6790d3fe2779b604532b6bdbc8ca7973 Mon Sep 17 00:00:00 2001 From: jfecher Date: Wed, 18 Sep 2024 17:21:37 -0500 Subject: [PATCH 3/3] Update test_programs/compile_success_empty/type_path/src/main.nr --- test_programs/compile_success_empty/type_path/src/main.nr | 1 - 1 file changed, 1 deletion(-) diff --git a/test_programs/compile_success_empty/type_path/src/main.nr b/test_programs/compile_success_empty/type_path/src/main.nr index 215fad41736..3b9c55b119d 100644 --- a/test_programs/compile_success_empty/type_path/src/main.nr +++ b/test_programs/compile_success_empty/type_path/src/main.nr @@ -12,6 +12,5 @@ struct Foo {} impl Foo { fn static() { - println("Foo::static called"); } }