Skip to content

Commit

Permalink
Implements qualified paths for associated types. (#5160)
Browse files Browse the repository at this point in the history
## Description

Adds support for checking if using fully qualified call paths is
necessary on associated types disambiguation.

Adds support for specifying fully qualified paths of associated types
used in type ascriptions.

Adds support for specifying fully qualified path when calling methods of
associated types and when using associated consts of associated types.

## Checklist

- [x] I have linked to any relevant issues.
- [x] I have commented my code, particularly in hard-to-understand
areas.
- [ ] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [x] I have added tests that prove my fix is effective or that my
feature works.
- [x] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [x] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [x] I have requested a review from the relevant team or maintainers.
  • Loading branch information
esdrubal authored Oct 13, 2023
1 parent 53fd58d commit ae8a39c
Show file tree
Hide file tree
Showing 40 changed files with 1,424 additions and 526 deletions.
7 changes: 5 additions & 2 deletions forc-plugins/forc-doc/src/render/item/type_anchor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,11 @@ pub(crate) fn render_type_anchor(
Err(anyhow!("Deferred AbiName is unhandled"))
}
}
TypeInfo::Custom { call_path, .. } => Ok(box_html! {
: call_path.suffix.as_str();
TypeInfo::Custom {
qualified_call_path,
..
} => Ok(box_html! {
: qualified_call_path.call_path.suffix.as_str();
}),
TypeInfo::B256 => Ok(box_html! {
: "b256";
Expand Down
5 changes: 4 additions & 1 deletion sway-core/src/abi_generation/evm_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,10 @@ pub fn abi_str(type_info: &TypeInfo, type_engine: &TypeEngine, decl_engine: &Dec
}
.into(),
Boolean => "bool".into(),
Custom { call_path, .. } => call_path.suffix.to_string(),
Custom {
qualified_call_path: call_path,
..
} => call_path.call_path.suffix.to_string(),
Tuple(fields) => {
let field_strs = fields
.iter()
Expand Down
5 changes: 4 additions & 1 deletion sway-core/src/abi_generation/fuel_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -799,7 +799,10 @@ impl TypeInfo {
}
.into(),
Boolean => "bool".into(),
Custom { call_path, .. } => call_path.suffix.to_string(),
Custom {
qualified_call_path: call_path,
..
} => call_path.call_path.suffix.to_string(),
Tuple(fields) => {
let field_strs = fields
.iter()
Expand Down
49 changes: 49 additions & 0 deletions sway-core/src/engine_threading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,21 @@ impl<T: DisplayWithEngines> DisplayWithEngines for &T {
}
}

impl<T: DisplayWithEngines> DisplayWithEngines for Option<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
match self {
None => Ok(()),
Some(x) => x.fmt(f, engines),
}
}
}

impl<T: DisplayWithEngines> DisplayWithEngines for Box<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
(**self).fmt(f, engines)
}
}

pub(crate) trait DebugWithEngines {
fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result;
}
Expand All @@ -134,6 +149,21 @@ impl<T: DebugWithEngines> DebugWithEngines for &T {
}
}

impl<T: DebugWithEngines> DebugWithEngines for Option<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
match self {
None => Ok(()),
Some(x) => x.fmt(f, engines),
}
}
}

impl<T: DebugWithEngines> DebugWithEngines for Box<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
(**self).fmt(f, engines)
}
}

pub trait HashWithEngines {
fn hash<H: Hasher>(&self, state: &mut H, engines: &Engines);
}
Expand Down Expand Up @@ -161,6 +191,12 @@ impl<T: HashWithEngines> HashWithEngines for [T] {
}
}

impl<T: HashWithEngines> HashWithEngines for Box<T> {
fn hash<H: Hasher>(&self, state: &mut H, engines: &Engines) {
(**self).hash(state, engines)
}
}

pub trait EqWithEngines: PartialEqWithEngines {}

pub trait PartialEqWithEngines {
Expand Down Expand Up @@ -194,6 +230,12 @@ impl<T: OrdWithEngines> OrdWithEngines for Option<T> {
}
}

impl<T: OrdWithEngines> OrdWithEngines for Box<T> {
fn cmp(&self, other: &Self, engines: &Engines) -> Ordering {
(**self).cmp(&(**other), engines)
}
}

impl<T: EqWithEngines> EqWithEngines for Option<T> {}
impl<T: PartialEqWithEngines> PartialEqWithEngines for Option<T> {
fn eq(&self, other: &Self, engines: &Engines) -> bool {
Expand All @@ -205,6 +247,13 @@ impl<T: PartialEqWithEngines> PartialEqWithEngines for Option<T> {
}
}

impl<T: EqWithEngines> EqWithEngines for Box<T> {}
impl<T: PartialEqWithEngines> PartialEqWithEngines for Box<T> {
fn eq(&self, other: &Self, engines: &Engines) -> bool {
(**self).eq(&(**other), engines)
}
}

impl<T: EqWithEngines> EqWithEngines for [T] {}
impl<T: PartialEqWithEngines> PartialEqWithEngines for [T] {
fn eq(&self, other: &Self, engines: &Engines) -> bool {
Expand Down
184 changes: 180 additions & 4 deletions sway-core/src/language/call_path.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,173 @@
use std::{fmt, sync::Arc};
use std::{
cmp::Ordering,
fmt,
hash::{Hash, Hasher},
sync::Arc,
};

use crate::{Ident, Namespace};
use crate::{
engine_threading::{
DebugWithEngines, DisplayWithEngines, EqWithEngines, HashWithEngines, OrdWithEngines,
PartialEqWithEngines,
},
Engines, Ident, Namespace,
};

use sway_error::{
error::CompileError,
handler::{ErrorEmitted, Handler},
};
use sway_types::{span::Span, Spanned};

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
use super::parsed::QualifiedPathRootTypes;

#[derive(Clone, Debug)]
pub struct CallPathTree {
pub call_path: CallPath,
pub qualified_call_path: QualifiedCallPath,
pub children: Vec<CallPathTree>,
}

impl HashWithEngines for CallPathTree {
fn hash<H: Hasher>(&self, state: &mut H, engines: &Engines) {
let CallPathTree {
qualified_call_path,
children,
} = self;
qualified_call_path.hash(state, engines);
children.hash(state, engines);
}
}

impl EqWithEngines for CallPathTree {}
impl PartialEqWithEngines for CallPathTree {
fn eq(&self, other: &Self, engines: &Engines) -> bool {
let CallPathTree {
qualified_call_path,
children,
} = self;
qualified_call_path.eq(&other.qualified_call_path, engines)
&& children.eq(&other.children, engines)
}
}

impl OrdWithEngines for CallPathTree {
fn cmp(&self, other: &Self, engines: &Engines) -> Ordering {
let CallPathTree {
qualified_call_path: l_call_path,
children: l_children,
} = self;
let CallPathTree {
qualified_call_path: r_call_path,
children: r_children,
} = other;
l_call_path
.cmp(r_call_path, engines)
.then_with(|| l_children.cmp(r_children, engines))
}
}

#[derive(Clone, Debug)]

pub struct QualifiedCallPath {
pub call_path: CallPath,
pub qualified_path_root: Option<Box<QualifiedPathRootTypes>>,
}

impl std::convert::From<Ident> for QualifiedCallPath {
fn from(other: Ident) -> Self {
QualifiedCallPath {
call_path: CallPath {
prefixes: vec![],
suffix: other,
is_absolute: false,
},
qualified_path_root: None,
}
}
}

impl std::convert::From<CallPath> for QualifiedCallPath {
fn from(other: CallPath) -> Self {
QualifiedCallPath {
call_path: other,
qualified_path_root: None,
}
}
}

impl QualifiedCallPath {
pub fn to_call_path(self, handler: &Handler) -> Result<CallPath, ErrorEmitted> {
if let Some(qualified_path_root) = self.qualified_path_root {
Err(handler.emit_err(CompileError::Internal(
"Unexpected qualified path.",
qualified_path_root.as_trait_span,
)))
} else {
Ok(self.call_path)
}
}
}

impl HashWithEngines for QualifiedCallPath {
fn hash<H: Hasher>(&self, state: &mut H, engines: &Engines) {
let QualifiedCallPath {
call_path,
qualified_path_root,
} = self;
call_path.hash(state);
qualified_path_root.hash(state, engines);
}
}

impl EqWithEngines for QualifiedCallPath {}
impl PartialEqWithEngines for QualifiedCallPath {
fn eq(&self, other: &Self, engines: &Engines) -> bool {
let QualifiedCallPath {
call_path,
qualified_path_root,
} = self;
call_path.eq(&other.call_path)
&& qualified_path_root.eq(&other.qualified_path_root, engines)
}
}

impl OrdWithEngines for QualifiedCallPath {
fn cmp(&self, other: &Self, engines: &Engines) -> Ordering {
let QualifiedCallPath {
call_path: l_call_path,
qualified_path_root: l_qualified_path_root,
} = self;
let QualifiedCallPath {
call_path: r_call_path,
qualified_path_root: r_qualified_path_root,
} = other;
l_call_path
.cmp(r_call_path)
.then_with(|| l_qualified_path_root.cmp(r_qualified_path_root, engines))
}
}

impl DisplayWithEngines for QualifiedCallPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
if let Some(qualified_path_root) = &self.qualified_path_root {
write!(
f,
"{}::{}",
engines.help_out(qualified_path_root),
&self.call_path
)
} else {
write!(f, "{}", &self.call_path)
}
}
}

impl DebugWithEngines for QualifiedCallPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
write!(f, "{}", engines.help_out(self))
}
}

/// in the expression `a::b::c()`, `a` and `b` are the prefixes and `c` is the suffix.
/// `c` can be any type `T`, but in practice `c` is either an `Ident` or a `TypeInfo`.
#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
Expand Down Expand Up @@ -43,6 +201,24 @@ where
}
}

impl<T: DisplayWithEngines> DisplayWithEngines for CallPath<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
for prefix in self.prefixes.iter() {
write!(f, "{}::", prefix.as_str())?;
}
write!(f, "{}", engines.help_out(&self.suffix))
}
}

impl<T: DisplayWithEngines> DebugWithEngines for CallPath<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
for prefix in self.prefixes.iter() {
write!(f, "{}::", prefix.as_str())?;
}
write!(f, "{}", engines.help_out(&self.suffix))
}
}

impl<T: Spanned> Spanned for CallPath<T> {
fn span(&self) -> Span {
if self.prefixes.is_empty() {
Expand Down
4 changes: 2 additions & 2 deletions sway-core/src/language/parsed/expression/method_name.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::language::CallPath;
use crate::type_system::TypeBinding;
use crate::{Ident, TypeArgument, TypeInfo};
use crate::{Ident, TypeArgument, TypeId, TypeInfo};

#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone)]
Expand All @@ -24,7 +24,7 @@ pub enum MethodName {
/// like <S as Trait>::method()
FromQualifiedPathRoot {
ty: TypeArgument,
as_trait: TypeInfo,
as_trait: TypeId,
method_name: Ident,
},
}
Expand Down
Loading

0 comments on commit ae8a39c

Please sign in to comment.