feat: type induction on builtin values (#694)

This commit is contained in:
Myriad-Dreamin 2024-10-17 01:02:00 +08:00 committed by GitHub
parent 0a008c8cc5
commit 6d62bffdeb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 359 additions and 107 deletions

View file

@ -71,6 +71,8 @@ impl Analysis {
pub fn clear_cache(&self) { pub fn clear_cache(&self) {
self.caches.signatures.clear(); self.caches.signatures.clear();
self.caches.static_signatures.clear(); self.caches.static_signatures.clear();
self.caches.docstrings.clear();
self.caches.terms.clear();
self.caches.def_use.clear(); self.caches.def_use.clear();
self.caches.type_check.clear(); self.caches.type_check.clear();
} }
@ -85,8 +87,9 @@ pub struct AnalysisGlobalCaches {
def_use: FxDashMap<u128, (u64, Option<Arc<DefUseInfo>>)>, def_use: FxDashMap<u128, (u64, Option<Arc<DefUseInfo>>)>,
type_check: FxDashMap<u128, (u64, Option<Arc<TypeScheme>>)>, type_check: FxDashMap<u128, (u64, Option<Arc<TypeScheme>>)>,
static_signatures: FxDashMap<u128, (u64, Source, usize, Option<Signature>)>, static_signatures: FxDashMap<u128, (u64, Source, usize, Option<Signature>)>,
docstrings: FxDashMap<u128, Option<Arc<DocString>>>, docstrings: FxDashMap<u128, (u64, Option<Arc<DocString>>)>,
signatures: FxDashMap<u128, (u64, Func, Option<Signature>)>, signatures: FxDashMap<u128, (u64, Func, Option<Signature>)>,
terms: FxDashMap<u128, (u64, Value, Ty)>,
} }
/// A cache for all level of analysis results of a module. /// A cache for all level of analysis results of a module.
@ -376,11 +379,6 @@ impl<'w> AnalysisContext<'w> {
Some((cursor, get_deref_target(node, cursor))) Some((cursor, get_deref_target(node, cursor)))
} }
/// Get the module-level analysis cache of a file.
pub fn get(&self, file_id: TypstFileId) -> Option<&ModuleAnalysisCache> {
self.caches.modules.get(&file_id)
}
/// Fork a new context for searching in the workspace. /// Fork a new context for searching in the workspace.
pub fn fork_for_search<'s>(&'s mut self) -> SearchCtx<'s, 'w> { pub fn fork_for_search<'s>(&'s mut self) -> SearchCtx<'s, 'w> {
SearchCtx { SearchCtx {
@ -443,6 +441,11 @@ impl<'w> AnalysisContext<'w> {
analyze_signature(self, SignatureTarget::Runtime(func)).unwrap() analyze_signature(self, SignatureTarget::Runtime(func)).unwrap()
} }
pub(crate) fn type_of_func(&mut self, func: Func) -> Signature {
log::debug!("convert runtime func {func:?}");
analyze_signature(self, SignatureTarget::Convert(func)).unwrap()
}
/// Compute the signature of a function. /// Compute the signature of a function.
pub fn compute_signature( pub fn compute_signature(
&mut self, &mut self,
@ -450,7 +453,7 @@ impl<'w> AnalysisContext<'w> {
compute: impl FnOnce(&mut Self) -> Option<Signature>, compute: impl FnOnce(&mut Self) -> Option<Signature>,
) -> Option<Signature> { ) -> Option<Signature> {
if let Some(sig) = self.get_signature(&func) { if let Some(sig) = self.get_signature(&func) {
return Some(sig); return sig;
} }
let res = compute(self); let res = compute(self);
match func { match func {
@ -481,21 +484,27 @@ impl<'w> AnalysisContext<'w> {
.3 .3
.clone() .clone()
} }
SignatureTarget::Runtime(rt) => { SignatureTarget::Convert(rt) => self
let key = hash128(&rt); .analysis
self.analysis .caches
.caches .signatures
.signatures .entry(hash128(&(&rt, true)))
.entry(key) .or_insert_with(|| (self.lifetime, rt, res))
.or_insert_with(|| (self.lifetime, rt, res)) .2
.2 .clone(),
.clone() SignatureTarget::Runtime(rt) => self
} .analysis
.caches
.signatures
.entry(hash128(&rt))
.or_insert_with(|| (self.lifetime, rt, res))
.2
.clone(),
} }
} }
/// Get the signature of a function. /// Get the signature of a function.
fn get_signature(&self, func: &SignatureTarget) -> Option<Signature> { fn get_signature(&self, func: &SignatureTarget) -> Option<Option<Signature>> {
match func { match func {
SignatureTarget::Def(source, r) => { SignatureTarget::Def(source, r) => {
// todo: check performance on peeking signature source frequently // todo: check performance on peeking signature source frequently
@ -524,6 +533,12 @@ impl<'w> AnalysisContext<'w> {
.get(&hash128(&cache_key)) .get(&hash128(&cache_key))
.and_then(|slot| (cache_key.1 == slot.2).then_some(slot.3.clone())) .and_then(|slot| (cache_key.1 == slot.2).then_some(slot.3.clone()))
} }
SignatureTarget::Convert(rt) => self
.analysis
.caches
.signatures
.get(&hash128(&(&rt, true)))
.and_then(|slot| (rt == &slot.1).then_some(slot.2.clone())),
SignatureTarget::Runtime(rt) => self SignatureTarget::Runtime(rt) => self
.analysis .analysis
.caches .caches
@ -531,7 +546,32 @@ impl<'w> AnalysisContext<'w> {
.get(&hash128(rt)) .get(&hash128(rt))
.and_then(|slot| (rt == &slot.1).then_some(slot.2.clone())), .and_then(|slot| (rt == &slot.1).then_some(slot.2.clone())),
} }
.flatten() }
pub(crate) fn type_of_value(&mut self, val: &Value) -> Ty {
log::debug!("convert runtime value {val:?}");
// todo: check performance on peeking signature source frequently
let cache_key = val;
let cached = self
.analysis
.caches
.terms
.get(&hash128(&cache_key))
.and_then(|slot| (cache_key == &slot.1).then_some(slot.2.clone()));
if let Some(cached) = cached {
return cached;
}
let res = crate::analysis::term_value(self, val);
self.analysis
.caches
.terms
.entry(hash128(&cache_key))
.or_insert_with(|| (self.lifetime, cache_key.clone(), res.clone()));
res
} }
pub(crate) fn signature_docs( pub(crate) fn signature_docs(
@ -553,10 +593,13 @@ impl<'w> AnalysisContext<'w> {
) -> Option<Arc<DocString>> { ) -> Option<Arc<DocString>> {
let h = hash128(&(&fid, &docs, &kind)); let h = hash128(&(&fid, &docs, &kind));
let res = if let Some(res) = self.analysis.caches.docstrings.get(&h) { let res = if let Some(res) = self.analysis.caches.docstrings.get(&h) {
res.clone() res.1.clone()
} else { } else {
let res = crate::analysis::tyck::compute_docstring(self, fid, docs, kind).map(Arc::new); let res = crate::analysis::tyck::compute_docstring(self, fid, docs, kind).map(Arc::new);
self.analysis.caches.docstrings.insert(h, res.clone()); self.analysis
.caches
.docstrings
.insert(h, (self.lifetime, res.clone()));
res res
}; };
@ -608,6 +651,7 @@ impl<'w> AnalysisContext<'w> {
/// Get the def-use information of a source file. /// Get the def-use information of a source file.
pub fn def_use(&mut self, source: Source) -> Option<Arc<DefUseInfo>> { pub fn def_use(&mut self, source: Source) -> Option<Arc<DefUseInfo>> {
let mut search_ctx = self.fork_for_search(); let mut search_ctx = self.fork_for_search();
Self::def_use_(&mut search_ctx, source) Self::def_use_(&mut search_ctx, source)
} }
@ -799,6 +843,14 @@ impl<'w> AnalysisContext<'w> {
.caches .caches
.static_signatures .static_signatures
.retain(|_, (l, _, _, _)| lifetime - *l < 60); .retain(|_, (l, _, _, _)| lifetime - *l < 60);
self.analysis
.caches
.terms
.retain(|_, (l, _, _)| lifetime - *l < 60);
self.analysis
.caches
.docstrings
.retain(|_, (l, _)| lifetime - *l < 60);
self.analysis self.analysis
.caches .caches
.signatures .signatures

View file

@ -134,7 +134,11 @@ impl<'a, 'w> TyCtxMut for PostTypeCheckWorker<'a, 'w> {
} }
fn type_of_func(&mut self, func: &Func) -> Option<Interned<SigTy>> { fn type_of_func(&mut self, func: &Func) -> Option<Interned<SigTy>> {
Some(self.ctx.signature_dyn(func.clone()).type_sig()) Some(self.ctx.type_of_func(func.clone()).type_sig())
}
fn type_of_value(&mut self, val: &Value) -> Ty {
self.ctx.type_of_value(val)
} }
} }

View file

@ -1,8 +1,8 @@
//! Analysis of function signatures. //! Analysis of function signatures.
use typst::foundations::{Closure, ParamInfo}; use typst::foundations::{self, Closure, ParamInfo};
use super::{prelude::*, resolve_callee, SigTy}; use super::{prelude::*, resolve_callee, BuiltinTy, SigTy, TypeSources};
use crate::docs::UntypedSymbolDocs; use crate::docs::UntypedSymbolDocs;
use crate::syntax::get_non_strict_def_target; use crate::syntax::get_non_strict_def_target;
use crate::upstream::truncated_repr; use crate::upstream::truncated_repr;
@ -204,6 +204,8 @@ pub enum SignatureTarget<'a> {
Syntax(Source, LinkedNode<'a>), Syntax(Source, LinkedNode<'a>),
/// A function that is known at runtime. /// A function that is known at runtime.
Runtime(Func), Runtime(Func),
/// A function that is known at runtime.
Convert(Func),
} }
pub(crate) fn analyze_signature( pub(crate) fn analyze_signature(
@ -222,7 +224,7 @@ fn analyze_type_signature(
callee_node: &SignatureTarget<'_>, callee_node: &SignatureTarget<'_>,
) -> Option<Signature> { ) -> Option<Signature> {
let (type_info, ty) = match callee_node { let (type_info, ty) = match callee_node {
SignatureTarget::Def(..) => None, SignatureTarget::Def(..) | SignatureTarget::Convert(..) => None,
SignatureTarget::SyntaxFast(source, node) | SignatureTarget::Syntax(source, node) => { SignatureTarget::SyntaxFast(source, node) | SignatureTarget::Syntax(source, node) => {
let type_info = ctx.type_check(source)?; let type_info = ctx.type_check(source)?;
let ty = type_info.type_of_span(node.span())?; let ty = type_info.type_of_span(node.span())?;
@ -237,73 +239,99 @@ fn analyze_type_signature(
Some((type_info, ty)) Some((type_info, ty))
} }
}?; }?;
let type_var = ty.sources().into_iter().next()?; log::debug!("check type signature of ty: {ty:?}");
let sig_ty = ty.sig_repr(true)?;
let docstring = match type_info.var_docs.get(&type_var.def).map(|x| x.as_ref()) { // todo multiple sources
Some(UntypedSymbolDocs::Function(sig)) => sig, let mut srcs = ty.sources();
_ => return None, srcs.sort();
}; let type_var = srcs.into_iter().next()?;
match type_var {
TypeSources::Var(v) => {
let sig_ty = ty.sig_repr(true)?;
let mut param_specs = Vec::new(); let docstring = match type_info.var_docs.get(&v.def).map(|x| x.as_ref()) {
let mut has_fill_or_size_or_stroke = false; Some(UntypedSymbolDocs::Function(sig)) => sig,
let mut _broken = false; _ => return None,
};
for (doc, ty) in docstring.pos.iter().zip(sig_ty.positional_params()) { let mut param_specs = Vec::new();
let default = doc.default.clone(); let mut has_fill_or_size_or_stroke = false;
let ty = ty.clone(); let mut _broken = false;
let name = doc.name.clone(); for (doc, ty) in docstring.pos.iter().zip(sig_ty.positional_params()) {
if matches!(name.as_ref(), "fill" | "stroke" | "size") { let default = doc.default.clone();
has_fill_or_size_or_stroke = true; let ty = ty.clone();
let name = doc.name.clone();
if matches!(name.as_ref(), "fill" | "stroke" | "size") {
has_fill_or_size_or_stroke = true;
}
param_specs.push(ParamSpec {
name,
docs: Some(doc.docs.clone()),
default,
ty,
attrs: ParamAttrs::positional(),
});
}
for (name, ty) in sig_ty.named_params() {
let doc = docstring.named.get(name).unwrap();
let default = doc.default.clone();
let ty = ty.clone();
if matches!(name.as_ref(), "fill" | "stroke" | "size") {
has_fill_or_size_or_stroke = true;
}
param_specs.push(ParamSpec {
name: name.clone(),
docs: Some(doc.docs.clone()),
default,
ty,
attrs: ParamAttrs::named(),
});
}
if let Some(doc) = docstring.rest.as_ref() {
let default = doc.default.clone();
param_specs.push(ParamSpec {
name: doc.name.clone(),
docs: Some(doc.docs.clone()),
default,
ty: sig_ty.rest_param().cloned().unwrap_or(Ty::Any),
attrs: ParamAttrs::variadic(),
});
}
Some(Signature::Primary(Arc::new(PrimarySignature {
docs: Some(docstring.docs.clone()),
param_specs,
has_fill_or_size_or_stroke,
sig_ty,
_broken,
})))
} }
TypeSources::Builtin(BuiltinTy::Type(ty)) => {
param_specs.push(ParamSpec { let cons = ty.constructor().ok()?;
name, Some(ctx.type_of_func(cons))
docs: Some(doc.docs.clone()),
default,
ty,
attrs: ParamAttrs::positional(),
});
}
for (name, ty) in sig_ty.named_params() {
let doc = docstring.named.get(name).unwrap();
let default = doc.default.clone();
let ty = ty.clone();
if matches!(name.as_ref(), "fill" | "stroke" | "size") {
has_fill_or_size_or_stroke = true;
} }
TypeSources::Builtin(BuiltinTy::Element(ty)) => {
param_specs.push(ParamSpec { let cons: Func = ty.into();
name: name.clone(), Some(ctx.type_of_func(cons))
docs: Some(doc.docs.clone()), }
default, TypeSources::Builtin(..) => None,
ty, TypeSources::Ins(i) => match &i.val {
attrs: ParamAttrs::named(), foundations::Value::Func(f) => Some(ctx.type_of_func(f.clone())),
}); foundations::Value::Type(f) => {
let cons = f.constructor().ok()?;
Some(ctx.type_of_func(cons))
}
_ => None,
},
} }
if let Some(doc) = docstring.rest.as_ref() {
let default = doc.default.clone();
param_specs.push(ParamSpec {
name: doc.name.clone(),
docs: Some(doc.docs.clone()),
default,
ty: sig_ty.rest_param().cloned().unwrap_or(Ty::Any),
attrs: ParamAttrs::variadic(),
});
}
Some(Signature::Primary(Arc::new(PrimarySignature {
docs: Some(docstring.docs.clone()),
param_specs,
has_fill_or_size_or_stroke,
sig_ty,
_broken,
})))
} }
fn analyze_dyn_signature( fn analyze_dyn_signature(
@ -318,7 +346,7 @@ fn analyze_dyn_signature(
log::debug!("got function {func:?}"); log::debug!("got function {func:?}");
func func
} }
SignatureTarget::Runtime(func) => func.clone(), SignatureTarget::Convert(func) | SignatureTarget::Runtime(func) => func.clone(),
}; };
use typst::foundations::func::Repr; use typst::foundations::func::Repr;

View file

@ -9,11 +9,13 @@ use super::{
use crate::ty::*; use crate::ty::*;
mod apply; mod apply;
mod convert;
mod docs; mod docs;
mod select; mod select;
mod syntax; mod syntax;
pub(crate) use apply::*; pub(crate) use apply::*;
pub(crate) use convert::*;
pub(crate) use docs::*; pub(crate) use docs::*;
pub(crate) use select::*; pub(crate) use select::*;
@ -77,7 +79,11 @@ impl<'a, 'w> TyCtxMut for TypeChecker<'a, 'w> {
} }
fn type_of_func(&mut self, func: &Func) -> Option<Interned<SigTy>> { fn type_of_func(&mut self, func: &Func) -> Option<Interned<SigTy>> {
Some(self.ctx.signature_dyn(func.clone()).type_sig()) Some(self.ctx.type_of_func(func.clone()).type_sig())
}
fn type_of_value(&mut self, val: &Value) -> Ty {
self.ctx.type_of_value(val)
} }
} }

View file

@ -0,0 +1,95 @@
use super::*;
pub fn term_value(ctx: &mut AnalysisContext, value: &Value) -> Ty {
match value {
Value::Array(a) => {
let values = a.iter().map(term_value_rec).collect::<Vec<_>>();
Ty::Tuple(values.into())
}
// todo: term arguments
Value::Args(..) => Ty::Builtin(BuiltinTy::Args),
Value::Plugin(p) => {
// todo: create infer variables for plugin functions
let values = p
.iter()
.map(|k| (k.as_str().into(), Ty::Func(SigTy::any()), Span::detached()))
.collect();
Ty::Dict(RecordTy::new(values))
}
Value::Dict(d) => {
let values = d
.iter()
.map(|(k, v)| (k.as_str().into(), term_value_rec(v), Span::detached()))
.collect();
Ty::Dict(RecordTy::new(values))
}
Value::Module(m) => {
let values = m
.scope()
.iter()
.map(|(k, v)| (k.into(), term_value_rec(v), Span::detached()))
.collect();
Ty::Dict(RecordTy::new(values))
}
Value::Type(ty) => Ty::Builtin(BuiltinTy::Type(*ty)),
Value::Dyn(v) => Ty::Builtin(BuiltinTy::Type(v.ty())),
Value::Func(func) => Ty::Func(ctx.type_of_func(func.clone()).type_sig()),
Value::Label(..)
| Value::None
| Value::Auto
| Value::Bool(..)
| Value::Int(..)
| Value::Float(..)
| Value::Length(..)
| Value::Angle(..)
| Value::Ratio(..)
| Value::Relative(..)
| Value::Fraction(..)
| Value::Color(..)
| Value::Gradient(..)
| Value::Pattern(..)
| Value::Symbol(..)
| Value::Version(..)
| Value::Str(..)
| Value::Bytes(..)
| Value::Datetime(..)
| Value::Duration(..)
| Value::Content(..)
| Value::Styles(..) => Ty::Value(InsTy::new(value.clone())),
}
}
pub fn term_value_rec(value: &Value) -> Ty {
match value {
Value::Type(ty) => Ty::Builtin(BuiltinTy::Type(*ty)),
Value::Dyn(v) => Ty::Builtin(BuiltinTy::Type(v.ty())),
Value::None
| Value::Auto
| Value::Array(..)
| Value::Args(..)
| Value::Plugin(..)
| Value::Dict(..)
| Value::Module(..)
| Value::Func(..)
| Value::Label(..)
| Value::Bool(..)
| Value::Int(..)
| Value::Float(..)
| Value::Length(..)
| Value::Angle(..)
| Value::Ratio(..)
| Value::Relative(..)
| Value::Fraction(..)
| Value::Color(..)
| Value::Gradient(..)
| Value::Pattern(..)
| Value::Symbol(..)
| Value::Version(..)
| Value::Str(..)
| Value::Bytes(..)
| Value::Datetime(..)
| Value::Duration(..)
| Value::Content(..)
| Value::Styles(..) => Ty::Value(InsTy::new(value.clone())),
}
}

View file

@ -8,7 +8,6 @@ use crate::analysis::SelectChecker;
pub struct SelectFieldChecker<'a, 'b, 'w> { pub struct SelectFieldChecker<'a, 'b, 'w> {
pub(super) base: &'a mut TypeChecker<'b, 'w>, pub(super) base: &'a mut TypeChecker<'b, 'w>,
pub select_site: Span, pub select_site: Span,
pub key: &'a Interned<str>,
pub resultant: Vec<Ty>, pub resultant: Vec<Ty>,
} }
@ -22,11 +21,12 @@ impl<'a, 'b, 'w> SelectChecker for SelectFieldChecker<'a, 'b, 'w> {
self.base.info.witness_at_least(self.select_site, ins); self.base.info.witness_at_least(self.select_site, ins);
} }
let Some(IfaceShape { iface }) = iface.shape(Some(self.base.ctx)) else { let Some(IfaceShape { iface }) = iface.shape(self.base) else {
return; return;
}; };
let res = iface.field_by_name(self.key); let res = iface.field_by_name(key);
log::debug!("selecting field real: {key:?} -> {res:?}");
if let Some(res) = res { if let Some(res) = res {
self.resultant.push(res.clone()); self.resultant.push(res.clone());
} }

View file

@ -305,13 +305,13 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
let select_site = field_access.target().span(); let select_site = field_access.target().span();
let ty = self.check_expr_in(select_site, root.clone()); let ty = self.check_expr_in(select_site, root.clone());
let field = Interned::new_str(field_access.field().get()); let field = Interned::new_str(field_access.field().get());
log::debug!("field access: {field_access:?} => {ty:?}.{field:?}");
// todo: move this to base // todo: move this to base
let base = Ty::Select(SelectTy::new(ty.clone().into(), field.clone())); let base = Ty::Select(SelectTy::new(ty.clone().into(), field.clone()));
let mut worker = SelectFieldChecker { let mut worker = SelectFieldChecker {
base: self, base: self,
select_site, select_site,
key: &field,
resultant: vec![base], resultant: vec![base],
}; };
ty.select(&field, true, &mut worker); ty.select(&field, true, &mut worker);

View file

@ -3,4 +3,4 @@ source: crates/tinymist-query/src/analysis.rs
expression: CallSnapshot(result.as_deref()) expression: CallSnapshot(result.as_deref())
input_file: crates/tinymist-query/src/fixtures/call_info/builtin.typ input_file: crates/tinymist-query/src/fixtures/call_info/builtin.typ
--- ---
<nil> 1 -> CallParamInfo { kind: Positional, is_content_block: false, param_name: "angle" }

View file

@ -3,4 +3,6 @@ source: crates/tinymist-query/src/analysis.rs
expression: CallSnapshot(result.as_deref()) expression: CallSnapshot(result.as_deref())
input_file: crates/tinymist-query/src/fixtures/call_info/builtin_poly.typ input_file: crates/tinymist-query/src/fixtures/call_info/builtin_poly.typ
--- ---
<nil> 255 -> CallParamInfo { kind: Positional, is_content_block: false, param_name: "red" }
255 -> CallParamInfo { kind: Positional, is_content_block: false, param_name: "green" }
255 -> CallParamInfo { kind: Positional, is_content_block: false, param_name: "blue" }

View file

@ -3,4 +3,4 @@ source: crates/tinymist-query/src/analysis.rs
expression: CallSnapshot(result.as_deref()) expression: CallSnapshot(result.as_deref())
input_file: crates/tinymist-query/src/fixtures/call_info/builtin_poly2.typ input_file: crates/tinymist-query/src/fixtures/call_info/builtin_poly2.typ
--- ---
<nil> "#fff" -> CallParamInfo { kind: Positional, is_content_block: false, param_name: "red" }

View file

@ -1,9 +1,18 @@
use typst::foundations;
use crate::ty::prelude::*; use crate::ty::prelude::*;
pub trait BoundChecker: TyCtx { pub trait BoundChecker: TyCtx {
fn collect(&mut self, ty: &Ty, pol: bool); fn collect(&mut self, ty: &Ty, pol: bool);
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum TypeSources {
Var(Interned<TypeVar>),
Ins(Interned<InsTy>),
Builtin(BuiltinTy),
}
impl Ty { impl Ty {
/// Check if the given type has bounds (is combinated). /// Check if the given type has bounds (is combinated).
pub fn has_bounds(&self) -> bool { pub fn has_bounds(&self) -> bool {
@ -11,12 +20,12 @@ impl Ty {
} }
/// Get the sources of the given type. /// Get the sources of the given type.
pub fn sources(&self) -> Vec<Interned<TypeVar>> { pub fn sources(&self) -> Vec<TypeSources> {
let mut results = vec![]; let mut results = vec![];
fn collect(ty: &Ty, results: &mut Vec<Interned<TypeVar>>) { fn collect(ty: &Ty, results: &mut Vec<TypeSources>) {
use Ty::*; use Ty::*;
match ty { match ty {
Any | Boolean(_) | If(..) | Builtin(..) | Value(..) => {} Any | Boolean(_) | If(..) => {}
Dict(..) | Array(..) | Tuple(..) | Func(..) | Args(..) => {} Dict(..) | Array(..) | Tuple(..) | Func(..) | Args(..) => {}
Unary(..) | Binary(..) => {} Unary(..) | Binary(..) => {}
Field(ty) => { Field(ty) => {
@ -36,8 +45,18 @@ impl Ty {
} }
} }
Var(ty) => { Var(ty) => {
results.push(ty.clone()); results.push(TypeSources::Var(ty.clone()));
} }
Builtin(ty @ (BuiltinTy::Type(..) | BuiltinTy::Element(..))) => {
results.push(TypeSources::Builtin(ty.clone()));
}
Builtin(..) => {}
Value(ty) => match &ty.val {
foundations::Value::Type(..) | foundations::Value::Func(..) => {
results.push(TypeSources::Ins(ty.clone()));
}
_ => {}
},
With(ty) => collect(&ty.sig, results), With(ty) => collect(&ty.sig, results),
Select(ty) => collect(&ty.ty, results), Select(ty) => collect(&ty.ty, results),
} }

View file

@ -561,6 +561,19 @@ impl SigTy {
}) })
} }
/// Any constructor
pub fn any() -> Interned<SigTy> {
let rest = Ty::Array(Interned::new(Ty::Any));
Interned::new(Self {
inputs: Interned::new(vec![rest]),
body: Some(Ty::Any),
names: NameBone::empty(),
name_started: 0,
spread_left: false,
spread_right: true,
})
}
/// Unary constructor /// Unary constructor
#[comemo::memoize] #[comemo::memoize]
pub fn unary(inp: Ty, ret: Ty) -> Interned<SigTy> { pub fn unary(inp: Ty, ret: Ty) -> Interned<SigTy> {
@ -1042,6 +1055,10 @@ impl TyCtxMut for TypeScheme {
fn type_of_func(&mut self, _func: &typst::foundations::Func) -> Option<Interned<SigTy>> { fn type_of_func(&mut self, _func: &typst::foundations::Func) -> Option<Interned<SigTy>> {
None None
} }
fn type_of_value(&mut self, _val: &Value) -> Ty {
Ty::Any
}
} }
/// A type variable bounds /// A type variable bounds

View file

@ -1,4 +1,4 @@
use typst::foundations::Dict; use typst::foundations::{Dict, Module};
use super::BoundChecker; use super::BoundChecker;
use crate::ty::prelude::*; use crate::ty::prelude::*;
@ -18,6 +18,10 @@ pub enum Iface<'a> {
val: &'a Dict, val: &'a Dict,
at: &'a Ty, at: &'a Ty,
}, },
Module {
val: &'a Module,
at: &'a Ty,
},
ArrayCons(&'a TyRef), ArrayCons(&'a TyRef),
Partialize(&'a Iface<'a>), Partialize(&'a Iface<'a>),
} }
@ -34,11 +38,12 @@ impl<'a> Iface<'a> {
Iface::Type { val, .. } => Ty::Builtin(BuiltinTy::Type(*val)), Iface::Type { val, .. } => Ty::Builtin(BuiltinTy::Type(*val)),
Iface::Element { val, .. } => Ty::Builtin(BuiltinTy::Element(*val)), Iface::Element { val, .. } => Ty::Builtin(BuiltinTy::Element(*val)),
Iface::Value { at, .. } => at.clone(), Iface::Value { at, .. } => at.clone(),
Iface::Module { at, .. } => at.clone(),
Iface::Partialize(..) => return None, Iface::Partialize(..) => return None,
}) })
} }
pub fn shape(self, _ctx: Option<&mut AnalysisContext>) -> Option<IfaceShape> { pub fn shape(self, ctx: &mut impl TyCtxMut) -> Option<IfaceShape> {
log::debug!("iface shape: {self:?}"); log::debug!("iface shape: {self:?}");
let record_ins = match self { let record_ins = match self {
@ -50,7 +55,8 @@ impl<'a> Iface<'a> {
Iface::Partialize(..) => return None, Iface::Partialize(..) => return None,
Iface::Element { .. } => return None, Iface::Element { .. } => return None,
Iface::Type { .. } => return None, Iface::Type { .. } => return None,
Iface::Value { .. } => return None, Iface::Value { val, at: _ } => ctx.type_of_dict(val),
Iface::Module { val, at: _ } => ctx.type_of_module(val),
}; };
Some(IfaceShape { iface: record_ins }) Some(IfaceShape { iface: record_ins })
@ -142,10 +148,13 @@ impl<'a> IfaceCheckDriver<'a> {
Ty::Value(v) => { Ty::Value(v) => {
if self.value_as_iface() { if self.value_as_iface() {
match &v.val { match &v.val {
// Value::Func(f) => { Value::Module(t) => {
// self.checker self.checker.check(
// .check(Iface::Value { val: f, at: ty }, &mut self.ctx, pol); Iface::Module { val: t, at: ty },
// } &mut self.ctx,
pol,
);
}
Value::Dict(d) => { Value::Dict(d) => {
self.checker self.checker
.check(Iface::Value { val: d, at: ty }, &mut self.ctx, pol); .check(Iface::Value { val: d, at: ty }, &mut self.ctx, pol);

View file

@ -21,7 +21,7 @@ pub(crate) use iface::*;
pub(crate) use mutate::*; pub(crate) use mutate::*;
pub(crate) use select::*; pub(crate) use select::*;
pub(crate) use sig::*; pub(crate) use sig::*;
use typst::foundations::Func; use typst::foundations::{self, Func, Module, Value};
/// A type context. /// A type context.
pub trait TyCtx { pub trait TyCtx {
@ -53,6 +53,24 @@ pub trait TyCtxMut: TyCtx {
fn bind_local(&mut self, var: &Interned<TypeVar>, ty: Ty); fn bind_local(&mut self, var: &Interned<TypeVar>, ty: Ty);
/// Get the type of a runtime function. /// Get the type of a runtime function.
fn type_of_func(&mut self, func: &Func) -> Option<Interned<SigTy>>; fn type_of_func(&mut self, func: &Func) -> Option<Interned<SigTy>>;
/// Get the type of a runtime value.
fn type_of_value(&mut self, val: &Value) -> Ty;
/// Get the type of a runtime dict.
fn type_of_dict(&mut self, dict: &foundations::Dict) -> Interned<RecordTy> {
let ty = self.type_of_value(&Value::Dict(dict.clone()));
let Ty::Dict(ty) = ty else {
panic!("expected dict type, found {ty:?}");
};
ty
}
/// Get the type of a runtime module.
fn type_of_module(&mut self, module: &Module) -> Interned<RecordTy> {
let ty = self.type_of_value(&Value::Module(module.clone()));
let Ty::Dict(ty) = ty else {
panic!("expected dict type, found {ty:?}");
};
ty
}
} }
impl TyCtx for () { impl TyCtx for () {
@ -75,6 +93,9 @@ impl TyCtxMut for () {
fn type_of_func(&mut self, _func: &Func) -> Option<Interned<SigTy>> { fn type_of_func(&mut self, _func: &Func) -> Option<Interned<SigTy>> {
None None
} }
fn type_of_value(&mut self, _val: &Value) -> Ty {
Ty::Any
}
} }
#[cfg(test)] #[cfg(test)]

View file

@ -5,4 +5,3 @@ pub use typst::foundations::Value;
pub use super::builtin::*; pub use super::builtin::*;
pub use super::def::*; pub use super::def::*;
pub use crate::analysis::AnalysisContext;