diff options
author | Florian Diebold <[email protected]> | 2021-03-21 19:19:07 +0000 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2021-03-21 19:19:07 +0000 |
commit | c4fd3f47f5b4f34476f8f085f2412a46aa0fd24f (patch) | |
tree | 82b6b318da1564cabd0225dfb3c67433f80b8ab8 /crates/hir_ty | |
parent | f7be314579db29f64ef660aef1896da33d420ad6 (diff) |
Align InEnvironment with Chalk
This in particular means storing a chalk_ir::Environment, not our
TraitEnvironment. This makes InEnvironment not usable for Type, where we
need to keep the full TraitEnvironment.
Diffstat (limited to 'crates/hir_ty')
-rw-r--r-- | crates/hir_ty/src/autoderef.rs | 24 | ||||
-rw-r--r-- | crates/hir_ty/src/infer.rs | 2 | ||||
-rw-r--r-- | crates/hir_ty/src/infer/coerce.rs | 6 | ||||
-rw-r--r-- | crates/hir_ty/src/infer/expr.rs | 16 | ||||
-rw-r--r-- | crates/hir_ty/src/infer/unify.rs | 7 | ||||
-rw-r--r-- | crates/hir_ty/src/method_resolution.rs | 6 | ||||
-rw-r--r-- | crates/hir_ty/src/traits.rs | 17 | ||||
-rw-r--r-- | crates/hir_ty/src/traits/chalk/mapping.rs | 5 |
8 files changed, 38 insertions, 45 deletions
diff --git a/crates/hir_ty/src/autoderef.rs b/crates/hir_ty/src/autoderef.rs index d6f0553b1..dc5fc759a 100644 --- a/crates/hir_ty/src/autoderef.rs +++ b/crates/hir_ty/src/autoderef.rs | |||
@@ -27,9 +27,9 @@ pub fn autoderef<'a>( | |||
27 | krate: Option<CrateId>, | 27 | krate: Option<CrateId>, |
28 | ty: InEnvironment<Canonical<Ty>>, | 28 | ty: InEnvironment<Canonical<Ty>>, |
29 | ) -> impl Iterator<Item = Canonical<Ty>> + 'a { | 29 | ) -> impl Iterator<Item = Canonical<Ty>> + 'a { |
30 | let InEnvironment { value: ty, environment } = ty; | 30 | let InEnvironment { goal: ty, environment } = ty; |
31 | successors(Some(ty), move |ty| { | 31 | successors(Some(ty), move |ty| { |
32 | deref(db, krate?, InEnvironment { value: ty, environment: environment.clone() }) | 32 | deref(db, krate?, InEnvironment { goal: ty, environment: environment.clone() }) |
33 | }) | 33 | }) |
34 | .take(AUTODEREF_RECURSION_LIMIT) | 34 | .take(AUTODEREF_RECURSION_LIMIT) |
35 | } | 35 | } |
@@ -39,8 +39,8 @@ pub(crate) fn deref( | |||
39 | krate: CrateId, | 39 | krate: CrateId, |
40 | ty: InEnvironment<&Canonical<Ty>>, | 40 | ty: InEnvironment<&Canonical<Ty>>, |
41 | ) -> Option<Canonical<Ty>> { | 41 | ) -> Option<Canonical<Ty>> { |
42 | if let Some(derefed) = ty.value.value.builtin_deref() { | 42 | if let Some(derefed) = ty.goal.value.builtin_deref() { |
43 | Some(Canonical { value: derefed, binders: ty.value.binders.clone() }) | 43 | Some(Canonical { value: derefed, binders: ty.goal.binders.clone() }) |
44 | } else { | 44 | } else { |
45 | deref_by_trait(db, krate, ty) | 45 | deref_by_trait(db, krate, ty) |
46 | } | 46 | } |
@@ -67,15 +67,15 @@ fn deref_by_trait( | |||
67 | // FIXME make the Canonical / bound var handling nicer | 67 | // FIXME make the Canonical / bound var handling nicer |
68 | 68 | ||
69 | let parameters = | 69 | let parameters = |
70 | Substitution::build_for_generics(&generic_params).push(ty.value.value.clone()).build(); | 70 | Substitution::build_for_generics(&generic_params).push(ty.goal.value.clone()).build(); |
71 | 71 | ||
72 | // Check that the type implements Deref at all | 72 | // Check that the type implements Deref at all |
73 | let trait_ref = | 73 | let trait_ref = |
74 | TraitRef { trait_id: to_chalk_trait_id(deref_trait), substitution: parameters.clone() }; | 74 | TraitRef { trait_id: to_chalk_trait_id(deref_trait), substitution: parameters.clone() }; |
75 | let implements_goal = Canonical { | 75 | let implements_goal = Canonical { |
76 | binders: ty.value.binders.clone(), | 76 | binders: ty.goal.binders.clone(), |
77 | value: InEnvironment { | 77 | value: InEnvironment { |
78 | value: trait_ref.cast(&Interner), | 78 | goal: trait_ref.cast(&Interner), |
79 | environment: ty.environment.clone(), | 79 | environment: ty.environment.clone(), |
80 | }, | 80 | }, |
81 | }; | 81 | }; |
@@ -91,20 +91,20 @@ fn deref_by_trait( | |||
91 | }), | 91 | }), |
92 | ty: TyKind::BoundVar(BoundVar::new( | 92 | ty: TyKind::BoundVar(BoundVar::new( |
93 | DebruijnIndex::INNERMOST, | 93 | DebruijnIndex::INNERMOST, |
94 | ty.value.binders.len(&Interner), | 94 | ty.goal.binders.len(&Interner), |
95 | )) | 95 | )) |
96 | .intern(&Interner), | 96 | .intern(&Interner), |
97 | }; | 97 | }; |
98 | 98 | ||
99 | let obligation = projection.cast(&Interner); | 99 | let obligation = projection.cast(&Interner); |
100 | 100 | ||
101 | let in_env = InEnvironment { value: obligation, environment: ty.environment }; | 101 | let in_env = InEnvironment { goal: obligation, environment: ty.environment }; |
102 | 102 | ||
103 | let canonical = Canonical { | 103 | let canonical = Canonical { |
104 | value: in_env, | 104 | value: in_env, |
105 | binders: CanonicalVarKinds::from_iter( | 105 | binders: CanonicalVarKinds::from_iter( |
106 | &Interner, | 106 | &Interner, |
107 | ty.value.binders.iter(&Interner).cloned().chain(Some(chalk_ir::WithKind::new( | 107 | ty.goal.binders.iter(&Interner).cloned().chain(Some(chalk_ir::WithKind::new( |
108 | chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General), | 108 | chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General), |
109 | chalk_ir::UniverseIndex::ROOT, | 109 | chalk_ir::UniverseIndex::ROOT, |
110 | ))), | 110 | ))), |
@@ -134,7 +134,7 @@ fn deref_by_trait( | |||
134 | if vars.0.value[i - 1].interned(&Interner) | 134 | if vars.0.value[i - 1].interned(&Interner) |
135 | != &TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, i - 1)) | 135 | != &TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, i - 1)) |
136 | { | 136 | { |
137 | warn!("complex solution for derefing {:?}: {:?}, ignoring", ty.value, solution); | 137 | warn!("complex solution for derefing {:?}: {:?}, ignoring", ty.goal, solution); |
138 | return None; | 138 | return None; |
139 | } | 139 | } |
140 | } | 140 | } |
@@ -144,7 +144,7 @@ fn deref_by_trait( | |||
144 | }) | 144 | }) |
145 | } | 145 | } |
146 | Solution::Ambig(_) => { | 146 | Solution::Ambig(_) => { |
147 | info!("Ambiguous solution for derefing {:?}: {:?}", ty.value, solution); | 147 | info!("Ambiguous solution for derefing {:?}: {:?}", ty.goal, solution); |
148 | None | 148 | None |
149 | } | 149 | } |
150 | } | 150 | } |
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index b9e434c78..8f9cf7480 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs | |||
@@ -331,7 +331,7 @@ impl<'a> InferenceContext<'a> { | |||
331 | fn resolve_obligations_as_possible(&mut self) { | 331 | fn resolve_obligations_as_possible(&mut self) { |
332 | let obligations = mem::replace(&mut self.obligations, Vec::new()); | 332 | let obligations = mem::replace(&mut self.obligations, Vec::new()); |
333 | for obligation in obligations { | 333 | for obligation in obligations { |
334 | let in_env = InEnvironment::new(self.trait_env.clone(), obligation.clone()); | 334 | let in_env = InEnvironment::new(self.trait_env.env.clone(), obligation.clone()); |
335 | let canonicalized = self.canonicalizer().canonicalize_obligation(in_env); | 335 | let canonicalized = self.canonicalizer().canonicalize_obligation(in_env); |
336 | let solution = | 336 | let solution = |
337 | self.db.trait_solve(self.resolver.krate().unwrap(), canonicalized.value.clone()); | 337 | self.db.trait_solve(self.resolver.krate().unwrap(), canonicalized.value.clone()); |
diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index 07eb96573..9c62932b1 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs | |||
@@ -142,7 +142,7 @@ impl<'a> InferenceContext<'a> { | |||
142 | .build(); | 142 | .build(); |
143 | let trait_ref = | 143 | let trait_ref = |
144 | TraitRef { trait_id: to_chalk_trait_id(coerce_unsized_trait), substitution: substs }; | 144 | TraitRef { trait_id: to_chalk_trait_id(coerce_unsized_trait), substitution: substs }; |
145 | let goal = InEnvironment::new(self.trait_env.clone(), trait_ref.cast(&Interner)); | 145 | let goal = InEnvironment::new(self.trait_env.env.clone(), trait_ref.cast(&Interner)); |
146 | 146 | ||
147 | let canonicalizer = self.canonicalizer(); | 147 | let canonicalizer = self.canonicalizer(); |
148 | let canonicalized = canonicalizer.canonicalize_obligation(goal); | 148 | let canonicalized = canonicalizer.canonicalize_obligation(goal); |
@@ -170,8 +170,8 @@ impl<'a> InferenceContext<'a> { | |||
170 | self.db, | 170 | self.db, |
171 | self.resolver.krate(), | 171 | self.resolver.krate(), |
172 | InEnvironment { | 172 | InEnvironment { |
173 | value: canonicalized.value.clone(), | 173 | goal: canonicalized.value.clone(), |
174 | environment: self.trait_env.clone(), | 174 | environment: self.trait_env.env.clone(), |
175 | }, | 175 | }, |
176 | ) { | 176 | ) { |
177 | let derefed_ty = canonicalized.decanonicalize_ty(derefed_ty.value); | 177 | let derefed_ty = canonicalized.decanonicalize_ty(derefed_ty.value); |
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 17849d552..4e2a432ed 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs | |||
@@ -90,12 +90,12 @@ impl<'a> InferenceContext<'a> { | |||
90 | let substs = | 90 | let substs = |
91 | Substitution::build_for_generics(&generic_params).push(ty.clone()).push(arg_ty).build(); | 91 | Substitution::build_for_generics(&generic_params).push(ty.clone()).push(arg_ty).build(); |
92 | 92 | ||
93 | let trait_env = Arc::clone(&self.trait_env); | 93 | let trait_env = self.trait_env.env.clone(); |
94 | let implements_fn_trait: DomainGoal = | 94 | let implements_fn_trait: DomainGoal = |
95 | TraitRef { trait_id: to_chalk_trait_id(fn_once_trait), substitution: substs.clone() } | 95 | TraitRef { trait_id: to_chalk_trait_id(fn_once_trait), substitution: substs.clone() } |
96 | .cast(&Interner); | 96 | .cast(&Interner); |
97 | let goal = self.canonicalizer().canonicalize_obligation(InEnvironment { | 97 | let goal = self.canonicalizer().canonicalize_obligation(InEnvironment { |
98 | value: implements_fn_trait.clone(), | 98 | goal: implements_fn_trait.clone(), |
99 | environment: trait_env, | 99 | environment: trait_env, |
100 | }); | 100 | }); |
101 | if self.db.trait_solve(krate, goal.value).is_some() { | 101 | if self.db.trait_solve(krate, goal.value).is_some() { |
@@ -299,8 +299,8 @@ impl<'a> InferenceContext<'a> { | |||
299 | self.db, | 299 | self.db, |
300 | self.resolver.krate(), | 300 | self.resolver.krate(), |
301 | InEnvironment { | 301 | InEnvironment { |
302 | value: canonicalized.value.clone(), | 302 | goal: canonicalized.value.clone(), |
303 | environment: self.trait_env.clone(), | 303 | environment: self.trait_env.env.clone(), |
304 | }, | 304 | }, |
305 | ); | 305 | ); |
306 | let (param_tys, ret_ty): (Vec<Ty>, Ty) = derefs | 306 | let (param_tys, ret_ty): (Vec<Ty>, Ty) = derefs |
@@ -438,8 +438,8 @@ impl<'a> InferenceContext<'a> { | |||
438 | self.db, | 438 | self.db, |
439 | self.resolver.krate(), | 439 | self.resolver.krate(), |
440 | InEnvironment { | 440 | InEnvironment { |
441 | value: canonicalized.value.clone(), | 441 | goal: canonicalized.value.clone(), |
442 | environment: self.trait_env.clone(), | 442 | environment: self.trait_env.env.clone(), |
443 | }, | 443 | }, |
444 | ) | 444 | ) |
445 | .find_map(|derefed_ty| { | 445 | .find_map(|derefed_ty| { |
@@ -538,8 +538,8 @@ impl<'a> InferenceContext<'a> { | |||
538 | self.db, | 538 | self.db, |
539 | krate, | 539 | krate, |
540 | InEnvironment { | 540 | InEnvironment { |
541 | value: &canonicalized.value, | 541 | goal: &canonicalized.value, |
542 | environment: self.trait_env.clone(), | 542 | environment: self.trait_env.env.clone(), |
543 | }, | 543 | }, |
544 | ) { | 544 | ) { |
545 | Some(derefed_ty) => { | 545 | Some(derefed_ty) => { |
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index 7595b46cf..75250a369 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs | |||
@@ -98,15 +98,12 @@ impl<'a, 'b> Canonicalizer<'a, 'b> { | |||
98 | mut self, | 98 | mut self, |
99 | obligation: InEnvironment<DomainGoal>, | 99 | obligation: InEnvironment<DomainGoal>, |
100 | ) -> Canonicalized<InEnvironment<DomainGoal>> { | 100 | ) -> Canonicalized<InEnvironment<DomainGoal>> { |
101 | let result = match obligation.value { | 101 | let result = match obligation.goal { |
102 | DomainGoal::Holds(wc) => { | 102 | DomainGoal::Holds(wc) => { |
103 | DomainGoal::Holds(self.do_canonicalize(wc, DebruijnIndex::INNERMOST)) | 103 | DomainGoal::Holds(self.do_canonicalize(wc, DebruijnIndex::INNERMOST)) |
104 | } | 104 | } |
105 | }; | 105 | }; |
106 | self.into_canonicalized(InEnvironment { | 106 | self.into_canonicalized(InEnvironment { goal: result, environment: obligation.environment }) |
107 | value: result, | ||
108 | environment: obligation.environment, | ||
109 | }) | ||
110 | } | 107 | } |
111 | } | 108 | } |
112 | 109 | ||
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index 0abe8f0a3..8e986ddde 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs | |||
@@ -376,7 +376,7 @@ fn iterate_method_candidates_impl( | |||
376 | // Also note that when we've got a receiver like &S, even if the method we | 376 | // Also note that when we've got a receiver like &S, even if the method we |
377 | // find in the end takes &self, we still do the autoderef step (just as | 377 | // find in the end takes &self, we still do the autoderef step (just as |
378 | // rustc does an autoderef and then autoref again). | 378 | // rustc does an autoderef and then autoref again). |
379 | let ty = InEnvironment { value: ty.clone(), environment: env.clone() }; | 379 | let ty = InEnvironment { goal: ty.clone(), environment: env.env.clone() }; |
380 | 380 | ||
381 | // We have to be careful about the order we're looking at candidates | 381 | // We have to be careful about the order we're looking at candidates |
382 | // in here. Consider the case where we're resolving `x.clone()` | 382 | // in here. Consider the case where we're resolving `x.clone()` |
@@ -622,7 +622,7 @@ pub fn resolve_indexing_op( | |||
622 | krate: CrateId, | 622 | krate: CrateId, |
623 | index_trait: TraitId, | 623 | index_trait: TraitId, |
624 | ) -> Option<Canonical<Ty>> { | 624 | ) -> Option<Canonical<Ty>> { |
625 | let ty = InEnvironment { value: ty.clone(), environment: env.clone() }; | 625 | let ty = InEnvironment { goal: ty.clone(), environment: env.env.clone() }; |
626 | let deref_chain = autoderef_method_receiver(db, krate, ty); | 626 | let deref_chain = autoderef_method_receiver(db, krate, ty); |
627 | for ty in deref_chain { | 627 | for ty in deref_chain { |
628 | let goal = generic_implements_goal(db, env.clone(), index_trait, ty.clone()); | 628 | let goal = generic_implements_goal(db, env.clone(), index_trait, ty.clone()); |
@@ -794,7 +794,7 @@ fn generic_implements_goal( | |||
794 | let obligation = trait_ref.cast(&Interner); | 794 | let obligation = trait_ref.cast(&Interner); |
795 | Canonical { | 795 | Canonical { |
796 | binders: CanonicalVarKinds::from_iter(&Interner, kinds), | 796 | binders: CanonicalVarKinds::from_iter(&Interner, kinds), |
797 | value: InEnvironment::new(env, obligation), | 797 | value: InEnvironment::new(env.env.clone(), obligation), |
798 | } | 798 | } |
799 | } | 799 | } |
800 | 800 | ||
diff --git a/crates/hir_ty/src/traits.rs b/crates/hir_ty/src/traits.rs index 7dadd1ffb..ccee0e5ad 100644 --- a/crates/hir_ty/src/traits.rs +++ b/crates/hir_ty/src/traits.rs | |||
@@ -1,6 +1,5 @@ | |||
1 | //! Trait solving using Chalk. | 1 | //! Trait solving using Chalk. |
2 | use std::env::var; | 2 | use std::env::var; |
3 | use std::sync::Arc; | ||
4 | 3 | ||
5 | use base_db::CrateId; | 4 | use base_db::CrateId; |
6 | use chalk_ir::cast::Cast; | 5 | use chalk_ir::cast::Cast; |
@@ -44,7 +43,7 @@ pub struct TraitEnvironment { | |||
44 | // When we're using Chalk's Ty we can make this a BTreeMap since it's Ord, | 43 | // When we're using Chalk's Ty we can make this a BTreeMap since it's Ord, |
45 | // but for now it's too annoying... | 44 | // but for now it's too annoying... |
46 | pub(crate) traits_from_clauses: Vec<(Ty, TraitId)>, | 45 | pub(crate) traits_from_clauses: Vec<(Ty, TraitId)>, |
47 | pub(crate) env: chalk_ir::Environment<Interner>, | 46 | pub env: chalk_ir::Environment<Interner>, |
48 | } | 47 | } |
49 | 48 | ||
50 | impl TraitEnvironment { | 49 | impl TraitEnvironment { |
@@ -74,13 +73,13 @@ impl Default for TraitEnvironment { | |||
74 | /// Something (usually a goal), along with an environment. | 73 | /// Something (usually a goal), along with an environment. |
75 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | 74 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] |
76 | pub struct InEnvironment<T> { | 75 | pub struct InEnvironment<T> { |
77 | pub environment: Arc<TraitEnvironment>, | 76 | pub environment: chalk_ir::Environment<Interner>, |
78 | pub value: T, | 77 | pub goal: T, |
79 | } | 78 | } |
80 | 79 | ||
81 | impl<T> InEnvironment<T> { | 80 | impl<T> InEnvironment<T> { |
82 | pub fn new(environment: Arc<TraitEnvironment>, value: T) -> InEnvironment<T> { | 81 | pub fn new(environment: chalk_ir::Environment<Interner>, value: T) -> InEnvironment<T> { |
83 | InEnvironment { environment, value } | 82 | InEnvironment { environment, goal: value } |
84 | } | 83 | } |
85 | } | 84 | } |
86 | 85 | ||
@@ -126,18 +125,18 @@ pub(crate) fn trait_solve_query( | |||
126 | krate: CrateId, | 125 | krate: CrateId, |
127 | goal: Canonical<InEnvironment<DomainGoal>>, | 126 | goal: Canonical<InEnvironment<DomainGoal>>, |
128 | ) -> Option<Solution> { | 127 | ) -> Option<Solution> { |
129 | let _p = profile::span("trait_solve_query").detail(|| match &goal.value.value { | 128 | let _p = profile::span("trait_solve_query").detail(|| match &goal.value.goal { |
130 | DomainGoal::Holds(WhereClause::Implemented(it)) => { | 129 | DomainGoal::Holds(WhereClause::Implemented(it)) => { |
131 | db.trait_data(it.hir_trait_id()).name.to_string() | 130 | db.trait_data(it.hir_trait_id()).name.to_string() |
132 | } | 131 | } |
133 | DomainGoal::Holds(WhereClause::AliasEq(_)) => "alias_eq".to_string(), | 132 | DomainGoal::Holds(WhereClause::AliasEq(_)) => "alias_eq".to_string(), |
134 | }); | 133 | }); |
135 | log::info!("trait_solve_query({})", goal.value.value.display(db)); | 134 | log::info!("trait_solve_query({})", goal.value.goal.display(db)); |
136 | 135 | ||
137 | if let DomainGoal::Holds(WhereClause::AliasEq(AliasEq { | 136 | if let DomainGoal::Holds(WhereClause::AliasEq(AliasEq { |
138 | alias: AliasTy::Projection(projection_ty), | 137 | alias: AliasTy::Projection(projection_ty), |
139 | .. | 138 | .. |
140 | })) = &goal.value.value | 139 | })) = &goal.value.goal |
141 | { | 140 | { |
142 | if let TyKind::BoundVar(_) = &projection_ty.substitution[0].interned(&Interner) { | 141 | if let TyKind::BoundVar(_) = &projection_ty.substitution[0].interned(&Interner) { |
143 | // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible | 142 | // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible |
diff --git a/crates/hir_ty/src/traits/chalk/mapping.rs b/crates/hir_ty/src/traits/chalk/mapping.rs index 58d8f2894..aef6b8a15 100644 --- a/crates/hir_ty/src/traits/chalk/mapping.rs +++ b/crates/hir_ty/src/traits/chalk/mapping.rs | |||
@@ -455,10 +455,7 @@ where | |||
455 | type Chalk = chalk_ir::InEnvironment<T::Chalk>; | 455 | type Chalk = chalk_ir::InEnvironment<T::Chalk>; |
456 | 456 | ||
457 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::InEnvironment<T::Chalk> { | 457 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::InEnvironment<T::Chalk> { |
458 | chalk_ir::InEnvironment { | 458 | chalk_ir::InEnvironment { environment: self.environment, goal: self.goal.to_chalk(db) } |
459 | environment: self.environment.env.clone(), | ||
460 | goal: self.value.to_chalk(db), | ||
461 | } | ||
462 | } | 459 | } |
463 | 460 | ||
464 | fn from_chalk( | 461 | fn from_chalk( |