Skip to content

Commit

Permalink
Improved trait coherence checking
Browse files Browse the repository at this point in the history
  • Loading branch information
tritao committed Feb 13, 2025
1 parent 8788257 commit e4ea1d3
Show file tree
Hide file tree
Showing 57 changed files with 1,003 additions and 38 deletions.
24 changes: 22 additions & 2 deletions forc-pkg/src/pkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use std::{
str::FromStr,
sync::{atomic::AtomicBool, Arc},
};
use sway_core::namespace::Root;
pub use sway_core::Programs;
use sway_core::{
abi_generation::{
Expand All @@ -50,7 +51,7 @@ use sway_core::{set_bytecode_configurables_offset, PrintAsm, PrintIr};
use sway_error::{error::CompileError, handler::Handler, warning::CompileWarning};
use sway_features::ExperimentalFeatures;
use sway_types::constants::{CORE, STD};
use sway_types::{Ident, Span, Spanned};
use sway_types::{Ident, ProgramId, Span, Spanned};
use sway_utils::{constants, time_expr, PerformanceData, PerformanceMetric};
use tracing::{debug, info};

Expand Down Expand Up @@ -1581,13 +1582,15 @@ pub fn sway_build_config(
///
/// `contract_id_value` should only be Some when producing the `dependency_namespace` for a contract with tests enabled.
/// This allows us to provide a contract's `CONTRACT_ID` constant to its own unit tests.
#[allow(clippy::too_many_arguments)]
pub fn dependency_namespace(
lib_namespace_map: &HashMap<NodeIx, namespace::Root>,
compiled_contract_deps: &CompiledContractDeps,
graph: &Graph,
node: NodeIx,
engines: &Engines,
contract_id_value: Option<ContractIdConst>,
program_id: ProgramId,
experimental: ExperimentalFeatures,
) -> Result<namespace::Root, vec1::Vec1<CompileError>> {
// TODO: Clean this up when config-time constants v1 are removed.
Expand All @@ -1597,11 +1600,12 @@ pub fn dependency_namespace(
namespace::namespace_with_contract_id(
engines,
name.clone(),
program_id,
contract_id_value,
experimental,
)?
} else {
namespace::namespace_without_contract_id(name.clone())
Root::new(name.clone(), None, program_id, false)
};

// Add direct dependencies.
Expand Down Expand Up @@ -1629,6 +1633,7 @@ pub fn dependency_namespace(
namespace::namespace_with_contract_id(
engines,
name.clone(),
program_id,
contract_id_value,
experimental,
)?
Expand Down Expand Up @@ -2444,6 +2449,10 @@ pub fn build(
..profile.clone()
};

let program_id = engines
.se()
.get_or_create_program_id_from_manifest_path(&manifest.entry_path());

// `ContractIdConst` is a None here since we do not yet have a
// contract ID value at this point.
let dep_namespace = match dependency_namespace(
Expand All @@ -2453,6 +2462,7 @@ pub fn build(
node,
&engines,
None,
program_id,
experimental,
) {
Ok(o) => o,
Expand Down Expand Up @@ -2510,6 +2520,10 @@ pub fn build(
profile.clone()
};

let program_id = engines
.se()
.get_or_create_program_id_from_manifest_path(&manifest.entry_path());

// Note that the contract ID value here is only Some if tests are enabled.
let dep_namespace = match dependency_namespace(
&lib_namespace_map,
Expand All @@ -2518,6 +2532,7 @@ pub fn build(
node,
&engines,
contract_id_value.clone(),
program_id,
experimental,
) {
Ok(o) => o,
Expand Down Expand Up @@ -2617,13 +2632,18 @@ pub fn check(
let contract_id_value =
(idx == plan.compilation_order.len() - 1).then(|| DUMMY_CONTRACT_ID.to_string());

let program_id = engines
.se()
.get_or_create_program_id_from_manifest_path(&manifest.entry_path());

let dep_namespace = dependency_namespace(
&lib_namespace_map,
&compiled_contract_deps,
&plan.graph,
node,
engines,
contract_id_value,
program_id,
experimental,
)
.expect("failed to create dependency namespace");
Expand Down
39 changes: 38 additions & 1 deletion sway-core/src/language/ty/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ use std::sync::Arc;
use crate::{
decl_engine::*,
fuel_prelude::fuel_tx::StorageSlot,
language::{parsed, ty::*, Purity},
language::{parsed, ty::*, Purity, Visibility},
namespace::TraitMap,
semantic_analysis::namespace,
transform::AllowDeprecatedState,
type_system::*,
Expand Down Expand Up @@ -65,6 +66,7 @@ impl TyProgram {
handler: &Handler,
engines: &Engines,
root: &TyModule,
root_namespace: &mut namespace::Namespace,
kind: parsed::TreeType,
package_name: &str,
experimental: ExperimentalFeatures,
Expand All @@ -81,6 +83,7 @@ impl TyProgram {
handler,
engines,
&submodule.module,
root_namespace,
parsed::TreeType::Library,
package_name,
experimental,
Expand Down Expand Up @@ -423,6 +426,40 @@ impl TyProgram {
}
}

// check orphan rules for all traits
TraitMap::check_orphan_rules(handler, engines, root_namespace.root_ref())?;

// check trait overlap
let mut unified_trait_map = root_namespace
.root_ref()
.current_package_root_module()
.root_lexical_scope()
.items
.implemented_traits
.clone();
unified_trait_map.check_overlap_and_extend(handler, unified_trait_map.clone(), engines)?;

for (submod_name, submodule) in root.submodules.iter() {
root_namespace.push_submodule(
handler,
engines,
submod_name.clone(),
Visibility::Public,
submodule.mod_name_span.clone(),
)?;

unified_trait_map.check_overlap_and_extend(
handler,
root_namespace
.current_module()
.root_lexical_scope()
.items
.implemented_traits
.clone(),
engines,
)?;
}

Ok((typed_program_kind, declarations, configurables))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ impl ty::TyAbiDecl {
CallPath::ident_to_fullpath(self.name.clone(), ctx.namespace()),
vec![],
type_id,
vec![],
&all_items,
&self.span,
Some(self.span()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ where
impl_trait.trait_name.clone(),
impl_trait.trait_type_arguments.clone(),
impl_trait.implementing_for.type_id,
impl_trait.impl_type_parameters.clone(),
&impl_trait.items,
&impl_trait.span,
impl_trait
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ impl TyDecl {
impl_trait.trait_name.clone(),
impl_trait.trait_type_arguments.clone(),
impl_trait.implementing_for.type_id,
impl_trait.impl_type_parameters.clone(),
&impl_trait.items,
&impl_trait.span,
impl_trait
Expand Down Expand Up @@ -310,6 +311,7 @@ impl TyDecl {
impl_trait.trait_name.clone(),
impl_trait.trait_type_arguments.clone(),
impl_trait.implementing_for.type_id,
impl_trait.impl_type_parameters.clone(),
impl_trait_items,
&impl_trait.span,
impl_trait
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,7 @@ impl TyImplSelfOrTrait {
impl_trait.trait_name.clone(),
impl_trait.trait_type_arguments.clone(),
impl_trait.implementing_for.type_id,
impl_trait.impl_type_parameters.clone(),
&impl_trait.items,
&impl_trait.span,
impl_trait
Expand Down Expand Up @@ -808,6 +809,7 @@ fn type_check_trait_implementation(
trait_name.clone(),
trait_type_arguments.to_vec(),
implementing_for,
impl_type_parameters.to_vec(),
&this_supertrait_impld_method_refs
.values()
.cloned()
Expand Down
3 changes: 3 additions & 0 deletions sway-core/src/semantic_analysis/ast_node/declaration/trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ impl TyTraitDecl {
CallPath::ident_to_fullpath(name.clone(), ctx.namespace),
new_type_parameters.iter().map(|x| x.into()).collect(),
self_type,
vec![],
&dummy_interface_surface,
&span,
None,
Expand Down Expand Up @@ -235,6 +236,7 @@ impl TyTraitDecl {
CallPath::ident_to_fullpath(name.clone(), ctx.namespace()),
new_type_parameters.iter().map(|x| x.into()).collect(),
self_type,
vec![],
&dummy_interface_surface,
&span,
None,
Expand Down Expand Up @@ -614,6 +616,7 @@ impl TyTraitDecl {
trait_name.clone(),
type_arguments.to_vec(),
type_id,
vec![],
&all_items,
&trait_name.span(),
Some(self.span()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1983,6 +1983,7 @@ impl ty::TyExpression {
abi_name.clone(),
vec![],
return_type,
vec![],
&abi_items,
span,
Some(span.clone()),
Expand Down
10 changes: 3 additions & 7 deletions sway-core/src/semantic_analysis/namespace/contract_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use sway_error::{
handler::{ErrorEmitted, Handler},
};
use sway_parse::{lex, Parser};
use sway_types::{constants::CONTRACT_ID, Spanned};
use sway_types::{constants::CONTRACT_ID, ProgramId, Spanned};

use crate::{
language::{
Expand All @@ -18,19 +18,15 @@ use crate::{
Engines, Ident, Namespace,
};

/// Factory function for contracts
pub fn namespace_without_contract_id(package_name: Ident) -> Root {
Root::new(package_name, None, false)
}

/// Factory function for contracts
pub fn namespace_with_contract_id(
engines: &Engines,
package_name: Ident,
program_id: ProgramId,
contract_id_value: String,
experimental: crate::ExperimentalFeatures,
) -> Result<Root, vec1::Vec1<CompileError>> {
let root = Root::new(package_name, None, true);
let root = Root::new(package_name, None, program_id, true);
let handler = <_>::default();
bind_contract_id_in_root_module(&handler, engines, contract_id_value, root, experimental)
.map_err(|_| {
Expand Down
16 changes: 14 additions & 2 deletions sway-core/src/semantic_analysis/namespace/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use sway_error::{
error::CompileError,
handler::{ErrorEmitted, Handler},
};
use sway_types::{span::Span, Spanned};
use sway_types::{span::Span, ProgramId, Spanned};
use sway_utils::iter_prefixes;

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -163,6 +163,8 @@ impl ResolvedDeclaration {
pub struct Root {
// The contents of the package being compiled.
current_package: Module,
// Program id for the package.
program_id: ProgramId,
// True if the current package is a contract, false otherwise.
is_contract_package: bool,
// The external dependencies of the current package. Note that an external package is
Expand All @@ -179,11 +181,17 @@ impl Root {
// and `package_root_with_contract_id` are supplied in `contract_helpers`.
//
// External packages must be added afterwards by calling `add_external`
pub(crate) fn new(package_name: Ident, span: Option<Span>, is_contract_package: bool) -> Self {
pub fn new(
package_name: Ident,
span: Option<Span>,
program_id: ProgramId,
is_contract_package: bool,
) -> Self {
// The root module must be public
let module = Module::new(package_name, Visibility::Public, span, &vec![]);
Self {
current_package: module,
program_id,
is_contract_package,
external_packages: Default::default(),
}
Expand Down Expand Up @@ -225,6 +233,10 @@ impl Root {
self.current_package.name()
}

pub fn program_id(&self) -> ProgramId {
self.program_id
}

fn check_path_is_in_current_package(&self, mod_path: &ModulePathBuf) -> bool {
!mod_path.is_empty() && mod_path[0] == *self.current_package.name()
}
Expand Down
Loading

0 comments on commit e4ea1d3

Please sign in to comment.