From 6a16d3294bae4ae30802e0d8942b3f0f555ec074 Mon Sep 17 00:00:00 2001 From: Joao Matos Date: Mon, 24 Jun 2024 14:31:20 +0100 Subject: [PATCH] Implement `DelineatedPathExpression` AST resolve handling. --- sway-core/src/language/parsed/declaration.rs | 30 +- .../src/language/parsed/expression/mod.rs | 4 +- .../ast_node/expression/typed_expression.rs | 1 + .../semantic_analysis/node_dependencies.rs | 1 + .../src/semantic_analysis/symbol_resolve.rs | 47 ++- .../to_parsed_lang/convert_parse_tree.rs | 1 + .../src/type_system/ast_elements/binding.rs | 288 ++++++++++++++++-- sway-lsp/src/traverse/parsed_tree.rs | 1 + 8 files changed, 342 insertions(+), 31 deletions(-) diff --git a/sway-core/src/language/parsed/declaration.rs b/sway-core/src/language/parsed/declaration.rs index 2489519ab7f..97537dfa473 100644 --- a/sway-core/src/language/parsed/declaration.rs +++ b/sway-core/src/language/parsed/declaration.rs @@ -118,7 +118,7 @@ impl Declaration { } } - pub(crate) fn to_fn_ref( + pub(crate) fn to_fn_decl( &self, handler: &Handler, engines: &Engines, @@ -146,6 +146,34 @@ impl Declaration { } } + pub(crate) fn to_enum_decl( + &self, + handler: &Handler, + engines: &Engines, + ) -> Result, ErrorEmitted> { + match self { + Declaration::EnumDeclaration(decl_id) => Ok(*decl_id), + decl => Err(handler.emit_err(CompileError::DeclIsNotAnEnum { + actually: decl.friendly_type_name().to_string(), + span: decl.span(engines), + })), + } + } + + pub(crate) fn to_const_decl( + &self, + handler: &Handler, + engines: &Engines, + ) -> Result, ErrorEmitted> { + match self { + Declaration::ConstantDeclaration(decl_id) => Ok(*decl_id), + decl => Err(handler.emit_err(CompileError::DeclIsNotAConstant { + actually: decl.friendly_type_name().to_string(), + span: decl.span(engines), + })), + } + } + #[allow(unused)] pub(crate) fn visibility(&self, decl_engine: &ParsedDeclEngine) -> Visibility { match self { diff --git a/sway-core/src/language/parsed/expression/mod.rs b/sway-core/src/language/parsed/expression/mod.rs index 90d41fbf19b..557a89c03de 100644 --- a/sway-core/src/language/parsed/expression/mod.rs +++ b/sway-core/src/language/parsed/expression/mod.rs @@ -23,7 +23,7 @@ pub use method_name::MethodName; pub use scrutinee::*; use sway_ast::intrinsics::Intrinsic; -use super::{FunctionDeclaration, StructDeclaration}; +use super::{Declaration, FunctionDeclaration, StructDeclaration}; /// Represents a parsed, but not yet type checked, [Expression](https://en.wikipedia.org/wiki/Expression_(computer_science)). #[derive(Debug, Clone)] @@ -295,6 +295,8 @@ impl PartialEqWithEngines for AmbiguousPathExpression { #[derive(Debug, Clone)] pub struct DelineatedPathExpression { pub call_path_binding: TypeBinding, + pub resolved_call_path_binding: Option>>, + /// When args is equal to Option::None then it means that the /// [DelineatedPathExpression] was initialized from an expression /// that does not end with parenthesis. diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs index 62f33b69428..ac675b8be6c 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -307,6 +307,7 @@ impl ty::TyExpression { ExpressionKind::DelineatedPath(delineated_path_expression) => { let DelineatedPathExpression { call_path_binding, + resolved_call_path_binding: _, args, } = *delineated_path_expression.clone(); Self::type_check_delineated_path( diff --git a/sway-core/src/semantic_analysis/node_dependencies.rs b/sway-core/src/semantic_analysis/node_dependencies.rs index 77253e08562..45b6b9f92dc 100644 --- a/sway-core/src/semantic_analysis/node_dependencies.rs +++ b/sway-core/src/semantic_analysis/node_dependencies.rs @@ -618,6 +618,7 @@ impl Dependencies { ExpressionKind::DelineatedPath(delineated_path_expression) => { let DelineatedPathExpression { call_path_binding, + resolved_call_path_binding: _, args, } = &**delineated_path_expression; // It's either a module path which we can ignore, or an enum variant path, in which diff --git a/sway-core/src/semantic_analysis/symbol_resolve.rs b/sway-core/src/semantic_analysis/symbol_resolve.rs index aa21731f78d..1c11772b319 100644 --- a/sway-core/src/semantic_analysis/symbol_resolve.rs +++ b/sway-core/src/semantic_analysis/symbol_resolve.rs @@ -15,7 +15,7 @@ use crate::{ }, CallPath, CallPathTree, ResolvedCallPath, }, - TraitConstraint, TypeArgument, TypeBinding, TypeParameter, + TraitConstraint, TypeArgs, TypeArgument, TypeBinding, TypeParameter, }; use super::symbol_resolve_context::SymbolResolveContext; @@ -491,14 +491,8 @@ impl ResolveSymbols for StructScrutineeField { } impl ResolveSymbols for Expression { - fn resolve_symbols(&mut self, handler: &Handler, ctx: SymbolResolveContext) { - self.kind.resolve_symbols(handler, ctx); - } -} - -impl ResolveSymbols for ExpressionKind { fn resolve_symbols(&mut self, handler: &Handler, mut ctx: SymbolResolveContext) { - match self { + match &mut self.kind { ExpressionKind::Error(_, _) => {} ExpressionKind::Literal(_) => {} ExpressionKind::AmbiguousPathExpression(_) => {} @@ -599,8 +593,28 @@ impl ResolveSymbols for ExpressionKind { .for_each(|arg| arg.resolve_symbols(handler, ctx.by_ref())); } ExpressionKind::Subfield(expr) => expr.prefix.resolve_symbols(handler, ctx), - ExpressionKind::DelineatedPath(expr) => { - expr.call_path_binding.resolve_symbols(handler, ctx) + ExpressionKind::DelineatedPath(ref mut expr) => { + expr.call_path_binding + .resolve_symbols(handler, ctx.by_ref()); + + let result = expr.call_path_binding.resolve_symbol( + handler, + ctx.by_ref(), + self.span.clone() + ); + + // if let Ok(result) = result { + // expr.resolved_call_path_binding = Some(TypeBinding::< + // ResolvedCallPath>, + // > { + // inner: ResolvedCallPath { + // decl: result, + // unresolved_call_path: expr.call_path_binding.inner.clone(), + // }, + // span: expr.call_path_binding.span.clone(), + // type_arguments: expr.call_path_binding.type_arguments.clone(), + // }); + // } } ExpressionKind::AbiCast(expr) => { expr.abi_name.resolve_symbols(handler, ctx.by_ref()); @@ -640,3 +654,16 @@ impl ResolveSymbols for ExpressionKind { } } } + +impl ResolveSymbols for TypeBinding { + fn resolve_symbols(&mut self, handler: &Handler, mut ctx: SymbolResolveContext) { + match self.type_arguments { + TypeArgs::Regular(ref mut args) => args + .iter_mut() + .for_each(|arg| arg.resolve_symbols(handler, ctx.by_ref())), + TypeArgs::Prefix(ref mut args) => args + .iter_mut() + .for_each(|arg| arg.resolve_symbols(handler, ctx.by_ref())), + } + } +} diff --git a/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs b/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs index 55902d3d858..ba3a9469e89 100644 --- a/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs +++ b/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs @@ -2900,6 +2900,7 @@ fn path_expr_to_expression( Expression { kind: ExpressionKind::DelineatedPath(Box::new(DelineatedPathExpression { call_path_binding, + resolved_call_path_binding: None, args: None, })), span, diff --git a/sway-core/src/type_system/ast_elements/binding.rs b/sway-core/src/type_system/ast_elements/binding.rs index 33e468e786c..c9901737274 100644 --- a/sway-core/src/type_system/ast_elements/binding.rs +++ b/sway-core/src/type_system/ast_elements/binding.rs @@ -1,5 +1,9 @@ use sway_ast::Intrinsic; -use sway_error::handler::{ErrorEmitted, Handler}; +use sway_error::{ + convert_parse_tree_error::ConvertParseTreeError, + error::CompileError, + handler::{ErrorEmitted, Handler}, +}; use sway_types::{Span, Spanned}; use crate::{ @@ -8,13 +12,17 @@ use crate::{ }, engine_threading::{EqWithEngines, PartialEqWithEngines, PartialEqWithEnginesContext}, language::{ - parsed::{FunctionDeclaration, StructDeclaration}, + parsed::{ + ConstantDeclaration, Declaration, EnumDeclaration, EnumVariantDeclaration, + FunctionDeclaration, StructDeclaration, + }, ty, CallPath, QualifiedCallPath, }, semantic_analysis::{ - symbol_resolve::ResolveSymbols, symbol_resolve_context::SymbolResolveContext, - type_check_context::EnforceTypeArguments, TypeCheckContext, + symbol_resolve_context::SymbolResolveContext, type_check_context::EnforceTypeArguments, + TypeCheckContext, }, + transform::to_parsed_lang::type_name_to_type_info_opt, type_system::priv_prelude::*, Ident, }; @@ -192,17 +200,6 @@ impl TypeBinding { span: self.span, } } - - pub(crate) fn resolve_symbols(&mut self, handler: &Handler, mut ctx: SymbolResolveContext<'_>) { - match self.type_arguments { - TypeArgs::Regular(ref mut args) => args - .iter_mut() - .for_each(|arg| arg.resolve_symbols(handler, ctx.by_ref())), - TypeArgs::Prefix(ref mut args) => args - .iter_mut() - .for_each(|arg| arg.resolve_symbols(handler, ctx.by_ref())), - } - } } impl TypeBinding> { @@ -274,10 +271,10 @@ pub trait SymbolResolveTypeBinding { &mut self, handler: &Handler, ctx: SymbolResolveContext, - ) -> Result, ErrorEmitted>; + ) -> Result; } -impl SymbolResolveTypeBinding for TypeBinding { +impl SymbolResolveTypeBinding> for TypeBinding { fn resolve_symbol( &mut self, handler: &Handler, @@ -287,11 +284,41 @@ impl SymbolResolveTypeBinding for TypeBinding { // Grab the declaration. let unknown_decl = ctx.resolve_call_path_with_visibility_check(handler, &self.inner)?; // Check to see if this is a function declaration. - let fn_decl = unknown_decl.expect_parsed().to_fn_ref(handler, engines)?; + let fn_decl = unknown_decl.expect_parsed().to_fn_decl(handler, engines)?; Ok(fn_decl) } } +impl SymbolResolveTypeBinding> for TypeBinding { + fn resolve_symbol( + &mut self, + handler: &Handler, + ctx: SymbolResolveContext, + ) -> Result, ErrorEmitted> { + let engines = ctx.engines(); + let parsed_decl_engine = engines.pe(); + + // Grab the declaration. + let unknown_decl = ctx + .resolve_call_path_with_visibility_check(handler, &self.inner)? + .expect_parsed(); + + // Check to see if this is a enum declaration. + let enum_decl = if let Declaration::EnumVariantDeclaration(EnumVariantDeclaration { + enum_ref, + .. + }) = &unknown_decl + { + *enum_ref + } else { + // Check to see if this is a enum declaration. + unknown_decl.to_enum_decl(handler, engines)? + }; + + Ok(enum_decl) + } +} + impl TypeCheckTypeBinding for TypeBinding { fn type_check( &mut self, @@ -352,7 +379,7 @@ impl TypeCheckTypeBinding for TypeBinding { } } -impl SymbolResolveTypeBinding for TypeBinding { +impl SymbolResolveTypeBinding> for TypeBinding { fn resolve_symbol( &mut self, handler: &Handler, @@ -464,6 +491,211 @@ impl TypeCheckTypeBinding for TypeBinding { } } +impl TypeBinding { + pub fn resolve_symbol( + &mut self, + handler: &Handler, + mut ctx: SymbolResolveContext, + span: Span, + ) -> Result { + let engines = ctx.engines(); + + // The first step is to determine if the call path refers to a module, + // enum, function or constant. + // If only one exists, then we use that one. Otherwise, if more than one exist, it is + // an ambiguous reference error. + + let mut is_module = false; + let mut maybe_function: Option<(ParsedDeclId, _)> = None; + let mut maybe_enum: Option<(ParsedDeclId, _, _)> = None; + + let module_probe_handler = Handler::default(); + let function_probe_handler = Handler::default(); + let enum_probe_handler = Handler::default(); + let const_probe_handler = Handler::default(); + + if self.inner.qualified_path_root.is_none() { + // Check if this could be a module + is_module = { + let call_path_binding = self.clone(); + ctx.namespace().program_id(engines).read(engines, |m| { + m.lookup_submodule( + &module_probe_handler, + engines, + &[ + call_path_binding.inner.call_path.prefixes.clone(), + vec![call_path_binding.inner.call_path.suffix.clone()], + ] + .concat(), + ) + .ok() + .is_some() + }) + }; + + // Check if this could be a function + maybe_function = { + let call_path_binding = self.clone(); + let mut call_path_binding = TypeBinding { + inner: call_path_binding.inner.call_path, + type_arguments: call_path_binding.type_arguments, + span: call_path_binding.span, + }; + + let result: Result, ErrorEmitted> = + SymbolResolveTypeBinding::resolve_symbol( + &mut call_path_binding, + &function_probe_handler, + ctx.by_ref(), + ); + + result.ok().map(|fn_ref| (fn_ref, call_path_binding)) + }; + + // Check if this could be an enum + maybe_enum = { + let call_path_binding = self.clone(); + let variant_name = call_path_binding.inner.call_path.suffix.clone(); + let enum_call_path = call_path_binding.inner.call_path.rshift(); + + let mut call_path_binding = TypeBinding { + inner: enum_call_path, + type_arguments: call_path_binding.type_arguments, + span: call_path_binding.span, + }; + + let result: Result, ErrorEmitted> = + SymbolResolveTypeBinding::resolve_symbol( + &mut call_path_binding, + &enum_probe_handler, + ctx.by_ref(), + ); + + result + .ok() + .map(|enum_ref| (enum_ref, variant_name, call_path_binding)) + }; + }; + + // Check if this could be a constant + let maybe_const = SymbolResolveTypeBinding::<( + ParsedDeclId, + TypeBinding, + )>::resolve_symbol(self, &const_probe_handler, ctx.by_ref()) + .ok(); + + // compare the results of the checks + let exp = match (is_module, maybe_function, maybe_enum, maybe_const) { + (false, None, Some((enum_ref, variant_name, call_path_binding)), None) => { + handler.append(enum_probe_handler); + Declaration::EnumDeclaration(enum_ref) + } + (false, Some((fn_ref, call_path_binding)), None, None) => { + handler.append(function_probe_handler); + // In case `foo::bar::::baz(...)` throw an error. + if let TypeArgs::Prefix(_) = call_path_binding.type_arguments { + handler.emit_err( + ConvertParseTreeError::GenericsNotSupportedHere { + span: call_path_binding.type_arguments.span(), + } + .into(), + ); + } + Declaration::FunctionDeclaration(fn_ref) + } + (true, None, None, None) => { + handler.append(module_probe_handler); + return Err(handler.emit_err(CompileError::ModulePathIsNotAnExpression { + module_path: self.inner.call_path.to_string(), + span, + })); + } + (false, None, None, Some((const_ref, call_path_binding))) => { + handler.append(const_probe_handler); + if !call_path_binding.type_arguments.to_vec().is_empty() { + // In case `foo::bar::CONST::` throw an error. + // In case `foo::bar::::CONST` throw an error. + handler.emit_err( + ConvertParseTreeError::GenericsNotSupportedHere { + span: self.type_arguments.span(), + } + .into(), + ); + } + Declaration::ConstantDeclaration(const_ref) + } + (false, None, None, None) => { + return Err(handler.emit_err(CompileError::SymbolNotFound { + name: self.inner.call_path.suffix.clone(), + span: self.inner.call_path.suffix.span(), + })); + } + _ => { + return Err(handler.emit_err(CompileError::AmbiguousPath { span })); + } + }; + + Ok(exp) + } +} + +impl SymbolResolveTypeBinding<(ParsedDeclId, TypeBinding)> + for TypeBinding +{ + fn resolve_symbol( + &mut self, + handler: &Handler, + mut ctx: SymbolResolveContext, + ) -> Result<(ParsedDeclId, TypeBinding), ErrorEmitted> { + let mut qualified_call_path_binding = self.clone(); + + let mut call_path_binding = TypeBinding { + inner: qualified_call_path_binding.inner.call_path.clone(), + type_arguments: qualified_call_path_binding.type_arguments.clone(), + span: qualified_call_path_binding.span.clone(), + }; + + let type_info_opt = call_path_binding + .clone() + .inner + .prefixes + .last() + .map(|type_name| { + type_name_to_type_info_opt(type_name).unwrap_or(TypeInfo::Custom { + qualified_call_path: type_name.clone().into(), + type_arguments: None, + root_type_id: None, + }) + }); + + if let Some(type_info) = type_info_opt { + if TypeInfo::is_self_type(&type_info) { + call_path_binding.strip_prefixes(); + } + } + + let const_res: Result, ErrorEmitted> = + SymbolResolveTypeBinding::resolve_symbol( + &mut call_path_binding, + &Handler::default(), + ctx.by_ref(), + ); + if const_res.is_ok() { + return const_res.map(|const_ref| (const_ref, call_path_binding.clone())); + } + + // If we didn't find a constant, check for the constant inside the impl. + let unknown_decl = ctx + .resolve_qualified_call_path_with_visibility_check(handler, &self.inner)? + .expect_parsed(); + + // Check to see if this is a const declaration. + let const_ref = unknown_decl.to_const_decl(handler, ctx.engines())?; + + Ok((const_ref, call_path_binding.clone())) + } +} + impl TypeBinding { pub(crate) fn type_check_qualified( &mut self, @@ -503,3 +735,21 @@ impl TypeCheckTypeBinding for TypeBinding { Ok((const_ref, None, None)) } } + +impl SymbolResolveTypeBinding> for TypeBinding { + fn resolve_symbol( + &mut self, + handler: &Handler, + ctx: SymbolResolveContext, + ) -> Result, ErrorEmitted> { + // Grab the declaration. + let unknown_decl = ctx + .resolve_call_path_with_visibility_check(handler, &self.inner)? + .expect_parsed(); + + // Check to see if this is a const declaration. + let const_ref = unknown_decl.to_const_decl(handler, ctx.engines())?; + + Ok(const_ref) + } +} diff --git a/sway-lsp/src/traverse/parsed_tree.rs b/sway-lsp/src/traverse/parsed_tree.rs index 39ba771f879..31b31fbb2cf 100644 --- a/sway-lsp/src/traverse/parsed_tree.rs +++ b/sway-lsp/src/traverse/parsed_tree.rs @@ -412,6 +412,7 @@ impl Parse for DelineatedPathExpression { let DelineatedPathExpression { call_path_binding, args, + resolved_call_path_binding: _, } = self; adaptive_iter(&call_path_binding.inner.call_path.prefixes, |ident| { ctx.tokens.insert(