Skip to content

Commit

Permalink
Allow trait methods to be called when trait is in scope
Browse files Browse the repository at this point in the history
  • Loading branch information
cburgdorf committed Jul 7, 2022
1 parent c238121 commit d3a0e74
Show file tree
Hide file tree
Showing 25 changed files with 474 additions and 146 deletions.
8 changes: 8 additions & 0 deletions crates/analyzer/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ pub trait AnalyzerDb: SourceDb + Upcast<dyn SourceDb> + UpcastMut<dyn SourceDb>
fn ingot_files(&self, ingot: IngotId) -> Rc<[SourceFileId]>;
#[salsa::input]
fn ingot_external_ingots(&self, ingot: IngotId) -> Rc<IndexMap<SmolStr, IngotId>>;
// Having the root ingot available as a "global" might offend functional programming purists
// but it makes for much nicer ergonomics in queries that just need the global entrypoint
#[salsa::input]
fn root_ingot(&self) -> IngotId;

#[salsa::invoke(queries::ingots::ingot_modules)]
fn ingot_modules(&self, ingot: IngotId) -> Rc<[ModuleId]>;
Expand Down Expand Up @@ -179,6 +183,10 @@ pub trait AnalyzerDb: SourceDb + Upcast<dyn SourceDb> + UpcastMut<dyn SourceDb>
#[salsa::invoke(queries::events::event_type)]
fn event_type(&self, event: EventId) -> Analysis<Rc<types::Event>>;

// Type
#[salsa::invoke(queries::types::all_impls)]
fn all_impls(&self, ty: TypeId) -> Rc<[ImplId]>;

// Type alias
#[salsa::invoke(queries::types::type_alias_type)]
#[salsa::cycle(queries::types::type_alias_type_cycle)]
Expand Down
20 changes: 18 additions & 2 deletions crates/analyzer/src/db/queries/types.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
use std::rc::Rc;

use crate::context::{AnalyzerContext, TempContext};
use crate::db::Analysis;
use crate::errors::TypeError;
use crate::namespace::items::TypeAliasId;
use crate::namespace::items::{ImplId, TypeAliasId};
use crate::namespace::scopes::ItemScope;
use crate::namespace::types;
use crate::namespace::types::{self, TypeId};
use crate::traversal::types::type_desc;
use crate::AnalyzerDb;

pub fn all_impls(db: &dyn AnalyzerDb, ty: TypeId) -> Rc<[ImplId]> {
db.root_ingot()
.all_existing_impls(db)
.iter()
.filter_map(|val| {
if val.receiver(db) == ty {
Some(*val)
} else {
None
}
})
.collect()
}

pub fn type_alias_type(
db: &dyn AnalyzerDb,
alias: TypeAliasId,
Expand Down
34 changes: 34 additions & 0 deletions crates/analyzer/src/namespace/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ impl Item {

pub fn item_kind_display_name(&self) -> &'static str {
match self {
Item::Type(TypeDef::Struct(_)) => "struct",
Item::Type(_) | Item::GenericType(_) => "type",
Item::Trait(_) => "trait",
Item::Impl(_) => "impl",
Expand Down Expand Up @@ -360,6 +361,7 @@ impl IngotId {
})
.collect();

db.set_root_ingot(ingot);
db.set_ingot_files(ingot, file_ids);
db.set_ingot_external_ingots(ingot, Rc::new(deps));
ingot
Expand Down Expand Up @@ -391,6 +393,22 @@ impl IngotId {
self.root_module(db).expect("missing root module").items(db)
}

/// Returns all `impl` from the current ingot as well as dependency ingots
pub fn all_existing_impls(&self, db: &dyn AnalyzerDb) -> Vec<ImplId> {
let ingot_modules = self
.all_modules(db)
.iter()
.flat_map(|module_id| module_id.all_impls(db).to_vec())
.collect::<Vec<_>>();

self.external_ingots(db)
.values()
.flat_map(|ingot| ingot.all_modules(db).to_vec())
.flat_map(|module_id| module_id.all_impls(db).to_vec())
.chain(ingot_modules)
.collect()
}

pub fn diagnostics(&self, db: &dyn AnalyzerDb) -> Vec<Diagnostic> {
let mut diagnostics = vec![];
self.sink_diagnostics(db, &mut diagnostics);
Expand Down Expand Up @@ -528,6 +546,22 @@ impl ModuleId {
db.module_used_item_map(*self).value
}

/// Returns `true` if the `item` is in scope of the module.
pub fn is_in_scope(&self, db: &dyn AnalyzerDb, item: Item) -> bool {
if let Some(val) = item.module(db) {
if val == *self {
return true;
}
}

if let Some((_, val)) = self.used_items(db).get(&item.name(db)) {
if *val == item {
return true;
}
}
false
}

/// Returns all of the internal items, except for `use`d items. This is used
/// when resolving `use` statements, as it does not create a query
/// cycle.
Expand Down
46 changes: 24 additions & 22 deletions crates/analyzer/src/namespace/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ use strum::{AsRefStr, EnumIter, EnumString};

use super::items::FunctionSigId;
use super::items::ImplId;
use super::items::ModuleId;

pub fn u256_min() -> BigInt {
BigInt::from(0)
Expand Down Expand Up @@ -109,6 +108,8 @@ impl TypeId {
match self.typ(db) {
Type::Contract(_) | Type::SelfContract(_) => "contract",
Type::Struct(_) => "struct",
Type::Array(_) => "array",
Type::Tuple(_) => "tuple",
_ => "type",
}
}
Expand All @@ -120,34 +121,17 @@ impl TypeId {
}
}

/// Returns all `impls` for the type
pub fn get_all_impls(&self, db: &dyn AnalyzerDb, module_id: ModuleId) -> Vec<ImplId> {
// TODO: Probably, this isn't looking for all `impl` at all relevant places.
// An `impl` could be in the module of the type but it could also be in the module
// of the trait (which we don't know yet since we just want to get all `impl` regardless of the trait).
// Most likely, what we actually need is a mapping from type to impls and then we can just pick all
// `impl` for some type via that mapping.
module_id
.impls(db)
.iter()
.filter_map(
|((_, ty), impl_)| {
if ty == self {
Some(*impl_)
} else {
None
}
},
)
.collect()
// Returns all `impl` for the type even from foreign ingots
pub fn get_all_impls(&self, db: &dyn AnalyzerDb) -> Rc<[ImplId]> {
db.all_impls(*self)
}

pub fn function_sig(&self, db: &dyn AnalyzerDb, name: &str) -> Option<FunctionSigId> {
match self.typ(db) {
Type::Contract(id) => id.function(db, name).map(|fun| fun.sig(db)),
Type::SelfContract(id) => id.function(db, name).map(|fun| fun.sig(db)),
Type::Struct(id) => id.function(db, name).map(|fun| fun.sig(db)),
// FIXME: multiple bounds
// TODO: This won't hold when we support multiple bounds
Type::Generic(inner) => inner
.bounds
.first()
Expand All @@ -156,6 +140,24 @@ impl TypeId {
}
}

/// Like `function_sig` but returns a `Vec<FunctionSigId>` which not only considers functions natively
/// implemented on the type but also those that are provided by implemented traits on the type.
pub fn function_sigs(&self, db: &dyn AnalyzerDb, name: &str) -> Vec<FunctionSigId> {
let native = self.function_sig(db, name);

let mut fns: Vec<FunctionSigId> = self
.get_all_impls(db)
.iter()
.filter_map(|impl_| impl_.function(db, name))
.map(|fun| fun.sig(db))
.collect();

if let Some(fun) = native {
fns.push(fun)
}
fns
}

pub fn self_function(&self, db: &dyn AnalyzerDb, name: &str) -> Option<FunctionSigId> {
let fun = self.function_sig(db, name)?;
fun.takes_self(db).then(|| fun)
Expand Down
Loading

0 comments on commit d3a0e74

Please sign in to comment.