#![cfg(feature = "inventory")]
mod common;
use crate::common::{EventLoggerDatabase, LogDatabase};
use expect_test::expect;
use salsa::{Database, Setter};
#[salsa::input(debug)]
struct ClassNode {
name: String,
type_params: Option<TypeParamNode>,
}
#[salsa::input(debug)]
struct TypeParamNode {
name: String,
constraint: Option<ClassNode>,
}
#[salsa::interned(debug)]
struct Class<'db> {
name: String,
type_params: Option<TypeParam<'db>>,
}
#[salsa::tracked(debug)]
struct TypeParam<'db> {
name: String,
constraint: Option<Type<'db>>,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, salsa::Update)]
enum Type<'db> {
Class(Class<'db>),
Unknown,
}
impl Type<'_> {
fn class(&self) -> Option<Class<'_>> {
match self {
Type::Class(class) => Some(*class),
Type::Unknown => None,
}
}
}
#[salsa::tracked(cycle_initial=infer_class_initial)]
fn infer_class<'db>(db: &'db dyn salsa::Database, node: ClassNode) -> Type<'db> {
Type::Class(Class::new(
db,
node.name(db),
node.type_params(db).map(|tp| infer_type_param(db, tp)),
))
}
#[salsa::tracked]
fn infer_type_param<'db>(db: &'db dyn salsa::Database, node: TypeParamNode) -> TypeParam<'db> {
if let Some(constraint) = node.constraint(db) {
match infer_class(db, constraint) {
Type::Class(class) => class
.type_params(db)
.unwrap_or_else(|| TypeParam::new(db, node.name(db), Some(Type::Unknown))),
Type::Unknown => TypeParam::new(db, node.name(db), Some(Type::Unknown)),
}
} else {
TypeParam::new(db, node.name(db), None)
}
}
fn infer_class_initial(_db: &'_ dyn Database, _id: salsa::Id, _node: ClassNode) -> Type<'_> {
Type::Unknown
}
#[test]
fn main() {
let mut db = EventLoggerDatabase::default();
let class_node = ClassNode::new(&db, "Test".to_string(), None);
let type_param_node = TypeParamNode::new(&db, "T".to_string(), Some(class_node));
class_node
.set_type_params(&mut db)
.to(Some(type_param_node));
let ty = infer_class(&db, class_node);
db.assert_logs(expect![[r#"
[
"DidSetCancellationFlag",
"WillCheckCancellation",
"WillExecute { database_key: infer_class(Id(0)) }",
"WillCheckCancellation",
"WillExecute { database_key: infer_type_param(Id(400)) }",
"WillCheckCancellation",
"DidInternValue { key: Class(Id(c00)), revision: R2 }",
"WillIterateCycle { database_key: infer_class(Id(0)), iteration_count: IterationCount(1) }",
"WillCheckCancellation",
"WillExecute { database_key: infer_type_param(Id(400)) }",
"WillCheckCancellation",
"DidFinalizeCycle { database_key: infer_class(Id(0)), iteration_count: IterationCount(1) }",
]"#]]);
let class = ty.class().unwrap();
let type_param = class.type_params(&db).unwrap();
assert_eq!(type_param.name(&db), "T");
}