Skip to content

Commit

Permalink
Fully resolve trait constraints call paths.
Browse files Browse the repository at this point in the history
  • Loading branch information
tritao committed Feb 25, 2025
1 parent 8788257 commit f2d9b84
Show file tree
Hide file tree
Showing 14 changed files with 188 additions and 108 deletions.
39 changes: 37 additions & 2 deletions sway-core/src/language/call_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,14 @@ impl<T: Spanned> Spanned for CallPath<T> {
}
}

/// This controls the type of display type for call path display string conversions.
pub enum CallPathDisplayType {
/// Prints the regular call path as exists internally.
Regular,
/// Strips the current root package if it exists as prefix.
StripPackagePrefix,
}

impl CallPath {
pub fn fullpath(path: &[&str]) -> Self {
assert!(!path.is_empty());
Expand Down Expand Up @@ -398,6 +406,27 @@ impl CallPath {
converted
}

pub fn to_display_path(
&self,
display_type: CallPathDisplayType,
namespace: &Namespace,
) -> CallPath {
let mut display_path = self.clone();

match display_type {
CallPathDisplayType::Regular => {}
CallPathDisplayType::StripPackagePrefix => {
if let Some(first) = self.prefixes.first() {
if namespace.root_ref().current_package_root_module().name() == first {
display_path = display_path.lshift();
}
}
}
};

display_path
}

/// Create a string form of the given [CallPath] and zero or more [TypeArgument]s.
/// The returned string is convenient for displaying full names, including generic arguments, in help messages.
/// E.g.:
Expand Down Expand Up @@ -553,11 +582,17 @@ impl CallPath {
Some(module) => {
// Resolve the path suffix in the found module
match module.resolve_symbol(&Handler::default(), engines, &full_path.suffix) {
Ok((_, decl_path)) => {
Ok((decl, decl_path)) => {
let name = decl.expect_typed().get_name(engines);
let suffix = if name.as_str() != full_path.suffix.as_str() {
name
} else {
full_path.suffix
};
// Replace the resolvable path with the declaration's path
CallPath {
prefixes: decl_path,
suffix: full_path.suffix.clone(),
suffix,
callpath_type: full_path.callpath_type,
}
}
Expand Down
62 changes: 61 additions & 1 deletion sway-core/src/language/ty/declaration/declaration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use sway_error::{
error::CompileError,
handler::{ErrorEmitted, Handler},
};
use sway_types::{Ident, Named, Span, Spanned};
use sway_types::{BaseIdent, Ident, Named, Span, Spanned};

#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum TyDecl {
Expand Down Expand Up @@ -667,6 +667,66 @@ impl TyDecl {
}
}

pub fn get_name(&self, engines: &Engines) -> BaseIdent {
match self {
TyDecl::VariableDecl(ty_variable_decl) => ty_variable_decl.name.clone(),
TyDecl::ConstantDecl(constant_decl) => engines
.de()
.get_constant(&constant_decl.decl_id)
.call_path
.suffix
.clone(),
TyDecl::ConfigurableDecl(configurable_decl) => engines
.de()
.get_configurable(&configurable_decl.decl_id)
.call_path
.suffix
.clone(),
TyDecl::TraitTypeDecl(trait_type_decl) => {
engines.de().get_type(&trait_type_decl.decl_id).name.clone()
}
TyDecl::FunctionDecl(function_decl) => engines
.de()
.get_function(&function_decl.decl_id)
.name
.clone(),
TyDecl::TraitDecl(trait_decl) => {
engines.de().get_trait(&trait_decl.decl_id).name.clone()
}
TyDecl::StructDecl(struct_decl) => engines
.de()
.get_struct(&struct_decl.decl_id)
.call_path
.suffix
.clone(),
TyDecl::EnumDecl(enum_decl) => engines
.de()
.get_enum(&enum_decl.decl_id)
.call_path
.suffix
.clone(),
TyDecl::EnumVariantDecl(_enum_variant_decl) => {
unreachable!()
}
TyDecl::ImplSelfOrTrait(impl_self_or_trait) => engines
.de()
.get_impl_self_or_trait(&impl_self_or_trait.decl_id)
.trait_name
.suffix
.clone(),
TyDecl::AbiDecl(abi_decl) => engines.de().get_abi(&abi_decl.decl_id).name.clone(),
TyDecl::GenericTypeForFunctionScope(_generic_type_for_function_scope) => unreachable!(),
TyDecl::ErrorRecovery(_span, _error_emitted) => unreachable!(),
TyDecl::StorageDecl(_storage_decl) => unreachable!(),
TyDecl::TypeAliasDecl(type_alias_decl) => engines
.de()
.get_type_alias(&type_alias_decl.decl_id)
.call_path
.suffix
.clone(),
}
}

/// Friendly name string used for error reporting,
/// which consists of the identifier for the declaration.
pub fn friendly_name(&self, engines: &Engines) -> String {
Expand Down
15 changes: 13 additions & 2 deletions sway-core/src/type_system/ast_elements/trait_constraint.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use crate::{
engine_threading::*,
language::{parsed::Supertrait, ty, CallPath},
language::{parsed::Supertrait, ty, CallPath, CallPathDisplayType},
semantic_analysis::{
declaration::{insert_supertraits_into_namespace, SupertraitOf},
TypeCheckContext,
},
type_system::priv_prelude::*,
types::{CollectTypesMetadata, CollectTypesMetadataContext, TypeMetadata},
EnforceTypeArguments,
EnforceTypeArguments, Namespace,
};
use serde::{Deserialize, Serialize};
use std::{
Expand Down Expand Up @@ -159,6 +159,10 @@ impl TraitConstraint {
}));
}

self.trait_name = self
.trait_name
.to_canonical_path(ctx.engines(), ctx.namespace());

// Type check the type arguments.
for type_argument in &mut self.type_arguments {
type_argument.type_id = ctx
Expand Down Expand Up @@ -259,4 +263,11 @@ impl TraitConstraint {

Ok(())
}

pub fn to_display_name(&self, engines: &Engines, namespace: &Namespace) -> String {
let display_path = self
.trait_name
.to_display_path(CallPathDisplayType::StripPackagePrefix, namespace);
display_path.to_string_with_args(engines, &self.type_arguments)
}
}
2 changes: 1 addition & 1 deletion sway-core/src/type_system/ast_elements/type_parameter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,7 @@ impl TypeParameter {
return Err(handler.emit_err(CompileError::MultipleImplsSatisfyingTraitForType{
span:access_span.clone(),
type_annotation: engines.help_out(type_id).to_string(),
trait_names: trait_constraints.iter().map(|t| engines.help_out(t).to_string()).collect(),
trait_names: trait_constraints.iter().map(|t| t.to_display_name(engines, ctx.namespace())).collect(),
trait_types_and_names: concrete_trait_type_ids.iter().map(|t| (engines.help_out(t.0).to_string(), t.1.clone())).collect::<Vec<_>>()
}));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,40 @@ category = "fail"
#check: $()impl Items2Trait<TestStruct2> for TestStruct1 {
#nextln: $()Trait "Items2Trait" cannot be found in the current scope.

#check: $()error
#check: $()fn project_items_3_struct(input: Items3_Struct) -> u64 {
#nextln: $()Could not find symbol "Items3_Struct" in this scope.

#check: $()error
#check: $()fn project_items_3_struct(input: Items3_Struct) -> u64 {
#nextln: $()Unknown type name "Items3_Struct".

#check: $()error
#check: $()fn project_items_3_enum(input: Items3_Enum) -> u64 {
#nextln: $()Could not find symbol "Items3_Enum" in this scope.

#check: $()error
#check: $()fn project_items_3_enum(input: Items3_Enum) -> u64 {
#nextln: $()Unknown type name "Items3_Enum".

#check: $()error
#check: $()fn project_items_3_variants(input: Items3_Variants) -> u64 {
#check: $()Could not find symbol "Items3_U" in this scope.

#check: $()error
#check: $()Could not find symbol "Items3_V" in this scope.

#check: $()error
#check: $()fn call_items_3_function() -> u64 {
#check: $()Could not find symbol "items_3_function" in this scope.

#check: $()error
#check: $()impl Items3Trait<TestStruct2> for TestStruct1 {
#check: $()Could not find symbol "Items3Trait" in this scope.

#check: $()Trait is already implemented for type
#check: $()Trait "aliases::items_5::Items5Trait<TestStruct2>" is already implemented for type "TestStruct1".

#check: $()error
#check: $()let items_2_struct = Items2_Struct { b: 123 };
#nextln: $()Could not find symbol "Items2_Struct" in this scope.
Expand Down Expand Up @@ -181,8 +215,5 @@ category = "fail"
#check: $()let items_3_trait_teststruct_1_res = teststruct_1.items_3_trait_function(teststruct_2);
#nextln: $()No method "items_3_trait_function(TestStruct1, TestStruct2) -> bool" found for type "TestStruct1".

#check: $()error
#check: $()let items_5_trait_teststruct_1_res = teststruct_1.items_5_trait_function(teststruct_2);
#nextln: $()Multiple applicable items in scope.
#check: $()Aborting due to 55 errors.

#check: $()Aborting due to 55 errors.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
script;

pub mod lib;
pub mod other_lib;
mod trait_impls;

// Previously this was a `should_fail` test that checked if we emitted an error
// related to the hash trait in sha256 trait constraint not being explicitly imported.

// After we changed trait constraints paths to be fully resolved, then lookup
// of those types now works transparently even without the trait being imported here.

// In fact the previous behavior was problematic since a locally defined trait with the
// same name would take precedence, thus making trait constraint type lookups in essen
// dynamically scoped.

use std::hash::sha256;

use ::lib::{S, A, function};
use ::trait_impls::*;

fn main() {
let _ = sha256(123u8);

let s = S {};
s.method_01(0u8);
s.method_02(A {});
S::associated_function(A {});

function(A {});

let a = A {};

a.trait_method(A {});

A::trait_associated_function(A {});

function_with_duplicated_trait(A {});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
category = "compile"
expected_warnings = 10

0 comments on commit f2d9b84

Please sign in to comment.