aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2019-06-26 17:35:22 +0100
committerbors[bot] <26634292+bors[bot]@users.noreply.github.com>2019-06-26 17:35:22 +0100
commit203d5dd0d0092b505db9efcff377fea154cbfe11 (patch)
tree80400bb1dc37c38bc2ce920ba616600a0d8b868d /crates
parent0cff7f84c64eb9e6524b0ecb27304b854d783ec1 (diff)
parenta198d78bd1dafbfe78c597ff0deab17ac4d9092e (diff)
Merge #1443
1443: cache chalk queries r=flodiebold a=matklad This gives a significant speedup, because chalk will call these functions several times even withing a single revision. The only significant one here is `impl_data`, but I figured it might be good to cache others just for consistency. The results I get are: Before: from scratch: 16.081457952s no change: 15.846493ms trivial change: 352.95592ms comment change: 361.998408ms const change: 457.629212ms After: from scratch: 14.910610278s no change: 14.934647ms trivial change: 85.633023ms comment change: 96.433023ms const change: 171.543296ms Seems like a nice win! Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir/src/db.rs20
-rw-r--r--crates/ra_hir/src/ty/traits.rs2
-rw-r--r--crates/ra_hir/src/ty/traits/chalk.rs412
3 files changed, 240 insertions, 194 deletions
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index d8832a9de..a9840905c 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -181,6 +181,26 @@ pub trait HirDatabase: DefDatabase + AstDatabase {
181 #[salsa::volatile] 181 #[salsa::volatile]
182 fn solver(&self, krate: Crate) -> Arc<Mutex<crate::ty::traits::Solver>>; 182 fn solver(&self, krate: Crate) -> Arc<Mutex<crate::ty::traits::Solver>>;
183 183
184 #[salsa::invoke(crate::ty::traits::chalk::associated_ty_data_query)]
185 fn associated_ty_data(&self, id: chalk_ir::TypeId) -> Arc<chalk_rust_ir::AssociatedTyDatum>;
186
187 #[salsa::invoke(crate::ty::traits::chalk::trait_datum_query)]
188 fn trait_datum(
189 &self,
190 krate: Crate,
191 trait_id: chalk_ir::TraitId,
192 ) -> Arc<chalk_rust_ir::TraitDatum>;
193
194 #[salsa::invoke(crate::ty::traits::chalk::struct_datum_query)]
195 fn struct_datum(
196 &self,
197 krate: Crate,
198 struct_id: chalk_ir::StructId,
199 ) -> Arc<chalk_rust_ir::StructDatum>;
200
201 #[salsa::invoke(crate::ty::traits::chalk::impl_datum_query)]
202 fn impl_datum(&self, krate: Crate, impl_id: chalk_ir::ImplId) -> Arc<chalk_rust_ir::ImplDatum>;
203
184 #[salsa::invoke(crate::ty::traits::implements_query)] 204 #[salsa::invoke(crate::ty::traits::implements_query)]
185 fn implements( 205 fn implements(
186 &self, 206 &self,
diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs
index 9a6349d4b..69c03a36c 100644
--- a/crates/ra_hir/src/ty/traits.rs
+++ b/crates/ra_hir/src/ty/traits.rs
@@ -12,7 +12,7 @@ use super::{TraitRef, Ty, Canonical, ProjectionTy};
12 12
13use self::chalk::{ToChalk, from_chalk}; 13use self::chalk::{ToChalk, from_chalk};
14 14
15mod chalk; 15pub(crate) mod chalk;
16 16
17pub(crate) type Solver = chalk_solve::Solver; 17pub(crate) type Solver = chalk_solve::Solver;
18 18
diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs
index 5105588ee..4ceb8b70b 100644
--- a/crates/ra_hir/src/ty/traits/chalk.rs
+++ b/crates/ra_hir/src/ty/traits/chalk.rs
@@ -10,7 +10,7 @@ use test_utils::tested_by;
10use ra_db::salsa::{InternId, InternKey}; 10use ra_db::salsa::{InternId, InternKey};
11 11
12use crate::{ 12use crate::{
13 Trait, HasGenericParams, ImplBlock, 13 Trait, HasGenericParams, ImplBlock, Crate,
14 db::HirDatabase, 14 db::HirDatabase,
15 ty::{TraitRef, Ty, ApplicationTy, TypeCtor, Substs, GenericPredicate, CallableDef, ProjectionTy}, 15 ty::{TraitRef, Ty, ApplicationTy, TypeCtor, Substs, GenericPredicate, CallableDef, ProjectionTy},
16 ty::display::HirDisplay, 16 ty::display::HirDisplay,
@@ -256,204 +256,16 @@ where
256 DB: HirDatabase, 256 DB: HirDatabase,
257{ 257{
258 fn associated_ty_data(&self, id: TypeId) -> Arc<AssociatedTyDatum> { 258 fn associated_ty_data(&self, id: TypeId) -> Arc<AssociatedTyDatum> {
259 debug!("associated_ty_data {:?}", id); 259 self.db.associated_ty_data(id)
260 let type_alias: TypeAlias = from_chalk(self.db, id);
261 let trait_ = match type_alias.container(self.db) {
262 Some(crate::Container::Trait(t)) => t,
263 _ => panic!("associated type not in trait"),
264 };
265 let generic_params = type_alias.generic_params(self.db);
266 let parameter_kinds = generic_params
267 .params_including_parent()
268 .into_iter()
269 .map(|p| chalk_ir::ParameterKind::Ty(lalrpop_intern::intern(&p.name.to_string())))
270 .collect();
271 let datum = AssociatedTyDatum {
272 trait_id: trait_.to_chalk(self.db),
273 id,
274 name: lalrpop_intern::intern(&type_alias.name(self.db).to_string()),
275 parameter_kinds,
276 // FIXME add bounds and where clauses
277 bounds: vec![],
278 where_clauses: vec![],
279 };
280 Arc::new(datum)
281 } 260 }
282 fn trait_datum(&self, trait_id: chalk_ir::TraitId) -> Arc<TraitDatum> { 261 fn trait_datum(&self, trait_id: chalk_ir::TraitId) -> Arc<TraitDatum> {
283 debug!("trait_datum {:?}", trait_id); 262 self.db.trait_datum(self.krate, trait_id)
284 if trait_id == UNKNOWN_TRAIT {
285 let trait_datum_bound = chalk_rust_ir::TraitDatumBound {
286 trait_ref: chalk_ir::TraitRef {
287 trait_id: UNKNOWN_TRAIT,
288 parameters: vec![chalk_ir::Ty::BoundVar(0).cast()],
289 },
290 associated_ty_ids: Vec::new(),
291 where_clauses: Vec::new(),
292 flags: chalk_rust_ir::TraitFlags {
293 auto: false,
294 marker: false,
295 upstream: true,
296 fundamental: false,
297 },
298 };
299 return Arc::new(TraitDatum { binders: make_binders(trait_datum_bound, 1) });
300 }
301 let trait_: Trait = from_chalk(self.db, trait_id);
302 debug!("trait {:?} = {:?}", trait_id, trait_.name(self.db));
303 let generic_params = trait_.generic_params(self.db);
304 let bound_vars = Substs::bound_vars(&generic_params);
305 let trait_ref = trait_.trait_ref(self.db).subst(&bound_vars).to_chalk(self.db);
306 let flags = chalk_rust_ir::TraitFlags {
307 auto: trait_.is_auto(self.db),
308 upstream: trait_.module(self.db).krate(self.db) != Some(self.krate),
309 // FIXME set these flags correctly
310 marker: false,
311 fundamental: false,
312 };
313 let where_clauses = convert_where_clauses(self.db, trait_.into(), &bound_vars);
314 let associated_ty_ids = trait_
315 .items(self.db)
316 .into_iter()
317 .filter_map(|trait_item| match trait_item {
318 crate::traits::TraitItem::TypeAlias(type_alias) => Some(type_alias),
319 _ => None,
320 })
321 .map(|type_alias| type_alias.to_chalk(self.db))
322 .collect();
323 let trait_datum_bound =
324 chalk_rust_ir::TraitDatumBound { trait_ref, where_clauses, flags, associated_ty_ids };
325 let trait_datum = TraitDatum { binders: make_binders(trait_datum_bound, bound_vars.len()) };
326 Arc::new(trait_datum)
327 } 263 }
328 fn struct_datum(&self, struct_id: chalk_ir::StructId) -> Arc<StructDatum> { 264 fn struct_datum(&self, struct_id: chalk_ir::StructId) -> Arc<StructDatum> {
329 debug!("struct_datum {:?}", struct_id); 265 self.db.struct_datum(self.krate, struct_id)
330 let type_ctor = from_chalk(self.db, struct_id);
331 debug!("struct {:?} = {:?}", struct_id, type_ctor);
332 // FIXME might be nicer if we can create a fake GenericParams for the TypeCtor
333 // FIXME extract this to a method on Ty
334 let (num_params, where_clauses, upstream) = match type_ctor {
335 TypeCtor::Bool
336 | TypeCtor::Char
337 | TypeCtor::Int(_)
338 | TypeCtor::Float(_)
339 | TypeCtor::Never
340 | TypeCtor::Str => (0, vec![], true),
341 TypeCtor::Slice | TypeCtor::Array | TypeCtor::RawPtr(_) | TypeCtor::Ref(_) => {
342 (1, vec![], true)
343 }
344 TypeCtor::FnPtr { num_args } => (num_args as usize + 1, vec![], true),
345 TypeCtor::Tuple { cardinality } => (cardinality as usize, vec![], true),
346 TypeCtor::FnDef(callable) => {
347 tested_by!(trait_resolution_on_fn_type);
348 let krate = match callable {
349 CallableDef::Function(f) => f.module(self.db).krate(self.db),
350 CallableDef::Struct(s) => s.module(self.db).krate(self.db),
351 CallableDef::EnumVariant(v) => {
352 v.parent_enum(self.db).module(self.db).krate(self.db)
353 }
354 };
355 let generic_def: GenericDef = match callable {
356 CallableDef::Function(f) => f.into(),
357 CallableDef::Struct(s) => s.into(),
358 CallableDef::EnumVariant(v) => v.parent_enum(self.db).into(),
359 };
360 let generic_params = generic_def.generic_params(self.db);
361 let bound_vars = Substs::bound_vars(&generic_params);
362 let where_clauses = convert_where_clauses(self.db, generic_def, &bound_vars);
363 (
364 generic_params.count_params_including_parent(),
365 where_clauses,
366 krate != Some(self.krate),
367 )
368 }
369 TypeCtor::Adt(adt) => {
370 let generic_params = adt.generic_params(self.db);
371 let bound_vars = Substs::bound_vars(&generic_params);
372 let where_clauses = convert_where_clauses(self.db, adt.into(), &bound_vars);
373 (
374 generic_params.count_params_including_parent(),
375 where_clauses,
376 adt.krate(self.db) != Some(self.krate),
377 )
378 }
379 };
380 let flags = chalk_rust_ir::StructFlags {
381 upstream,
382 // FIXME set fundamental flag correctly
383 fundamental: false,
384 };
385 let self_ty = chalk_ir::ApplicationTy {
386 name: TypeName::TypeKindId(type_ctor.to_chalk(self.db).into()),
387 parameters: (0..num_params).map(|i| chalk_ir::Ty::BoundVar(i).cast()).collect(),
388 };
389 let struct_datum_bound = chalk_rust_ir::StructDatumBound {
390 self_ty,
391 fields: Vec::new(), // FIXME add fields (only relevant for auto traits)
392 where_clauses,
393 flags,
394 };
395 let struct_datum = StructDatum { binders: make_binders(struct_datum_bound, num_params) };
396 Arc::new(struct_datum)
397 } 266 }
398 fn impl_datum(&self, impl_id: ImplId) -> Arc<ImplDatum> { 267 fn impl_datum(&self, impl_id: ImplId) -> Arc<ImplDatum> {
399 debug!("impl_datum {:?}", impl_id); 268 self.db.impl_datum(self.krate, impl_id)
400 let impl_block: ImplBlock = from_chalk(self.db, impl_id);
401 let generic_params = impl_block.generic_params(self.db);
402 let bound_vars = Substs::bound_vars(&generic_params);
403 let trait_ref = impl_block
404 .target_trait_ref(self.db)
405 .expect("FIXME handle unresolved impl block trait ref")
406 .subst(&bound_vars);
407 let impl_type = if impl_block.module().krate(self.db) == Some(self.krate) {
408 chalk_rust_ir::ImplType::Local
409 } else {
410 chalk_rust_ir::ImplType::External
411 };
412 let where_clauses = convert_where_clauses(self.db, impl_block.into(), &bound_vars);
413 let negative = impl_block.is_negative(self.db);
414 debug!(
415 "impl {:?}: {}{} where {:?}",
416 impl_id,
417 if negative { "!" } else { "" },
418 trait_ref.display(self.db),
419 where_clauses
420 );
421 let trait_ = trait_ref.trait_;
422 let trait_ref = trait_ref.to_chalk(self.db);
423 let associated_ty_values = impl_block
424 .items(self.db)
425 .into_iter()
426 .filter_map(|item| match item {
427 ImplItem::TypeAlias(t) => Some(t),
428 _ => None,
429 })
430 .filter_map(|t| {
431 let assoc_ty = trait_.associated_type_by_name(self.db, t.name(self.db))?;
432 let ty = self.db.type_for_def(t.into(), crate::Namespace::Types).subst(&bound_vars);
433 Some(chalk_rust_ir::AssociatedTyValue {
434 impl_id,
435 associated_ty_id: assoc_ty.to_chalk(self.db),
436 value: chalk_ir::Binders {
437 value: chalk_rust_ir::AssociatedTyValueBound { ty: ty.to_chalk(self.db) },
438 binders: vec![], // we don't support GATs yet
439 },
440 })
441 })
442 .collect();
443
444 let impl_datum_bound = chalk_rust_ir::ImplDatumBound {
445 trait_ref: if negative {
446 chalk_rust_ir::PolarizedTraitRef::Negative(trait_ref)
447 } else {
448 chalk_rust_ir::PolarizedTraitRef::Positive(trait_ref)
449 },
450 where_clauses,
451 associated_ty_values,
452 impl_type,
453 };
454 debug!("impl_datum: {:?}", impl_datum_bound);
455 let impl_datum = ImplDatum { binders: make_binders(impl_datum_bound, bound_vars.len()) };
456 Arc::new(impl_datum)
457 } 269 }
458 fn impls_for_trait(&self, trait_id: chalk_ir::TraitId) -> Vec<ImplId> { 270 fn impls_for_trait(&self, trait_id: chalk_ir::TraitId) -> Vec<ImplId> {
459 debug!("impls_for_trait {:?}", trait_id); 271 debug!("impls_for_trait {:?}", trait_id);
@@ -503,6 +315,220 @@ where
503 } 315 }
504} 316}
505 317
318pub(crate) fn associated_ty_data_query(
319 db: &impl HirDatabase,
320 id: TypeId,
321) -> Arc<AssociatedTyDatum> {
322 debug!("associated_ty_data {:?}", id);
323 let type_alias: TypeAlias = from_chalk(db, id);
324 let trait_ = match type_alias.container(db) {
325 Some(crate::Container::Trait(t)) => t,
326 _ => panic!("associated type not in trait"),
327 };
328 let generic_params = type_alias.generic_params(db);
329 let parameter_kinds = generic_params
330 .params_including_parent()
331 .into_iter()
332 .map(|p| chalk_ir::ParameterKind::Ty(lalrpop_intern::intern(&p.name.to_string())))
333 .collect();
334 let datum = AssociatedTyDatum {
335 trait_id: trait_.to_chalk(db),
336 id,
337 name: lalrpop_intern::intern(&type_alias.name(db).to_string()),
338 parameter_kinds,
339 // FIXME add bounds and where clauses
340 bounds: vec![],
341 where_clauses: vec![],
342 };
343 Arc::new(datum)
344}
345
346pub(crate) fn trait_datum_query(
347 db: &impl HirDatabase,
348 krate: Crate,
349 trait_id: chalk_ir::TraitId,
350) -> Arc<TraitDatum> {
351 debug!("trait_datum {:?}", trait_id);
352 if trait_id == UNKNOWN_TRAIT {
353 let trait_datum_bound = chalk_rust_ir::TraitDatumBound {
354 trait_ref: chalk_ir::TraitRef {
355 trait_id: UNKNOWN_TRAIT,
356 parameters: vec![chalk_ir::Ty::BoundVar(0).cast()],
357 },
358 associated_ty_ids: Vec::new(),
359 where_clauses: Vec::new(),
360 flags: chalk_rust_ir::TraitFlags {
361 auto: false,
362 marker: false,
363 upstream: true,
364 fundamental: false,
365 },
366 };
367 return Arc::new(TraitDatum { binders: make_binders(trait_datum_bound, 1) });
368 }
369 let trait_: Trait = from_chalk(db, trait_id);
370 debug!("trait {:?} = {:?}", trait_id, trait_.name(db));
371 let generic_params = trait_.generic_params(db);
372 let bound_vars = Substs::bound_vars(&generic_params);
373 let trait_ref = trait_.trait_ref(db).subst(&bound_vars).to_chalk(db);
374 let flags = chalk_rust_ir::TraitFlags {
375 auto: trait_.is_auto(db),
376 upstream: trait_.module(db).krate(db) != Some(krate),
377 // FIXME set these flags correctly
378 marker: false,
379 fundamental: false,
380 };
381 let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars);
382 let associated_ty_ids = trait_
383 .items(db)
384 .into_iter()
385 .filter_map(|trait_item| match trait_item {
386 crate::traits::TraitItem::TypeAlias(type_alias) => Some(type_alias),
387 _ => None,
388 })
389 .map(|type_alias| type_alias.to_chalk(db))
390 .collect();
391 let trait_datum_bound =
392 chalk_rust_ir::TraitDatumBound { trait_ref, where_clauses, flags, associated_ty_ids };
393 let trait_datum = TraitDatum { binders: make_binders(trait_datum_bound, bound_vars.len()) };
394 Arc::new(trait_datum)
395}
396
397pub(crate) fn struct_datum_query(
398 db: &impl HirDatabase,
399 krate: Crate,
400 struct_id: chalk_ir::StructId,
401) -> Arc<StructDatum> {
402 debug!("struct_datum {:?}", struct_id);
403 let type_ctor = from_chalk(db, struct_id);
404 debug!("struct {:?} = {:?}", struct_id, type_ctor);
405 // FIXME might be nicer if we can create a fake GenericParams for the TypeCtor
406 // FIXME extract this to a method on Ty
407 let (num_params, where_clauses, upstream) = match type_ctor {
408 TypeCtor::Bool
409 | TypeCtor::Char
410 | TypeCtor::Int(_)
411 | TypeCtor::Float(_)
412 | TypeCtor::Never
413 | TypeCtor::Str => (0, vec![], true),
414 TypeCtor::Slice | TypeCtor::Array | TypeCtor::RawPtr(_) | TypeCtor::Ref(_) => {
415 (1, vec![], true)
416 }
417 TypeCtor::FnPtr { num_args } => (num_args as usize + 1, vec![], true),
418 TypeCtor::Tuple { cardinality } => (cardinality as usize, vec![], true),
419 TypeCtor::FnDef(callable) => {
420 tested_by!(trait_resolution_on_fn_type);
421 let upstream = match callable {
422 CallableDef::Function(f) => f.module(db).krate(db),
423 CallableDef::Struct(s) => s.module(db).krate(db),
424 CallableDef::EnumVariant(v) => v.parent_enum(db).module(db).krate(db),
425 } != Some(krate);
426 let generic_def: GenericDef = match callable {
427 CallableDef::Function(f) => f.into(),
428 CallableDef::Struct(s) => s.into(),
429 CallableDef::EnumVariant(v) => v.parent_enum(db).into(),
430 };
431 let generic_params = generic_def.generic_params(db);
432 let bound_vars = Substs::bound_vars(&generic_params);
433 let where_clauses = convert_where_clauses(db, generic_def, &bound_vars);
434 (generic_params.count_params_including_parent(), where_clauses, upstream)
435 }
436 TypeCtor::Adt(adt) => {
437 let generic_params = adt.generic_params(db);
438 let bound_vars = Substs::bound_vars(&generic_params);
439 let where_clauses = convert_where_clauses(db, adt.into(), &bound_vars);
440 (
441 generic_params.count_params_including_parent(),
442 where_clauses,
443 adt.krate(db) != Some(krate),
444 )
445 }
446 };
447 let flags = chalk_rust_ir::StructFlags {
448 upstream,
449 // FIXME set fundamental flag correctly
450 fundamental: false,
451 };
452 let self_ty = chalk_ir::ApplicationTy {
453 name: TypeName::TypeKindId(type_ctor.to_chalk(db).into()),
454 parameters: (0..num_params).map(|i| chalk_ir::Ty::BoundVar(i).cast()).collect(),
455 };
456 let struct_datum_bound = chalk_rust_ir::StructDatumBound {
457 self_ty,
458 fields: Vec::new(), // FIXME add fields (only relevant for auto traits)
459 where_clauses,
460 flags,
461 };
462 let struct_datum = StructDatum { binders: make_binders(struct_datum_bound, num_params) };
463 Arc::new(struct_datum)
464}
465
466pub(crate) fn impl_datum_query(
467 db: &impl HirDatabase,
468 krate: Crate,
469 impl_id: ImplId,
470) -> Arc<ImplDatum> {
471 let _p = ra_prof::profile("impl_datum");
472 debug!("impl_datum {:?}", impl_id);
473 let impl_block: ImplBlock = from_chalk(db, impl_id);
474 let generic_params = impl_block.generic_params(db);
475 let bound_vars = Substs::bound_vars(&generic_params);
476 let trait_ref = impl_block
477 .target_trait_ref(db)
478 .expect("FIXME handle unresolved impl block trait ref")
479 .subst(&bound_vars);
480 let impl_type = if impl_block.module().krate(db) == Some(krate) {
481 chalk_rust_ir::ImplType::Local
482 } else {
483 chalk_rust_ir::ImplType::External
484 };
485 let where_clauses = convert_where_clauses(db, impl_block.into(), &bound_vars);
486 let negative = impl_block.is_negative(db);
487 debug!(
488 "impl {:?}: {}{} where {:?}",
489 impl_id,
490 if negative { "!" } else { "" },
491 trait_ref.display(db),
492 where_clauses
493 );
494 let trait_ = trait_ref.trait_;
495 let trait_ref = trait_ref.to_chalk(db);
496 let associated_ty_values = impl_block
497 .items(db)
498 .into_iter()
499 .filter_map(|item| match item {
500 ImplItem::TypeAlias(t) => Some(t),
501 _ => None,
502 })
503 .filter_map(|t| {
504 let assoc_ty = trait_.associated_type_by_name(db, t.name(db))?;
505 let ty = db.type_for_def(t.into(), crate::Namespace::Types).subst(&bound_vars);
506 Some(chalk_rust_ir::AssociatedTyValue {
507 impl_id,
508 associated_ty_id: assoc_ty.to_chalk(db),
509 value: chalk_ir::Binders {
510 value: chalk_rust_ir::AssociatedTyValueBound { ty: ty.to_chalk(db) },
511 binders: vec![], // we don't support GATs yet
512 },
513 })
514 })
515 .collect();
516
517 let impl_datum_bound = chalk_rust_ir::ImplDatumBound {
518 trait_ref: if negative {
519 chalk_rust_ir::PolarizedTraitRef::Negative(trait_ref)
520 } else {
521 chalk_rust_ir::PolarizedTraitRef::Positive(trait_ref)
522 },
523 where_clauses,
524 associated_ty_values,
525 impl_type,
526 };
527 debug!("impl_datum: {:?}", impl_datum_bound);
528 let impl_datum = ImplDatum { binders: make_binders(impl_datum_bound, bound_vars.len()) };
529 Arc::new(impl_datum)
530}
531
506fn id_from_chalk<T: InternKey>(chalk_id: chalk_ir::RawId) -> T { 532fn id_from_chalk<T: InternKey>(chalk_id: chalk_ir::RawId) -> T {
507 T::from_intern_id(InternId::from(chalk_id.index)) 533 T::from_intern_id(InternId::from(chalk_id.index))
508} 534}