Make QueryEngine opaque to TyCtxt.
This commit is contained in:
parent
3f868b1791
commit
5d71b99690
12 changed files with 99 additions and 63 deletions
|
@ -3890,6 +3890,7 @@ dependencies = [
|
|||
"rustc_expand",
|
||||
"rustc_hir",
|
||||
"rustc_incremental",
|
||||
"rustc_index",
|
||||
"rustc_lint",
|
||||
"rustc_metadata",
|
||||
"rustc_middle",
|
||||
|
|
|
@ -30,6 +30,7 @@ rustc_codegen_ssa = { path = "../rustc_codegen_ssa" }
|
|||
rustc_symbol_mangling = { path = "../rustc_symbol_mangling" }
|
||||
rustc_codegen_llvm = { path = "../rustc_codegen_llvm", optional = true }
|
||||
rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_index = { path = "../rustc_index" }
|
||||
rustc_metadata = { path = "../rustc_metadata" }
|
||||
rustc_mir = { path = "../rustc_mir" }
|
||||
rustc_mir_build = { path = "../rustc_mir_build" }
|
||||
|
|
|
@ -15,11 +15,13 @@ use rustc_expand::base::ExtCtxt;
|
|||
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
|
||||
use rustc_hir::definitions::Definitions;
|
||||
use rustc_hir::Crate;
|
||||
use rustc_index::vec::IndexVec;
|
||||
use rustc_lint::LintStore;
|
||||
use rustc_middle::arena::Arena;
|
||||
use rustc_middle::dep_graph::DepGraph;
|
||||
use rustc_middle::middle;
|
||||
use rustc_middle::middle::cstore::{CrateStore, MetadataLoader, MetadataLoaderDyn};
|
||||
use rustc_middle::ty::query;
|
||||
use rustc_middle::ty::query::Providers;
|
||||
use rustc_middle::ty::{self, GlobalCtxt, ResolverOutputs, TyCtxt};
|
||||
use rustc_mir as mir;
|
||||
|
@ -738,20 +740,18 @@ pub static DEFAULT_EXTERN_QUERY_PROVIDERS: SyncLazy<Providers> = SyncLazy::new(|
|
|||
extern_providers
|
||||
});
|
||||
|
||||
pub struct QueryContext<'tcx>(&'tcx GlobalCtxt<'tcx>);
|
||||
pub struct QueryContext<'tcx> {
|
||||
gcx: &'tcx GlobalCtxt<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> QueryContext<'tcx> {
|
||||
pub fn enter<F, R>(&mut self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(TyCtxt<'tcx>) -> R,
|
||||
{
|
||||
let icx = ty::tls::ImplicitCtxt::new(self.0);
|
||||
let icx = ty::tls::ImplicitCtxt::new(self.gcx);
|
||||
ty::tls::enter_context(&icx, |_| f(icx.tcx))
|
||||
}
|
||||
|
||||
pub fn print_stats(&mut self) {
|
||||
self.enter(ty::query::print_stats)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_global_ctxt<'tcx>(
|
||||
|
@ -762,6 +762,7 @@ pub fn create_global_ctxt<'tcx>(
|
|||
mut resolver_outputs: ResolverOutputs,
|
||||
outputs: OutputFilenames,
|
||||
crate_name: &str,
|
||||
queries: &'tcx OnceCell<query::Queries<'tcx>>,
|
||||
global_ctxt: &'tcx OnceCell<GlobalCtxt<'tcx>>,
|
||||
arena: &'tcx WorkerLocal<Arena<'tcx>>,
|
||||
) -> QueryContext<'tcx> {
|
||||
|
@ -785,26 +786,33 @@ pub fn create_global_ctxt<'tcx>(
|
|||
callback(sess, &mut local_providers, &mut extern_providers);
|
||||
}
|
||||
|
||||
let queries = {
|
||||
let crates = resolver_outputs.cstore.crates_untracked();
|
||||
let max_cnum = crates.iter().map(|c| c.as_usize()).max().unwrap_or(0);
|
||||
let mut providers = IndexVec::from_elem_n(extern_providers, max_cnum + 1);
|
||||
providers[LOCAL_CRATE] = local_providers;
|
||||
queries.get_or_init(|| query::Queries::new(providers, extern_providers))
|
||||
};
|
||||
|
||||
let gcx = sess.time("setup_global_ctxt", || {
|
||||
global_ctxt.get_or_init(|| {
|
||||
TyCtxt::create_global_ctxt(
|
||||
sess,
|
||||
lint_store,
|
||||
local_providers,
|
||||
extern_providers,
|
||||
arena,
|
||||
resolver_outputs,
|
||||
krate,
|
||||
defs,
|
||||
dep_graph,
|
||||
query_result_on_disk_cache,
|
||||
queries,
|
||||
&crate_name,
|
||||
&outputs,
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
QueryContext(gcx)
|
||||
QueryContext { gcx }
|
||||
}
|
||||
|
||||
/// Runs the resolution, type-checking, region checking and other
|
||||
|
|
|
@ -13,6 +13,7 @@ use rustc_incremental::DepGraphFuture;
|
|||
use rustc_lint::LintStore;
|
||||
use rustc_middle::arena::Arena;
|
||||
use rustc_middle::dep_graph::DepGraph;
|
||||
use rustc_middle::ty::query;
|
||||
use rustc_middle::ty::{GlobalCtxt, ResolverOutputs, TyCtxt};
|
||||
use rustc_serialize::json;
|
||||
use rustc_session::config::{self, OutputFilenames, OutputType};
|
||||
|
@ -71,6 +72,7 @@ impl<T> Default for Query<T> {
|
|||
pub struct Queries<'tcx> {
|
||||
compiler: &'tcx Compiler,
|
||||
gcx: OnceCell<GlobalCtxt<'tcx>>,
|
||||
queries: OnceCell<query::Queries<'tcx>>,
|
||||
|
||||
arena: WorkerLocal<Arena<'tcx>>,
|
||||
hir_arena: WorkerLocal<rustc_ast_lowering::Arena<'tcx>>,
|
||||
|
@ -92,6 +94,7 @@ impl<'tcx> Queries<'tcx> {
|
|||
Queries {
|
||||
compiler,
|
||||
gcx: OnceCell::new(),
|
||||
queries: OnceCell::new(),
|
||||
arena: WorkerLocal::new(|_| Arena::default()),
|
||||
hir_arena: WorkerLocal::new(|_| rustc_ast_lowering::Arena::default()),
|
||||
dep_graph_future: Default::default(),
|
||||
|
@ -265,6 +268,7 @@ impl<'tcx> Queries<'tcx> {
|
|||
resolver_outputs.steal(),
|
||||
outputs,
|
||||
&crate_name,
|
||||
&self.queries,
|
||||
&self.gcx,
|
||||
&self.arena,
|
||||
))
|
||||
|
@ -429,7 +433,7 @@ impl Compiler {
|
|||
}
|
||||
|
||||
if self.session().opts.debugging_opts.query_stats {
|
||||
gcx.print_stats();
|
||||
gcx.enter(query::print_stats);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -354,10 +354,9 @@ fn add_query_description_impl(
|
|||
quote! {
|
||||
#[inline]
|
||||
fn try_load_from_disk(
|
||||
tcx: QueryCtxt<'tcx>,
|
||||
id: SerializedDepNodeIndex
|
||||
#tcx: QueryCtxt<'tcx>,
|
||||
#id: SerializedDepNodeIndex
|
||||
) -> Option<Self::Value> {
|
||||
let (#tcx, #id) = (*tcx, id);
|
||||
#block
|
||||
}
|
||||
}
|
||||
|
@ -394,11 +393,10 @@ fn add_query_description_impl(
|
|||
#[inline]
|
||||
#[allow(unused_variables, unused_braces)]
|
||||
fn cache_on_disk(
|
||||
tcx: QueryCtxt<'tcx>,
|
||||
key: &Self::Key,
|
||||
value: Option<&Self::Value>
|
||||
#tcx: QueryCtxt<'tcx>,
|
||||
#key: &Self::Key,
|
||||
#value: Option<&Self::Value>
|
||||
) -> bool {
|
||||
let (#tcx, #key, #value) = (*tcx, key, value);
|
||||
#expr
|
||||
}
|
||||
|
||||
|
|
|
@ -262,7 +262,7 @@ pub mod dep_kind {
|
|||
|
||||
if let Some(key) = recover(tcx, dep_node) {
|
||||
force_query::<queries::$variant<'_>, _>(
|
||||
QueryCtxt(tcx),
|
||||
QueryCtxt { tcx, queries: tcx.queries },
|
||||
key,
|
||||
DUMMY_SP,
|
||||
*dep_node
|
||||
|
@ -288,7 +288,8 @@ pub mod dep_kind {
|
|||
.unwrap_or(false));
|
||||
|
||||
let key = recover(tcx, dep_node).unwrap_or_else(|| panic!("Failed to recover key for {:?} with hash {}", dep_node, dep_node.hash));
|
||||
if queries::$variant::cache_on_disk(QueryCtxt(tcx), &key, None) {
|
||||
let qcx = QueryCtxt { tcx, queries: tcx.queries };
|
||||
if queries::$variant::cache_on_disk(qcx, &key, None) {
|
||||
let _ = tcx.$variant(key);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -699,7 +699,7 @@ rustc_queries! {
|
|||
load_cached(tcx, id) {
|
||||
let typeck_results: Option<ty::TypeckResults<'tcx>> = tcx
|
||||
.on_disk_cache.as_ref()
|
||||
.and_then(|c| c.try_load_query_result(tcx, id));
|
||||
.and_then(|c| c.try_load_query_result(*tcx, id));
|
||||
|
||||
typeck_results.map(|x| &*tcx.arena.alloc(x))
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use crate::middle::stability;
|
|||
use crate::mir::interpret::{self, Allocation, ConstValue, Scalar};
|
||||
use crate::mir::{Body, Field, Local, Place, PlaceElem, ProjectionKind, Promoted};
|
||||
use crate::traits;
|
||||
use crate::ty::query::{self, OnDiskCache, TyCtxtAt};
|
||||
use crate::ty::query::{self, OnDiskCache, Queries, TyCtxtAt};
|
||||
use crate::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSubsts};
|
||||
use crate::ty::TyKind::*;
|
||||
use crate::ty::{
|
||||
|
@ -968,7 +968,7 @@ pub struct GlobalCtxt<'tcx> {
|
|||
/// This is `None` if we are not incremental compilation mode
|
||||
pub(crate) on_disk_cache: Option<OnDiskCache<'tcx>>,
|
||||
|
||||
pub queries: query::Queries<'tcx>,
|
||||
pub queries: &'tcx query::Queries<'tcx>,
|
||||
pub query_caches: query::QueryCaches<'tcx>,
|
||||
|
||||
maybe_unused_trait_imports: FxHashSet<LocalDefId>,
|
||||
|
@ -1109,14 +1109,13 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
pub fn create_global_ctxt(
|
||||
s: &'tcx Session,
|
||||
lint_store: Lrc<dyn Any + sync::Send + sync::Sync>,
|
||||
local_providers: ty::query::Providers,
|
||||
extern_providers: ty::query::Providers,
|
||||
arena: &'tcx WorkerLocal<Arena<'tcx>>,
|
||||
resolutions: ty::ResolverOutputs,
|
||||
krate: &'tcx hir::Crate<'tcx>,
|
||||
definitions: &'tcx Definitions,
|
||||
dep_graph: DepGraph,
|
||||
on_disk_cache: Option<query::OnDiskCache<'tcx>>,
|
||||
queries: &'tcx Queries<'tcx>,
|
||||
crate_name: &str,
|
||||
output_filenames: &OutputFilenames,
|
||||
) -> GlobalCtxt<'tcx> {
|
||||
|
@ -1128,10 +1127,6 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
let common_lifetimes = CommonLifetimes::new(&interners);
|
||||
let common_consts = CommonConsts::new(&interners, &common_types);
|
||||
let cstore = resolutions.cstore;
|
||||
let crates = cstore.crates_untracked();
|
||||
let max_cnum = crates.iter().map(|c| c.as_usize()).max().unwrap_or(0);
|
||||
let mut providers = IndexVec::from_elem_n(extern_providers, max_cnum + 1);
|
||||
providers[LOCAL_CRATE] = local_providers;
|
||||
|
||||
let mut trait_map: FxHashMap<_, FxHashMap<_, _>> = FxHashMap::default();
|
||||
for (hir_id, v) in krate.trait_map.iter() {
|
||||
|
@ -1161,7 +1156,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
untracked_crate: krate,
|
||||
definitions,
|
||||
on_disk_cache,
|
||||
queries: query::Queries::new(providers, extern_providers),
|
||||
queries,
|
||||
query_caches: query::QueryCaches::default(),
|
||||
ty_rcache: Default::default(),
|
||||
pred_rcache: Default::default(),
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
use crate::ty::query::QueryCtxt;
|
||||
use crate::ty::tls;
|
||||
|
||||
use rustc_query_system::query::deadlock;
|
||||
use rustc_rayon_core as rayon_core;
|
||||
use std::thread;
|
||||
|
||||
|
@ -21,7 +18,7 @@ pub unsafe fn handle_deadlock() {
|
|||
thread::spawn(move || {
|
||||
tls::enter_context(icx, |_| {
|
||||
rustc_span::SESSION_GLOBALS
|
||||
.set(session_globals, || tls::with(|tcx| deadlock(QueryCtxt(tcx), ®istry)))
|
||||
.set(session_globals, || tls::with(|tcx| tcx.queries.deadlock(tcx, ®istry)))
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId};
|
|||
use rustc_hir::lang_items::{LangItem, LanguageItems};
|
||||
use rustc_hir::{Crate, ItemLocalId, TraitCandidate};
|
||||
use rustc_index::{bit_set::FiniteBitSet, vec::IndexVec};
|
||||
use rustc_serialize::opaque;
|
||||
use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion};
|
||||
use rustc_session::utils::NativeLibKind;
|
||||
use rustc_session::CrateDisambiguator;
|
||||
|
|
|
@ -133,7 +133,7 @@ struct Footer {
|
|||
foreign_def_path_hashes: UnhashMap<DefPathHash, RawDefId>,
|
||||
}
|
||||
|
||||
type EncodedQueryResultIndex = Vec<(SerializedDepNodeIndex, AbsoluteBytePos)>;
|
||||
pub type EncodedQueryResultIndex = Vec<(SerializedDepNodeIndex, AbsoluteBytePos)>;
|
||||
type EncodedDiagnosticsIndex = Vec<(SerializedDepNodeIndex, AbsoluteBytePos)>;
|
||||
type EncodedDiagnostics = Vec<Diagnostic>;
|
||||
|
||||
|
@ -141,7 +141,7 @@ type EncodedDiagnostics = Vec<Diagnostic>;
|
|||
struct SourceFileIndex(u32);
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Encodable, Decodable)]
|
||||
struct AbsoluteBytePos(u32);
|
||||
pub struct AbsoluteBytePos(u32);
|
||||
|
||||
impl AbsoluteBytePos {
|
||||
fn new(pos: usize) -> AbsoluteBytePos {
|
||||
|
@ -308,22 +308,7 @@ impl<'sess> OnDiskCache<'sess> {
|
|||
tcx.sess.time("encode_query_results", || -> FileEncodeResult {
|
||||
let enc = &mut encoder;
|
||||
let qri = &mut query_result_index;
|
||||
|
||||
macro_rules! encode_queries {
|
||||
($($query:ident,)*) => {
|
||||
$(
|
||||
encode_query_results::<ty::query::queries::$query<'_>>(
|
||||
QueryCtxt(tcx),
|
||||
enc,
|
||||
qri
|
||||
)?;
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
rustc_cached_queries!(encode_queries!);
|
||||
|
||||
Ok(())
|
||||
tcx.queries.encode_query_results(tcx, enc, qri)
|
||||
})?;
|
||||
|
||||
// Encode diagnostics.
|
||||
|
@ -973,7 +958,7 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [Span] {
|
|||
|
||||
//- ENCODING -------------------------------------------------------------------
|
||||
|
||||
trait OpaqueEncoder: Encoder {
|
||||
pub trait OpaqueEncoder: Encoder {
|
||||
fn position(&self) -> usize;
|
||||
}
|
||||
|
||||
|
@ -985,7 +970,7 @@ impl OpaqueEncoder for FileEncoder {
|
|||
}
|
||||
|
||||
/// An encoder that can write to the incremental compilation cache.
|
||||
struct CacheEncoder<'a, 'tcx, E: OpaqueEncoder> {
|
||||
pub struct CacheEncoder<'a, 'tcx, E: OpaqueEncoder> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
encoder: &'a mut E,
|
||||
type_shorthands: FxHashMap<Ty<'tcx>, usize>,
|
||||
|
@ -1230,7 +1215,7 @@ impl<'a> Decodable<opaque::Decoder<'a>> for IntEncodedWithFixedSize {
|
|||
}
|
||||
}
|
||||
|
||||
fn encode_query_results<'a, 'tcx, Q>(
|
||||
pub fn encode_query_results<'a, 'tcx, Q>(
|
||||
tcx: QueryCtxt<'tcx>,
|
||||
encoder: &mut CacheEncoder<'a, 'tcx, FileEncoder>,
|
||||
query_result_index: &mut EncodedQueryResultIndex,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
//! generate the actual methods on tcx which find and execute the provider,
|
||||
//! manage the caches, and so forth.
|
||||
|
||||
use crate::ty::query::Query;
|
||||
use crate::ty::query::{on_disk_cache, Query};
|
||||
use crate::ty::tls::{self, ImplicitCtxt};
|
||||
use crate::ty::{self, TyCtxt};
|
||||
use rustc_query_system::dep_graph::HasDepContext;
|
||||
|
@ -13,17 +13,21 @@ use rustc_data_structures::fx::FxHashMap;
|
|||
use rustc_data_structures::sync::Lock;
|
||||
use rustc_data_structures::thin_vec::ThinVec;
|
||||
use rustc_errors::{struct_span_err, Diagnostic, DiagnosticBuilder, Handler, Level};
|
||||
use rustc_serialize::opaque;
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::Span;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct QueryCtxt<'tcx>(pub TyCtxt<'tcx>);
|
||||
pub struct QueryCtxt<'tcx> {
|
||||
pub tcx: TyCtxt<'tcx>,
|
||||
pub queries: &'tcx super::Queries<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> std::ops::Deref for QueryCtxt<'tcx> {
|
||||
type Target = TyCtxt<'tcx>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
&self.tcx
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,7 +38,7 @@ impl HasDepContext for QueryCtxt<'tcx> {
|
|||
|
||||
#[inline]
|
||||
fn dep_context(&self) -> &Self::DepContext {
|
||||
&self.0
|
||||
&self.tcx
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,7 +53,7 @@ impl QueryContext for QueryCtxt<'tcx> {
|
|||
}
|
||||
|
||||
fn def_path_str(&self, def_id: DefId) -> String {
|
||||
self.0.def_path_str(def_id)
|
||||
self.tcx.def_path_str(def_id)
|
||||
}
|
||||
|
||||
fn current_query_job(&self) -> Option<QueryJobId<Self::DepKind>> {
|
||||
|
@ -142,6 +146,28 @@ impl<'tcx> QueryCtxt<'tcx> {
|
|||
err
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn encode_query_results(
|
||||
self,
|
||||
encoder: &mut on_disk_cache::CacheEncoder<'a, 'tcx, opaque::FileEncoder>,
|
||||
query_result_index: &mut on_disk_cache::EncodedQueryResultIndex,
|
||||
) -> opaque::FileEncodeResult {
|
||||
macro_rules! encode_queries {
|
||||
($($query:ident,)*) => {
|
||||
$(
|
||||
on_disk_cache::encode_query_results::<ty::query::queries::$query<'_>>(
|
||||
self,
|
||||
encoder,
|
||||
query_result_index
|
||||
)?;
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
rustc_cached_queries!(encode_queries!);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TyCtxt<'tcx> {
|
||||
|
@ -174,7 +200,10 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
"#{} [{}] {}",
|
||||
i,
|
||||
query_info.info.query.name(),
|
||||
query_info.info.query.describe(QueryCtxt(icx.tcx))
|
||||
query_info
|
||||
.info
|
||||
.query
|
||||
.describe(QueryCtxt { tcx: icx.tcx, queries: icx.tcx.queries })
|
||||
),
|
||||
);
|
||||
diag.span =
|
||||
|
@ -570,7 +599,7 @@ macro_rules! define_queries_struct {
|
|||
}
|
||||
|
||||
impl<$tcx> Queries<$tcx> {
|
||||
pub(crate) fn new(
|
||||
pub fn new(
|
||||
providers: IndexVec<CrateNum, Providers>,
|
||||
fallback_extern_providers: Providers,
|
||||
) -> Self {
|
||||
|
@ -597,17 +626,33 @@ macro_rules! define_queries_struct {
|
|||
Some(jobs)
|
||||
}
|
||||
|
||||
#[cfg(parallel_compiler)]
|
||||
unsafe fn deadlock(&'tcx self, tcx: TyCtxt<'tcx>, registry: &rustc_rayon_core::Registry) {
|
||||
let tcx = QueryCtxt { tcx, queries: self };
|
||||
rustc_query_system::query::deadlock(tcx, registry)
|
||||
}
|
||||
|
||||
pub(crate) fn encode_query_results(
|
||||
&'tcx self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
encoder: &mut on_disk_cache::CacheEncoder<'a, 'tcx, opaque::FileEncoder>,
|
||||
query_result_index: &mut on_disk_cache::EncodedQueryResultIndex,
|
||||
) -> opaque::FileEncodeResult {
|
||||
let tcx = QueryCtxt { tcx, queries: self };
|
||||
tcx.encode_query_results(encoder, query_result_index)
|
||||
}
|
||||
|
||||
$($(#[$attr])*
|
||||
#[inline(always)]
|
||||
fn $name(
|
||||
&self,
|
||||
&'tcx self,
|
||||
tcx: TyCtxt<$tcx>,
|
||||
span: Span,
|
||||
key: query_keys::$name<$tcx>,
|
||||
lookup: QueryLookup,
|
||||
mode: QueryMode,
|
||||
) -> Option<query_stored::$name<$tcx>> {
|
||||
let qcx = QueryCtxt(tcx);
|
||||
let qcx = QueryCtxt { tcx, queries: self };
|
||||
get_query::<queries::$name<$tcx>, _>(qcx, span, key, lookup, mode)
|
||||
})*
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue