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

Added traits from impl aliases in the module, to the context #4042

Merged
merged 1 commit into from
Sep 10, 2023
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
3 changes: 2 additions & 1 deletion crates/cairo-lang-semantic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ license-file.workspace = true
description = "Cairo semantic model."

[features]
testing = []
testing = ["dep:cairo-lang-test-utils"]

[dependencies]
cairo-lang-debug = { path = "../cairo-lang-debug", version = "2.2.0" }
Expand All @@ -19,6 +19,7 @@ cairo-lang-plugins = { path = "../cairo-lang-plugins", version = "2.2.0" }
cairo-lang-proc-macros = { path = "../cairo-lang-proc-macros", version = "2.2.0" }
cairo-lang-syntax = { path = "../cairo-lang-syntax", version = "2.2.0" }
cairo-lang-utils = { path = "../cairo-lang-utils", version = "2.2.0" }
cairo-lang-test-utils = { path = "../cairo-lang-test-utils", optional = true, features = ["testing"] }
id-arena.workspace = true
itertools.workspace = true
log.workspace = true
Expand Down
13 changes: 9 additions & 4 deletions crates/cairo-lang-semantic/src/expr/inference/solver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,15 @@ impl Solver {
SolutionSet::Ambiguous(ambiguity) => return Ok(SolutionSet::Ambiguous(ambiguity)),
};
if let Some(unique_solution) = unique_solution {
return Ok(SolutionSet::Ambiguous(Ambiguity::MultipleImplsFound {
concrete_trait_id: self.canonical_trait.0,
impls: vec![unique_solution.0, candidate_solution.0],
}));
// There might be multiple unique solutions from different candidates that are
// solved to the same impl id (e.g. finding it near the trait, and
// through an impl alias). This is valid.
if unique_solution.0 != candidate_solution.0 {
return Ok(SolutionSet::Ambiguous(Ambiguity::MultipleImplsFound {
concrete_trait_id: self.canonical_trait.0,
impls: vec![unique_solution.0, candidate_solution.0],
}));
}
}
unique_solution = Some(candidate_solution);
}
Expand Down
134 changes: 131 additions & 3 deletions crates/cairo-lang-semantic/src/expr/test_data/method
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! > Test method

//! > test_runner_name
test_function_diagnostics
test_function_diagnostics(allow_diagnostics: false)

//! > function
fn foo() -> Option::<felt252> {
Expand Down Expand Up @@ -30,7 +30,7 @@ impl OtherImpl of AnotherTrait {
//! > Test method failures

//! > test_runner_name
test_function_diagnostics
test_function_diagnostics(allow_diagnostics: true)

//! > function
fn foo() -> Option::<felt252> {
Expand Down Expand Up @@ -104,7 +104,7 @@ error: Candidate impl test::AnotherMyTraitImpl::<?0> has an unused generic param
//! > Test calling a method of a trait from the generic arguments.

//! > test_runner_name
test_function_diagnostics
test_function_diagnostics(allow_diagnostics: false)

//! > function
fn foo() {
Expand All @@ -128,3 +128,131 @@ mod callable {
}

//! > expected_diagnostics

//! > ==========================================================================

//! > Test calling a method of a trait from an impl in the module.

//! > test_runner_name
test_function_diagnostics(allow_diagnostics: false)

//! > function
fn foo() {
bar(3_u8);
}

//! > function_name
foo

//! > module_code
fn bar(x: u8) {
x.call();
}
mod callable {
trait CallableTrait<T>{
fn call(self: T);
}
}
impl U8Callable of callable::CallableTrait<u8> {
fn call(self: u8) {}
}

//! > expected_diagnostics

//! > ==========================================================================

//! > Test calling a method of a trait from an impl in the module that is not the relevant impl.

//! > test_runner_name
test_function_diagnostics(allow_diagnostics: false)

//! > function
fn foo() {
bar(3_u8);
}

//! > function_name
foo

//! > module_code
fn bar(x: u8) {
x.call();
}
mod callable {
trait CallableTrait<T>{
fn call(self: T);
}
impl U8Callable of CallableTrait<u8> {
fn call(self: u8) {}
}
}
impl U16Callable of callable::CallableTrait<u16> {
fn call(self: u16) {}
}

//! > expected_diagnostics

//! > ==========================================================================

//! > Test calling a method of a trait from an impl alias in the module.

//! > test_runner_name
test_function_diagnostics(allow_diagnostics: false)

//! > function
fn foo() {
bar(3_u8);
}

//! > function_name
foo

//! > module_code
impl X = callable::U8Callable;
fn bar(x: u8) {
x.call();
}
mod callable {
trait CallableTrait<T>{
fn call(self: T);
}
impl U8Callable of CallableTrait<u8> {
fn call(self: u8) {}
}
}

//! > expected_diagnostics

//! > ==========================================================================

//! > Test calling a a trait method from an impl alias in the module that is not the relevant impl.

//! > test_runner_name
test_function_diagnostics(allow_diagnostics: false)

//! > function
fn foo() {
bar(3_u8);
}

//! > function_name
foo

//! > module_code
impl X = callable::U16Callable;
fn bar(x: u8) {
x.call();
}
mod callable {
trait CallableTrait<T>{
fn call(self: T);
}
impl U8Callable of CallableTrait<u8> {
fn call(self: u8) {}
}
impl U16Callable of CallableTrait<u16> {
fn call(self: u16) {}
}
}

//! > expected_diagnostics
6 changes: 6 additions & 0 deletions crates/cairo-lang-semantic/src/items/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ pub fn module_usable_trait_ids(
let trait_id = db.impl_def_trait(imp)?;
module_traits.insert(trait_id);
}
// Add traits from impl aliases in the module.
for alias in db.module_impl_aliases_ids(module_id)?.iter().copied() {
let impl_id = db.impl_alias_impl_def(alias)?;
let trait_id = db.impl_def_trait(impl_id)?;
module_traits.insert(trait_id);
}
// Add traits from uses in the module.
for use_id in db.module_uses_ids(module_id)?.iter().copied() {
match db.use_resolved_item(use_id)? {
Expand Down
24 changes: 13 additions & 11 deletions crates/cairo-lang-semantic/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use cairo_lang_parser::db::ParserDatabase;
use cairo_lang_plugins::get_default_plugins;
use cairo_lang_syntax::node::ast;
use cairo_lang_syntax::node::db::{SyntaxDatabase, SyntaxGroup};
use cairo_lang_test_utils::has_disallowed_diagnostics;
use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
use cairo_lang_utils::{extract_matches, OptionFrom, Upcast};
use once_cell::sync::Lazy;
Expand Down Expand Up @@ -274,19 +275,20 @@ pub fn test_expr_diagnostics(

pub fn test_function_diagnostics(
inputs: &OrderedHashMap<String, String>,
_args: &OrderedHashMap<String, String>,
args: &OrderedHashMap<String, String>,
) -> Result<OrderedHashMap<String, String>, String> {
let db = &SemanticDatabaseForTesting::default();
Ok(OrderedHashMap::from([(
"expected_diagnostics".into(),
setup_test_function(
db,
inputs["function"].as_str(),
inputs["function_name"].as_str(),
inputs["module_code"].as_str(),
)
.get_diagnostics(),
)]))

let diagnostics = setup_test_function(
db,
inputs["function"].as_str(),
inputs["function_name"].as_str(),
inputs["module_code"].as_str(),
)
.get_diagnostics();
has_disallowed_diagnostics(args, &diagnostics)?;

Ok(OrderedHashMap::from([("expected_diagnostics".into(), diagnostics)]))
}

/// Gets the diagnostics for all the modules (including nested) in the given crate.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,13 @@ mod my_contract {

#[embed(v0)]
impl OwnershipTransfer = super::ownable::Transfer<ContractState>;
use super::ownable::TransferTrait;

#[embed(v0)]
#[generate_trait]
impl Impl of Trait {
#[constructor]
fn constructor(ref self: ContractState, owner: ContractAddress, initial: u128) {
self.ownable.init_ownable(owner);
self.init_ownable(owner);
self.balance.write(initial);
}
#[external(v0)]
Expand All @@ -72,7 +71,7 @@ mod my_contract {
}
#[external(v0)]
fn set_balance(ref self: ContractState, new_balance: u128) {
self.ownable.validate_ownership();
self.validate_ownership();
self.balance.write(new_balance);
}
}
Expand Down Expand Up @@ -133,14 +132,13 @@ mod my_contract {

#[embed(v0)]
impl OwnershipTransfer = super::ownable::Transfer<ContractState>;
use super::ownable::TransferTrait;

#[embed(v0)]
#[generate_trait]
impl Impl of Trait {
#[constructor]
fn constructor(ref self: ContractState, owner: ContractAddress, initial: u128) {
self.ownable.init_ownable(owner);
self.init_ownable(owner);
self.balance.write(initial);
}
#[external(v0)]
Expand All @@ -149,7 +147,7 @@ mod my_contract {
}
#[external(v0)]
fn set_balance(ref self: ContractState, new_balance: u128) {
self.ownable.validate_ownership();
self.validate_ownership();
self.balance.write(new_balance);
}
}
Expand Down Expand Up @@ -385,7 +383,6 @@ contract:
mod balance {
use super::ContractAddress;
use super::Event;
use super::TransferTrait;
use super::Impl;
#[derive(Copy, Drop)]
struct ContractMemberState {}
Expand Down Expand Up @@ -423,7 +420,7 @@ contract:
}
}
#[cfg(test)]
const TEST_CLASS_HASH: felt252 = 0xb660f9d7232a652f156bdad5ae5094889d83ec521206a1aa95635bfa2c4bb6;
const TEST_CLASS_HASH: felt252 = 0xe0444b0e3e2ebc94534b4e9603fe33e5720f7f9ded7694df89ed3113eab701;

impl ContractStateTransfer of
super::ownable::UnsafeNewContractStateTraitForTransfer<ContractState> {
Expand Down
2 changes: 2 additions & 0 deletions crates/cairo-lang-test-utils/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg(any(feature = "testing", test))]

pub mod parse_test_file;
use std::fs;
use std::path::Path;
Expand Down