Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fully resolve trait constraints call paths. #6962

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading