mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-07-24 05:05:00 +00:00
feat: type induction on builtin values (#694)
This commit is contained in:
parent
0a008c8cc5
commit
6d62bffdeb
15 changed files with 359 additions and 107 deletions
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
95
crates/tinymist-query/src/analysis/tyck/convert.rs
Normal file
95
crates/tinymist-query/src/analysis/tyck/convert.rs
Normal 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())),
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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" }
|
||||||
|
|
|
@ -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" }
|
||||||
|
|
|
@ -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" }
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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;
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue