feat: introduce type context trait TyCtx (#670)

* feat: introduce `TyCtx`

* core: simplify import

* feat: implement local bind apis

* build: update cargo.lock

* dev: rename `LocalTyCtx` back to `TyCtxMut`
This commit is contained in:
Myriad-Dreamin 2024-10-14 12:31:07 +08:00 committed by GitHub
parent 5898c60de1
commit e9ec60d2b5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 417 additions and 240 deletions

9
Cargo.lock generated
View file

@ -4013,6 +4013,14 @@ version = "0.11.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea141357280a85cdacb962dc64b07ae6fa4381df468f6aba1d3dd93483afdc38"
[[package]]
name = "tinymist-derive"
version = "0.11.32"
dependencies = [
"quote",
"syn 2.0.79",
]
[[package]]
name = "tinymist-query"
version = "0.11.32"
@ -4051,6 +4059,7 @@ dependencies = [
"siphasher 1.0.1",
"strum 0.26.3",
"tinymist-analysis",
"tinymist-derive",
"tinymist-world",
"toml 0.8.19",
"triomphe",

View file

@ -23,6 +23,8 @@ once_cell = "1"
paste = "1.0"
cfg-if = "1.0"
strum = { version = "0.26.2", features = ["derive"] }
quote = "1"
syn = "2"
triomphe = { version = "0.1.10", default-features = false, features = ["std"] }
# Asynchoronous and Multi-threading
@ -139,6 +141,7 @@ insta = { version = "1.39", features = ["glob"] }
typst-preview = { path = "./crates/typst-preview/" }
tinymist-assets = { version = "0.11.32" }
tinymist = { path = "./crates/tinymist/" }
tinymist-derive = { path = "./crates/tinymist-derive/" }
tinymist-analysis = { path = "./crates/tinymist-analysis/" }
tinymist-query = { path = "./crates/tinymist-query/" }
tinymist-world = { path = "./crates/tinymist-world/" }

View file

@ -0,0 +1,21 @@
[package]
name = "tinymist-derive"
description = "Provides derive for tinymist."
categories = ["compilers"]
keywords = ["typst"]
authors.workspace = true
version.workspace = true
license.workspace = true
edition.workspace = true
homepage.workspace = true
repository.workspace = true
[dependencies]
syn.workspace = true
quote.workspace = true
[lib]
proc-macro = true
[features]
typst-preview = []

View file

@ -0,0 +1,48 @@
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
#[proc_macro_derive(BindTyCtx, attributes(bind))]
pub fn bind_ty_ctx(input: TokenStream) -> TokenStream {
// Parse the input tokens into a syntax tree
let input = parse_macro_input!(input as DeriveInput);
// Build the output, possibly using quasi-quotation
let expanded = match input.data {
syn::Data::Struct(..) => {
let name = &input.ident;
let bind_name = input
.attrs
.iter()
.find_map(|attr| {
if attr.path().is_ident("bind") {
Some(attr.parse_args::<syn::Expr>().unwrap())
} else {
None
}
})
.unwrap_or_else(|| {
let t = syn::Ident::new("tyctx", input.ident.span());
syn::parse_quote!(#t)
});
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
quote! {
impl #impl_generics TyCtx for #name #ty_generics #where_clause {
fn global_bounds(&self, var: &Interned<TypeVar>, pol: bool) -> Option<TypeBounds> {
self.#bind_name.global_bounds(var, pol)
}
fn local_bind_of(&self, var: &Interned<TypeVar>) -> Option<Ty> {
self.#bind_name.local_bind_of(var)
}
}
}
}
_ => panic!("only structs are supported"),
};
// Hand the output tokens back to the compiler
TokenStream::from(expanded)
}

View file

@ -55,6 +55,7 @@ base64.workspace = true
typlite.workspace = true
tinymist-world.workspace = true
tinymist-analysis.workspace = true
tinymist-derive.workspace = true
[dev-dependencies]
once_cell.workspace = true

View file

@ -5,14 +5,17 @@ use std::{collections::HashMap, sync::Arc};
use once_cell::sync::Lazy;
use reflexo::vector::ir::DefId;
use typst::{
foundations::Value,
foundations::{Func, Value},
syntax::{
ast::{self, AstNode},
LinkedNode, Source, Span, SyntaxKind,
},
};
use crate::analysis::{Ty, *};
use crate::{
adt::interner::Interned,
analysis::{Ty, *},
};
use crate::{analysis::TypeScheme, ty::TypeInterface, AnalysisContext};
use super::{
@ -61,6 +64,8 @@ enum InterpretMode {
Math,
}
#[derive(BindTyCtx)]
#[bind(info)]
struct TypeChecker<'a, 'w> {
ctx: &'a mut AnalysisContext<'w>,
source: Source,
@ -71,6 +76,26 @@ struct TypeChecker<'a, 'w> {
mode: InterpretMode,
}
impl<'a, 'w> TyCtxMut for TypeChecker<'a, 'w> {
type Snap = <TypeScheme as TyCtxMut>::Snap;
fn start_scope(&mut self) -> Self::Snap {
self.info.start_scope()
}
fn end_scope(&mut self, snap: Self::Snap) {
self.info.end_scope(snap)
}
fn bind_local(&mut self, var: &Interned<TypeVar>, ty: Ty) {
self.info.bind_local(var, ty);
}
fn type_of_func(&mut self, func: &Func) -> Option<Interned<SigTy>> {
self.ctx.type_of_func(func)
}
}
impl<'a, 'w> TypeChecker<'a, 'w> {
fn check(&mut self, root: LinkedNode) -> Ty {
let should_record = matches!(root.kind(), SyntaxKind::FuncCall).then(|| root.span());

View file

@ -9,6 +9,8 @@ use crate::{analysis::ApplyChecker, ty::ArgsTy};
use super::*;
use crate::adt::interner::Interned;
#[derive(BindTyCtx)]
#[bind(base)]
pub struct ApplyTypeChecker<'a, 'b, 'w> {
pub(super) base: &'a mut TypeChecker<'b, 'w>,
pub call_site: Span,
@ -17,18 +19,6 @@ pub struct ApplyTypeChecker<'a, 'b, 'w> {
}
impl<'a, 'b, 'w> ApplyChecker for ApplyTypeChecker<'a, 'b, 'w> {
fn bound_of_var(
&mut self,
var: &Interned<super::TypeVar>,
_pol: bool,
) -> Option<super::TypeBounds> {
self.base
.info
.vars
.get(&var.def)
.map(|v| v.bounds.bounds().read().clone())
}
fn apply(&mut self, sig: Sig, args: &Interned<ArgsTy>, pol: bool) {
let _ = self.args;
@ -38,7 +28,7 @@ impl<'a, 'b, 'w> ApplyChecker for ApplyTypeChecker<'a, 'b, 'w> {
};
if !is_partialize {
if let Some(ty) = sig.call(args, pol, Some(self.base.ctx)) {
if let Some(ty) = sig.call(args, pol, self.base) {
self.resultant.push(ty);
}
}
@ -155,7 +145,7 @@ impl<'a, 'b, 'w> ApplyChecker for ApplyTypeChecker<'a, 'b, 'w> {
let callee = sig.ty();
let Some(SigShape { sig, withs }) = sig.shape(Some(self.base.ctx)) else {
let Some(SigShape { sig, withs }) = sig.shape(self.base) else {
return;
};
for (arg_recv, arg_ins) in sig.matches(args, withs) {
@ -210,24 +200,14 @@ impl<T: FnMut(&mut TypeChecker, Sig, bool)> TupleCheckDriver for T {
}
}
#[derive(BindTyCtx)]
#[bind(base)]
pub struct TupleChecker<'a, 'b, 'w> {
pub(super) base: &'a mut TypeChecker<'b, 'w>,
driver: &'a mut dyn TupleCheckDriver,
}
impl<'a, 'b, 'w> ApplyChecker for TupleChecker<'a, 'b, 'w> {
fn bound_of_var(
&mut self,
var: &Interned<super::TypeVar>,
_pol: bool,
) -> Option<super::TypeBounds> {
self.base
.info
.vars
.get(&var.def)
.map(|v| v.bounds.bounds().read().clone())
}
fn apply(&mut self, sig: Sig, _args: &Interned<ArgsTy>, pol: bool) {
self.driver.check(self.base, sig, pol);
}

View file

@ -3,9 +3,13 @@
use std::collections::HashMap;
use hashbrown::HashSet;
use typst::syntax::{
ast::{self, AstNode},
LinkedNode, Span, SyntaxKind,
use tinymist_derive::BindTyCtx;
use typst::{
foundations::Func,
syntax::{
ast::{self, AstNode},
LinkedNode, Span, SyntaxKind,
},
};
use crate::{
@ -15,7 +19,7 @@ use crate::{
AnalysisContext,
};
use super::{FieldTy, SigShape, Ty, TypeScheme};
use super::{FieldTy, SigShape, SigTy, Ty, TyCtx, TyCtxMut, TypeScheme, TypeVar};
/// With given type information, check the type of a literal expression again by
/// touching the possible related nodes.
@ -27,6 +31,7 @@ pub(crate) fn post_type_check(
let mut worker = PostTypeCheckWorker {
ctx: _ctx,
checked: HashMap::new(),
locals: TypeScheme::default(),
info,
};
@ -62,7 +67,7 @@ fn check_signature<'a>(
target: &'a ParamTarget,
) -> impl FnMut(&mut PostTypeCheckWorker, Sig, &[Interned<ArgsTy>], bool) -> Option<()> + 'a {
move |worker, sig, args, pol| {
let SigShape { sig: sig_ins, .. } = sig.shape(Some(worker.ctx))?;
let SigShape { sig: sig_ins, .. } = sig.shape(worker)?;
match &target {
ParamTarget::Named(n) => {
@ -105,10 +110,43 @@ fn check_signature<'a>(
}
}
// #[derive(BindTyCtx)]
// #[bind(info)]
struct PostTypeCheckWorker<'a, 'w> {
ctx: &'a mut AnalysisContext<'w>,
checked: HashMap<Span, Option<Ty>>,
info: &'a TypeScheme,
locals: TypeScheme,
}
impl<'a, 'w> TyCtx for PostTypeCheckWorker<'a, 'w> {
fn global_bounds(&self, var: &Interned<TypeVar>, pol: bool) -> Option<TypeBounds> {
self.info.global_bounds(var, pol)
}
fn local_bind_of(&self, var: &Interned<TypeVar>) -> Option<Ty> {
self.locals.local_bind_of(var)
}
}
impl<'a, 'w> TyCtxMut for PostTypeCheckWorker<'a, 'w> {
type Snap = <TypeScheme as TyCtxMut>::Snap;
fn start_scope(&mut self) -> Self::Snap {
self.locals.start_scope()
}
fn end_scope(&mut self, snap: Self::Snap) {
self.locals.end_scope(snap)
}
fn bind_local(&mut self, var: &Interned<TypeVar>, ty: Ty) {
self.locals.bind_local(var, ty);
}
fn type_of_func(&mut self, func: &Func) -> Option<Interned<SigTy>> {
self.ctx.type_of_func(func)
}
}
impl<'a, 'w> PostTypeCheckWorker<'a, 'w> {
@ -289,20 +327,17 @@ impl<'a, 'w> PostTypeCheckWorker<'a, 'w> {
}
}
fn check_signatures(
&mut self,
ty: &Ty,
pol: bool,
checker: &mut impl FnMut(&mut Self, Sig, &[Interned<ArgsTy>], bool) -> Option<()>,
) {
ty.sig_surface(pol, SigSurfaceKind::Call, &mut (self, checker));
fn check_signatures(&mut self, ty: &Ty, pol: bool, checker: &mut impl PostSigChecker) {
let mut checker = PostSigCheckWorker(self, checker);
ty.sig_surface(pol, SigSurfaceKind::Call, &mut checker);
}
fn check_element_of<T>(&mut self, ty: &Ty, pol: bool, context: &LinkedNode, checker: &mut T)
where
T: FnMut(&mut Self, Sig, &[Interned<ArgsTy>], bool) -> Option<()>,
T: PostSigChecker,
{
ty.sig_surface(pol, sig_context_of(context), &mut (self, checker))
let mut checker = PostSigCheckWorker(self, checker);
ty.sig_surface(pol, sig_context_of(context), &mut checker)
}
fn simplify(&mut self, ty: &Ty) -> Option<Ty> {
@ -310,29 +345,43 @@ impl<'a, 'w> PostTypeCheckWorker<'a, 'w> {
}
}
impl<'a, 'w, T> SigChecker for (&mut PostTypeCheckWorker<'a, 'w>, &mut T)
trait PostSigChecker {
fn check(
&mut self,
checker: &mut PostTypeCheckWorker,
sig: Sig,
args: &[Interned<ArgsTy>],
pol: bool,
) -> Option<()>;
}
impl<T> PostSigChecker for T
where
T: FnMut(&mut PostTypeCheckWorker<'a, 'w>, Sig, &[Interned<ArgsTy>], bool) -> Option<()>,
T: FnMut(&mut PostTypeCheckWorker, Sig, &[Interned<ArgsTy>], bool) -> Option<()>,
{
fn check(
&mut self,
checker: &mut PostTypeCheckWorker,
sig: Sig,
args: &[Interned<ArgsTy>],
pol: bool,
) -> Option<()> {
self(checker, sig, args, pol)
}
}
#[derive(BindTyCtx)]
#[bind(0)]
struct PostSigCheckWorker<'x, 'a, 'w, T>(&'x mut PostTypeCheckWorker<'a, 'w>, &'x mut T);
impl<'x, 'a, 'w, T: PostSigChecker> SigChecker for PostSigCheckWorker<'x, 'a, 'w, T> {
fn check(
&mut self,
sig: Sig,
args: &mut crate::analysis::SigCheckContext,
pol: bool,
) -> Option<()> {
self.1(self.0, sig, &args.args, pol)
}
fn check_var(
&mut self,
var: &Interned<crate::analysis::TypeVar>,
_pol: bool,
) -> Option<TypeBounds> {
self.0
.info
.vars
.get(&var.def)
.map(|v| v.bounds.bounds().read().clone())
self.1.check(self.0, sig, &args.args, pol)
}
}

View file

@ -8,6 +8,8 @@ use crate::analysis::Ty;
use super::*;
use crate::adt::interner::Interned;
#[derive(BindTyCtx)]
#[bind(base)]
pub struct SelectFieldChecker<'a, 'b, 'w> {
pub(super) base: &'a mut TypeChecker<'b, 'w>,
pub select_site: Span,
@ -16,18 +18,6 @@ pub struct SelectFieldChecker<'a, 'b, 'w> {
}
impl<'a, 'b, 'w> SelectChecker for SelectFieldChecker<'a, 'b, 'w> {
fn bound_of_var(
&mut self,
var: &Interned<super::TypeVar>,
_pol: bool,
) -> Option<super::TypeBounds> {
self.base
.info
.vars
.get(&var.def)
.map(|v| v.bounds.bounds().read().clone())
}
fn select(&mut self, iface: Iface, key: &Interned<str>, pol: bool) {
log::debug!("selecting field: {iface:?} {key:?}");
let _ = pol;

View file

@ -1,18 +1,13 @@
use once_cell::sync::Lazy;
use std::sync::LazyLock;
use crate::{adt::interner::Interned, ty::def::*};
use super::{Sig, SigChecker, SigSurfaceKind, TyCtx};
use crate::ty::def::*;
use super::{Sig, SigChecker, SigSurfaceKind};
pub trait ApplyChecker {
pub trait ApplyChecker: TyCtx {
fn apply(&mut self, sig: Sig, arguments: &Interned<ArgsTy>, pol: bool);
fn bound_of_var(&mut self, _var: &Interned<TypeVar>, _pol: bool) -> Option<TypeBounds> {
None
}
}
static EMPTY_ARGS: Lazy<Interned<ArgsTy>> = Lazy::new(|| ArgsTy::default().into());
static EMPTY_ARGS: LazyLock<Interned<ArgsTy>> = LazyLock::new(|| ArgsTy::default().into());
impl Ty {
/// Call the given type with the given arguments.
@ -42,7 +37,9 @@ impl Ty {
}
}
pub struct ApplySigChecker<'a, T>(&'a mut T, &'a Interned<ArgsTy>);
#[derive(BindTyCtx)]
#[bind(0)]
pub struct ApplySigChecker<'a, T: ApplyChecker>(&'a mut T, &'a Interned<ArgsTy>);
impl<'a, T: ApplyChecker> ApplySigChecker<'a, T> {
fn ty(&mut self, ty: &Ty, surface: SigSurfaceKind, pol: bool) {
@ -65,8 +62,4 @@ impl<'a, T: ApplyChecker> SigChecker for ApplySigChecker<'a, T> {
self.0.apply(partial_sig, self.1, pol);
Some(())
}
fn check_var(&mut self, _var: &Interned<TypeVar>, _pol: bool) -> Option<TypeBounds> {
self.0.bound_of_var(_var, _pol)
}
}

View file

@ -1,12 +1,20 @@
use crate::{adt::interner::Interned, ty::def::*};
use crate::ty::def::*;
pub trait BoundChecker {
pub trait BoundChecker: TyCtx {
fn collect(&mut self, ty: &Ty, pol: bool);
fn bound_of_var(&mut self, _var: &Interned<TypeVar>, _pol: bool) -> Option<TypeBounds> {
}
impl<T> TyCtx for T
where
T: FnMut(&Ty, bool) -> Option<TypeBounds>,
{
fn local_bind_of(&self, _var: &Interned<TypeVar>) -> Option<Ty> {
None
}
fn global_bounds(&self, _var: &Interned<TypeVar>, _pol: bool) -> Option<TypeBounds> {
None
}
}
impl<T> BoundChecker for T
where
T: FnMut(&Ty, bool) -> Option<TypeBounds>,
@ -53,7 +61,7 @@ impl BoundCheckContext {
self.tys(u.lbs.iter(), !pol, checker);
}
Ty::Var(u) => {
let Some(w) = checker.bound_of_var(u, pol) else {
let Some(w) = checker.global_bounds(u, pol) else {
return;
};
self.tys(w.ubs.iter(), pol, checker);

View file

@ -9,7 +9,7 @@ use typst::{
layout::Length,
};
use crate::{adt::interner::Interned, ty::*};
use crate::ty::*;
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, EnumIter)]
pub enum PathPreference {

View file

@ -19,14 +19,19 @@ use typst::{
};
use crate::{
adt::interner::{impl_internable, Interned},
adt::{interner::impl_internable, snapshot_map},
analysis::BuiltinTy,
};
pub use tinymist_derive::BindTyCtx;
pub(crate) use super::{TyCtxMut, TyCtx};
pub(crate) use crate::adt::interner::Interned;
/// A reference to the interned type
pub(super) type TyRef = Interned<Ty>;
pub(crate) type TyRef = Interned<Ty>;
/// A reference to the interned string
pub(super) type StrRef = Interned<str>;
pub(crate) type StrRef = Interned<str>;
/// All possible types in tinymist
#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
@ -949,12 +954,25 @@ impl IfTy {
pub struct TypeScheme {
/// The typing on definitions
pub vars: HashMap<DefId, TypeVarBounds>,
/// The local binding of the type variable
pub local_binds: snapshot_map::SnapshotMap<DefId, Ty>,
/// The typing on syntax structures
pub mapping: HashMap<Span, Vec<Ty>>,
pub(super) cano_cache: Mutex<TypeCanoStore>,
}
impl TyCtx for TypeScheme {
fn global_bounds(&self, var: &Interned<TypeVar>, _pol: bool) -> Option<TypeBounds> {
let v = self.vars.get(&var.def)?;
Some(v.bounds.bounds().read().clone())
}
fn local_bind_of(&self, var: &Interned<TypeVar>) -> Option<Ty> {
self.local_binds.get(&var.def).cloned()
}
}
impl TypeScheme {
/// Get the type of a definition
pub fn type_of_def(&self, def: DefId) -> Option<Ty> {
@ -997,6 +1015,26 @@ impl TypeScheme {
}
}
impl TyCtxMut for TypeScheme {
type Snap = ena::undo_log::Snapshot;
fn start_scope(&mut self) -> Self::Snap {
self.local_binds.snapshot()
}
fn end_scope(&mut self, snap: Self::Snap) {
self.local_binds.rollback_to(snap);
}
fn bind_local(&mut self, var: &Interned<TypeVar>, ty: Ty) {
self.local_binds.insert(var.def, ty);
}
fn type_of_func(&mut self, _func: &typst::foundations::Func) -> Option<Interned<SigTy>> {
None
}
}
/// A type variable bounds
#[derive(Clone)]
pub struct TypeVarBounds {
@ -1049,7 +1087,7 @@ impl TypeVarBounds {
/// A type variable bounds
#[derive(Clone)]
pub enum FlowVarKind {
/// A type variable that receives both types and values (type instnaces)
/// A type variable that receives both types and values (type instances)
Strong(Arc<RwLock<TypeBounds>>),
/// A type variable that receives only types
/// The received values will be lifted to types

View file

@ -3,7 +3,7 @@ use std::collections::HashSet;
use reflexo::hash::hash128;
use typst::foundations::Repr;
use crate::{adt::interner::Interned, analysis::*, ty::def::*};
use crate::{analysis::*, ty::def::*};
impl TypeScheme {
/// Describe the given type with the given type scheme.

View file

@ -1,6 +1,6 @@
use typst::foundations::{Dict, Value};
use crate::{adt::interner::Interned, analysis::*, ty::def::*};
use crate::{analysis::*, ty::def::*};
#[derive(Debug, Clone, Copy)]
pub enum Iface<'a> {
@ -56,20 +56,8 @@ impl<'a> Iface<'a> {
}
}
pub trait IfaceChecker {
pub trait IfaceChecker: TyCtx {
fn check(&mut self, sig: Iface, args: &mut IfaceCheckContext, pol: bool) -> Option<()>;
fn check_var(&mut self, _var: &Interned<TypeVar>, _pol: bool) -> Option<TypeBounds> {
None
}
}
impl<T> IfaceChecker for T
where
T: FnMut(Iface, &mut IfaceCheckContext, bool) -> Option<()>,
{
fn check(&mut self, sig: Iface, args: &mut IfaceCheckContext, pol: bool) -> Option<()> {
self(sig, args, pol)
}
}
impl Ty {
@ -98,6 +86,8 @@ pub struct IfaceCheckContext {
pub at: TyRef,
}
#[derive(BindTyCtx)]
#[bind(checker)]
pub struct IfaceCheckDriver<'a> {
ctx: IfaceCheckContext,
checker: &'a mut dyn IfaceChecker,
@ -107,10 +97,6 @@ impl BoundChecker for IfaceCheckDriver<'_> {
fn collect(&mut self, ty: &Ty, pol: bool) {
self.ty(ty, pol);
}
fn bound_of_var(&mut self, var: &Interned<TypeVar>, pol: bool) -> Option<TypeBounds> {
self.checker.check_var(var, pol)
}
}
impl<'a> IfaceCheckDriver<'a> {

View file

@ -20,6 +20,61 @@ pub(crate) use iface::*;
pub(crate) use mutate::*;
pub(crate) use select::*;
pub(crate) use sig::*;
use typst::foundations::Func;
/// A type context.
pub trait TyCtx {
/// Get local binding of a variable.
fn local_bind_of(&self, _var: &Interned<TypeVar>) -> Option<Ty>;
/// Get the type of a variable.
fn global_bounds(&self, _var: &Interned<TypeVar>, _pol: bool) -> Option<TypeBounds>;
}
/// A mutable type context.
pub trait TyCtxMut: TyCtx {
/// The type of a snapshot of the scope.
type Snap;
/// Start a new scope.
#[must_use]
fn start_scope(&mut self) -> Self::Snap;
/// End the current scope.
fn end_scope(&mut self, snap: Self::Snap);
/// Execute a function with a new scope.
fn with_scope<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
let snap = self.start_scope();
let res = f(self);
self.end_scope(snap);
res
}
/// Bind a variable locally.
fn bind_local(&mut self, var: &Interned<TypeVar>, ty: Ty);
/// Get the type of a runtime function.
fn type_of_func(&mut self, func: &Func) -> Option<Interned<SigTy>>;
}
impl TyCtx for () {
fn local_bind_of(&self, _var: &Interned<TypeVar>) -> Option<Ty> {
None
}
fn global_bounds(&self, _var: &Interned<TypeVar>, _pol: bool) -> Option<TypeBounds> {
None
}
}
impl TyCtxMut for () {
type Snap = ();
fn start_scope(&mut self) -> Self::Snap {
Self::Snap::default()
}
fn end_scope(&mut self, _snap: Self::Snap) {}
fn bind_local(&mut self, _var: &Interned<TypeVar>, _ty: Ty) {}
fn type_of_func(&mut self, _func: &Func) -> Option<Interned<SigTy>> {
None
}
}
#[cfg(test)]
mod tests {

View file

@ -1,7 +1,28 @@
use crate::{adt::interner::Interned, ty::def::*};
use crate::ty::def::*;
pub trait MutateDriver {
fn mutate(&mut self, ty: &Ty, pol: bool) -> Option<Ty>;
pub trait TyMutator {
fn mutate(&mut self, ty: &Ty, pol: bool) -> Option<Ty> {
self.mutate_rec(ty, pol)
}
fn mutate_rec(&mut self, ty: &Ty, pol: bool) -> Option<Ty> {
use Ty::*;
match ty {
Value(..) | Any | Boolean(..) | Builtin(..) => None,
Union(v) => Some(Union(self.mutate_vec(v, pol)?)),
Var(..) | Let(..) => None,
Array(e) => Some(Array(self.mutate(e, pol)?.into())),
Dict(r) => Some(Dict(self.mutate_record(r, pol)?.into())),
Tuple(e) => Some(Tuple(self.mutate_vec(e, pol)?)),
Func(f) => Some(Func(self.mutate_func(f, pol)?.into())),
Args(args) => Some(Args(self.mutate_func(args, pol)?.into())),
Field(f) => Some(Field(self.mutate_field(f, pol)?.into())),
Select(s) => Some(Select(self.mutate_select(s, pol)?.into())),
With(w) => Some(With(self.mutate_with_sig(w, pol)?.into())),
Unary(u) => Some(Unary(self.mutate_unary(u, pol)?.into())),
Binary(b) => Some(Binary(self.mutate_binary(b, pol)?.into())),
If(i) => Some(If(self.mutate_if(i, pol)?.into())),
}
}
fn mutate_vec(&mut self, ty: &[Ty], pol: bool) -> Option<Interned<Vec<Ty>>> {
let mut mutated = false;
@ -49,6 +70,13 @@ pub trait MutateDriver {
})
}
fn mutate_field(&mut self, f: &Interned<FieldTy>, pol: bool) -> Option<FieldTy> {
let field = self.mutate(&f.field, pol)?;
let mut f = f.as_ref().clone();
f.field = field;
Some(f)
}
fn mutate_record(&mut self, ty: &Interned<RecordTy>, pol: bool) -> Option<RecordTy> {
let types = self.mutate_vec(&ty.types, pol)?;
@ -121,7 +149,7 @@ pub trait MutateDriver {
}
}
impl<T> MutateDriver for T
impl<T> TyMutator for T
where
T: FnMut(&Ty, bool) -> Option<Ty>,
{
@ -132,37 +160,7 @@ where
impl Ty {
/// Mutate the given type.
pub fn mutate(&self, pol: bool, checker: &mut impl MutateDriver) -> Option<Ty> {
let mut worker = Mutator;
worker.ty(self, pol, checker)
}
}
struct Mutator;
impl Mutator {
fn ty(&mut self, ty: &Ty, pol: bool, mutator: &mut impl MutateDriver) -> Option<Ty> {
use Ty::*;
match ty {
Value(..) | Any | Boolean(..) | Builtin(..) => mutator.mutate(ty, pol),
Union(v) => Some(Union(mutator.mutate_vec(v, pol)?)),
Var(..) | Let(..) => mutator.mutate(ty, pol),
Array(e) => Some(Array(mutator.mutate(e, pol)?.into())),
Dict(r) => Some(Dict(mutator.mutate_record(r, pol)?.into())),
Tuple(e) => Some(Tuple(mutator.mutate_vec(e, pol)?)),
Func(f) => Some(Func(mutator.mutate_func(f, pol)?.into())),
Args(args) => Some(Args(mutator.mutate_func(args, pol)?.into())),
Field(f) => {
let field = f.field.mutate(pol, mutator)?;
let mut f = f.as_ref().clone();
f.field = field;
Some(Field(f.into()))
}
Select(s) => Some(Select(mutator.mutate_select(s, pol)?.into())),
With(w) => Some(With(mutator.mutate_with_sig(w, pol)?.into())),
Unary(u) => Some(Unary(mutator.mutate_unary(u, pol)?.into())),
Binary(b) => Some(Binary(mutator.mutate_binary(b, pol)?.into())),
If(i) => Some(If(mutator.mutate_if(i, pol)?.into())),
}
pub fn mutate(&self, pol: bool, checker: &mut impl TyMutator) -> Option<Ty> {
checker.mutate(self, pol)
}
}

View file

@ -1,13 +1,8 @@
use crate::{adt::interner::Interned, ty::def::*};
use super::{Iface, IfaceChecker};
use crate::ty::def::*;
pub trait SelectChecker {
pub trait SelectChecker: TyCtx {
fn select(&mut self, sig: Iface, key: &Interned<str>, pol: bool);
fn bound_of_var(&mut self, _var: &Interned<TypeVar>, _pol: bool) -> Option<TypeBounds> {
None
}
}
impl Ty {
@ -17,7 +12,9 @@ impl Ty {
}
}
pub struct SelectKeyChecker<'a, T>(&'a mut T, &'a Interned<str>);
#[derive(BindTyCtx)]
#[bind(0)]
pub struct SelectKeyChecker<'a, T: TyCtx>(&'a mut T, &'a Interned<str>);
impl<'a, T: SelectChecker> SelectKeyChecker<'a, T> {
fn ty(&mut self, ty: &Ty, pol: bool) {
@ -35,8 +32,4 @@ impl<'a, T: SelectChecker> IfaceChecker for SelectKeyChecker<'a, T> {
self.0.select(iface, self.1, pol);
Some(())
}
fn check_var(&mut self, _var: &Interned<TypeVar>, _pol: bool) -> Option<TypeBounds> {
self.0.bound_of_var(_var, _pol)
}
}

View file

@ -1,6 +1,6 @@
use typst::foundations::{Func, Value};
use crate::{adt::interner::Interned, analysis::*, ty::def::*};
use crate::{analysis::*, ty::def::*};
#[derive(Debug, Clone, Copy)]
pub enum Sig<'a> {
@ -45,7 +45,7 @@ impl<'a> Sig<'a> {
})
}
pub fn shape(self, ctx: Option<&mut AnalysisContext>) -> Option<SigShape<'a>> {
pub fn shape(self, ctx: &mut impl TyCtxMut) -> Option<SigShape<'a>> {
let (cano_sig, withs) = match self {
Sig::With { sig, withs, .. } => (*sig, Some(withs)),
_ => (self, None),
@ -56,8 +56,8 @@ impl<'a> Sig<'a> {
Sig::ArrayCons(a) => SigTy::array_cons(a.as_ref().clone(), false),
Sig::TupleCons(t) => SigTy::tuple_cons(t.clone(), false),
Sig::DictCons(d) => SigTy::dict_cons(d, false),
Sig::TypeCons { val, .. } => ctx?.type_of_func(&val.constructor().ok()?)?,
Sig::Value { val, .. } => ctx?.type_of_func(val)?,
Sig::TypeCons { val, .. } => ctx.type_of_func(&val.constructor().ok()?)?,
Sig::Value { val, .. } => ctx.type_of_func(val)?,
// todo
Sig::Partialize(..) => return None,
Sig::With { .. } => return None,
@ -79,20 +79,8 @@ pub enum SigSurfaceKind {
ArrayOrDict,
}
pub trait SigChecker {
pub trait SigChecker: TyCtx {
fn check(&mut self, sig: Sig, args: &mut SigCheckContext, pol: bool) -> Option<()>;
fn check_var(&mut self, _var: &Interned<TypeVar>, _pol: bool) -> Option<TypeBounds> {
None
}
}
impl<T> SigChecker for T
where
T: FnMut(Sig, &mut SigCheckContext, bool) -> Option<()>,
{
fn check(&mut self, sig: Sig, args: &mut SigCheckContext, pol: bool) -> Option<()> {
self(sig, args, pol)
}
}
impl Ty {
@ -121,14 +109,24 @@ impl Ty {
let mut primary = None;
#[derive(BindTyCtx)]
#[bind(0)]
struct SigReprDriver<'a, C: TyCtx>(&'a C, &'a mut Option<Interned<SigTy>>);
impl<C: TyCtx> SigChecker for SigReprDriver<'_, C> {
fn check(&mut self, sig: Sig, _ctx: &mut SigCheckContext, _pol: bool) -> Option<()> {
// todo: bind type context
let sig = sig.shape(&mut ())?;
*self.1 = Some(sig.sig.clone());
Some(())
}
}
self.sig_surface(
pol,
SigSurfaceKind::Call,
&mut |sig: Sig, _ctx: &mut SigCheckContext, _pol: bool| {
let sig = sig.shape(None)?;
primary = Some(sig.sig.clone());
Some(())
},
// todo: bind type context
&mut SigReprDriver(&(), &mut primary),
);
primary
@ -141,6 +139,8 @@ pub struct SigCheckContext {
pub at: TyRef,
}
#[derive(BindTyCtx)]
#[bind(checker)]
pub struct SigCheckDriver<'a> {
ctx: SigCheckContext,
checker: &'a mut dyn SigChecker,
@ -252,12 +252,10 @@ impl BoundChecker for SigCheckDriver<'_> {
log::debug!("sig bounds: {ty:?}");
self.ty(ty, pol);
}
fn bound_of_var(&mut self, var: &Interned<TypeVar>, pol: bool) -> Option<TypeBounds> {
self.checker.check_var(var, pol)
}
}
#[derive(BindTyCtx)]
#[bind(0)]
struct MethodDriver<'a, 'b>(&'a mut SigCheckDriver<'b>, &'a StrRef);
impl<'a, 'b> MethodDriver<'a, 'b> {
@ -327,8 +325,4 @@ impl<'a, 'b> BoundChecker for MethodDriver<'a, 'b> {
_ => {}
}
}
fn bound_of_var(&mut self, var: &Interned<TypeVar>, pol: bool) -> Option<TypeBounds> {
self.0.checker.check_var(var, pol)
}
}

View file

@ -5,7 +5,7 @@ use std::collections::HashSet;
use ecow::EcoVec;
use reflexo::hash::hash128;
use crate::{adt::interner::Interned, analysis::*, ty::def::*};
use crate::{analysis::*, ty::def::*};
#[derive(Default)]
struct CompactTy {

View file

@ -1,79 +1,57 @@
use hashbrown::HashMap;
use crate::{adt::interner::Interned, analysis::*, ty::def::*};
use crate::{analysis::*, ty::def::*};
impl<'a> Sig<'a> {
pub fn call(
&self,
args: &Interned<ArgsTy>,
pol: bool,
ctx: Option<&mut AnalysisContext>,
ctx: &mut impl TyCtxMut,
) -> Option<Ty> {
let (bound_variables, body) = self.check_bind(args, ctx)?;
ctx.with_scope(|ctx| {
let body = self.check_bind(args, ctx)?;
if bound_variables.is_empty() {
return body;
}
let body = body?;
// Substitute the bound variables in the body or just body
let mut checker = SubstituteChecker { bound_variables };
Some(checker.ty(&body, pol).unwrap_or(body))
// Substitute the bound variables in the body or just body
let mut checker = SubstituteChecker { ctx };
Some(checker.ty(&body, pol).unwrap_or(body))
})
}
pub fn check_bind(
&self,
args: &Interned<ArgsTy>,
ctx: Option<&mut AnalysisContext>,
) -> Option<(HashMap<DefId, Ty>, Option<Ty>)> {
pub fn check_bind(&self, args: &Interned<ArgsTy>, ctx: &mut impl TyCtxMut) -> Option<Ty> {
let SigShape { sig, withs } = self.shape(ctx)?;
// todo: check if the signature has free variables
// let has_free_vars = sig.has_free_variables;
let has_free_vars = true;
let mut arguments = HashMap::new();
if has_free_vars {
for (arg_recv, arg_ins) in sig.matches(args, withs) {
if let Ty::Var(arg_recv) = arg_recv {
arguments.insert(arg_recv.def, arg_ins.clone());
}
for (arg_recv, arg_ins) in sig.matches(args, withs) {
if let Ty::Var(arg_recv) = arg_recv {
ctx.bind_local(arg_recv, arg_ins.clone());
}
}
Some((arguments, sig.body.clone()))
sig.body.clone()
}
}
struct SubstituteChecker {
bound_variables: HashMap<DefId, Ty>,
struct SubstituteChecker<'a, T: TyCtxMut> {
ctx: &'a mut T,
}
impl SubstituteChecker {
impl<'a, T: TyCtxMut> SubstituteChecker<'a, T> {
fn ty(&mut self, body: &Ty, pol: bool) -> Option<Ty> {
body.mutate(pol, self)
}
}
impl MutateDriver for SubstituteChecker {
impl<'a, T: TyCtxMut> TyMutator for SubstituteChecker<'a, T> {
fn mutate(&mut self, ty: &Ty, pol: bool) -> Option<Ty> {
// todo: extrude the type into a polarized type
let _ = pol;
Some(match ty {
// todo: substitute the bound in the type
Ty::Let(..) => return None,
Ty::Var(v) => {
if let Some(ty) = self.bound_variables.get(&v.def) {
ty.clone()
} else {
return None;
}
}
Ty::Value(..) | Ty::Any | Ty::Boolean(..) | Ty::Builtin(..) => return None,
_ => return None,
})
if let Ty::Var(v) = ty {
self.ctx.local_bind_of(v)
} else {
self.mutate_rec(ty, pol)
}
}
}
@ -83,7 +61,7 @@ mod tests {
use crate::ty::tests::*;
use super::{ApplyChecker, Ty};
use super::{ApplyChecker, Interned, Ty, TyCtx, TypeBounds, TypeVar};
#[test]
fn test_ty() {
use super::*;
@ -95,6 +73,14 @@ mod tests {
#[derive(Default)]
struct CallCollector(Vec<Ty>);
impl TyCtx for CallCollector {
fn local_bind_of(&self, _var: &Interned<TypeVar>) -> Option<Ty> {
None
}
fn global_bounds(&self, _var: &Interned<TypeVar>, _pol: bool) -> Option<TypeBounds> {
None
}
}
impl ApplyChecker for CallCollector {
fn apply(
&mut self,
@ -102,7 +88,7 @@ mod tests {
arguments: &crate::adt::interner::Interned<super::ArgsTy>,
pol: bool,
) {
let ty = sig.call(arguments, pol, None);
let ty = sig.call(arguments, pol, &mut ());
if let Some(ty) = ty {
self.0.push(ty);
}
@ -128,7 +114,7 @@ mod tests {
})
}
assert_snapshot!(call(literal_sig!(p1 -> p1), literal_args!(q1)), @"@q1");
assert_snapshot!(call(literal_sig!(!u1: w1 -> w1), literal_args!(!u1: w2)), @"@w2");
assert_snapshot!(call(literal_sig!(p1 -> p1), literal_args!(q1)), @"@p1");
assert_snapshot!(call(literal_sig!(!u1: w1 -> w1), literal_args!(!u1: w2)), @"@w1");
}
}