Skip to content

Commit b460e4b

Browse files
committed
Auto merge of #772 - eggyal:split-fallible-infallible-folding, r=jackh726
Split fallible infallible folding For folding operations that cannot fail, the existing fallible folding trait is a little unergonomic. This PR introduces an infallible folder trait that can be used as a more ergonomic alternative in such cases, reflecting the status quo in rustc. Infallible folders must however also implement the fallible trait (it's a supertrait of the infallible one), but such implementation should merely delegate to the infallible trait. Coherence rules unfortunately do not permit this to be provided by blanket implementation, however delegating implementations can be simply derived on infallible folders via the `chalk_derive::FallibleTypeFolder` macro. r? `@jackh726`
2 parents d875af0 + 994bc59 commit b460e4b

File tree

14 files changed

+897
-444
lines changed

14 files changed

+897
-444
lines changed

chalk-derive/src/lib.rs

Lines changed: 222 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@ extern crate proc_macro;
33
use proc_macro2::{Span, TokenStream};
44
use quote::quote;
55
use quote::ToTokens;
6-
use syn::{parse_quote, DeriveInput, GenericParam, Ident, TypeParamBound};
6+
use syn::{parse_quote, DeriveInput, Ident, TypeParam, TypeParamBound};
77

88
use synstructure::decl_derive;
99

1010
/// Checks whether a generic parameter has a `: HasInterner` bound
11-
fn has_interner(param: &GenericParam) -> Option<&Ident> {
11+
fn has_interner(param: &TypeParam) -> Option<&Ident> {
1212
bounded_by_trait(param, "HasInterner")
1313
}
1414

1515
/// Checks whether a generic parameter has a `: Interner` bound
16-
fn is_interner(param: &GenericParam) -> Option<&Ident> {
16+
fn is_interner(param: &TypeParam) -> Option<&Ident> {
1717
bounded_by_trait(param, "Interner")
1818
}
1919

@@ -28,48 +28,44 @@ fn has_interner_attr(input: &DeriveInput) -> Option<TokenStream> {
2828
)
2929
}
3030

31-
fn bounded_by_trait<'p>(param: &'p GenericParam, name: &str) -> Option<&'p Ident> {
31+
fn bounded_by_trait<'p>(param: &'p TypeParam, name: &str) -> Option<&'p Ident> {
3232
let name = Some(String::from(name));
33-
match param {
34-
GenericParam::Type(ref t) => t.bounds.iter().find_map(|b| {
35-
if let TypeParamBound::Trait(trait_bound) = b {
36-
if trait_bound
37-
.path
38-
.segments
39-
.last()
40-
.map(|s| s.ident.to_string())
41-
== name
42-
{
43-
return Some(&t.ident);
44-
}
33+
param.bounds.iter().find_map(|b| {
34+
if let TypeParamBound::Trait(trait_bound) = b {
35+
if trait_bound
36+
.path
37+
.segments
38+
.last()
39+
.map(|s| s.ident.to_string())
40+
== name
41+
{
42+
return Some(&param.ident);
4543
}
46-
None
47-
}),
48-
_ => None,
49-
}
44+
}
45+
None
46+
})
5047
}
5148

52-
fn get_generic_param(input: &DeriveInput) -> &GenericParam {
53-
match input.generics.params.len() {
54-
1 => {}
49+
fn get_intern_param(input: &DeriveInput) -> Option<(DeriveKind, &Ident)> {
50+
let mut params = input.generics.type_params().filter_map(|param| {
51+
has_interner(param)
52+
.map(|ident| (DeriveKind::FromHasInterner, ident))
53+
.or_else(|| is_interner(param).map(|ident| (DeriveKind::FromInterner, ident)))
54+
});
5555

56-
0 => panic!(
57-
"deriving this trait requires a single type parameter or a `#[has_interner]` attr"
58-
),
56+
let param = params.next();
57+
assert!(params.next().is_none(), "deriving this trait only works with at most one type parameter that implements HasInterner or Interner");
5958

60-
_ => panic!("deriving this trait only works with a single type parameter"),
61-
};
62-
&input.generics.params[0]
59+
param
6360
}
6461

65-
fn get_generic_param_name(input: &DeriveInput) -> Option<&Ident> {
66-
match get_generic_param(input) {
67-
GenericParam::Type(t) => Some(&t.ident),
68-
_ => None,
69-
}
62+
fn get_intern_param_name(input: &DeriveInput) -> &Ident {
63+
get_intern_param(input)
64+
.expect("deriving this trait requires a parameter that implements HasInterner or Interner")
65+
.1
7066
}
7167

72-
fn find_interner(s: &mut synstructure::Structure) -> (TokenStream, DeriveKind) {
68+
fn try_find_interner(s: &mut synstructure::Structure) -> Option<(TokenStream, DeriveKind)> {
7369
let input = s.ast();
7470

7571
if let Some(arg) = has_interner_attr(input) {
@@ -79,35 +75,40 @@ fn find_interner(s: &mut synstructure::Structure) -> (TokenStream, DeriveKind) {
7975
// struct S {
8076
//
8177
// }
82-
return (arg, DeriveKind::FromHasInternerAttr);
78+
return Some((arg, DeriveKind::FromHasInternerAttr));
8379
}
8480

85-
let generic_param0 = get_generic_param(input);
86-
87-
if let Some(param) = has_interner(generic_param0) {
88-
// HasInterner bound:
89-
//
90-
// Example:
91-
//
92-
// struct Binders<T: HasInterner> { }
93-
s.add_impl_generic(parse_quote! { _I });
94-
95-
s.add_where_predicate(parse_quote! { _I: ::chalk_ir::interner::Interner });
96-
s.add_where_predicate(
97-
parse_quote! { #param: ::chalk_ir::interner::HasInterner<Interner = _I> },
98-
);
81+
get_intern_param(input).map(|generic_param0| match generic_param0 {
82+
(DeriveKind::FromHasInterner, param) => {
83+
// HasInterner bound:
84+
//
85+
// Example:
86+
//
87+
// struct Binders<T: HasInterner> { }
88+
s.add_impl_generic(parse_quote! { _I });
89+
90+
s.add_where_predicate(parse_quote! { _I: ::chalk_ir::interner::Interner });
91+
s.add_where_predicate(
92+
parse_quote! { #param: ::chalk_ir::interner::HasInterner<Interner = _I> },
93+
);
94+
95+
(quote! { _I }, DeriveKind::FromHasInterner)
96+
}
97+
(DeriveKind::FromInterner, i) => {
98+
// Interner bound:
99+
//
100+
// Example:
101+
//
102+
// struct Foo<I: Interner> { }
103+
(quote! { #i }, DeriveKind::FromInterner)
104+
}
105+
_ => unreachable!(),
106+
})
107+
}
99108

100-
(quote! { _I }, DeriveKind::FromHasInterner)
101-
} else if let Some(i) = is_interner(generic_param0) {
102-
// Interner bound:
103-
//
104-
// Example:
105-
//
106-
// struct Foo<I: Interner> { }
107-
(quote! { #i }, DeriveKind::FromInterner)
108-
} else {
109-
panic!("deriving this trait requires a parameter that implements HasInterner or Interner",);
110-
}
109+
fn find_interner(s: &mut synstructure::Structure) -> (TokenStream, DeriveKind) {
110+
try_find_interner(s)
111+
.expect("deriving this trait requires a `#[has_interner]` attr or a parameter that implements HasInterner or Interner")
111112
}
112113

113114
#[derive(Copy, Clone, PartialEq)]
@@ -117,6 +118,7 @@ enum DeriveKind {
117118
FromInterner,
118119
}
119120

121+
decl_derive!([FallibleTypeFolder, attributes(has_interner)] => derive_fallible_type_folder);
120122
decl_derive!([HasInterner, attributes(has_interner)] => derive_has_interner);
121123
decl_derive!([TypeVisitable, attributes(has_interner)] => derive_type_visitable);
122124
decl_derive!([TypeSuperVisitable, attributes(has_interner)] => derive_type_super_visitable);
@@ -173,7 +175,7 @@ fn derive_any_type_visitable(
173175
});
174176

175177
if kind == DeriveKind::FromHasInterner {
176-
let param = get_generic_param_name(input).unwrap();
178+
let param = get_intern_param_name(input);
177179
s.add_where_predicate(parse_quote! { #param: ::chalk_ir::visit::TypeVisitable<#interner> });
178180
}
179181

@@ -269,29 +271,183 @@ fn derive_type_foldable(mut s: synstructure::Structure) -> TokenStream {
269271
vi.construct(|_, index| {
270272
let bind = &bindings[index];
271273
quote! {
272-
::chalk_ir::fold::TypeFoldable::fold_with(#bind, folder, outer_binder)?
274+
::chalk_ir::fold::TypeFoldable::try_fold_with(#bind, folder, outer_binder)?
273275
}
274276
})
275277
});
276278

277279
let input = s.ast();
278280

279281
if kind == DeriveKind::FromHasInterner {
280-
let param = get_generic_param_name(input).unwrap();
282+
let param = get_intern_param_name(input);
281283
s.add_where_predicate(parse_quote! { #param: ::chalk_ir::fold::TypeFoldable<#interner> });
282284
};
283285

284286
s.add_bounds(synstructure::AddBounds::None);
285287
s.bound_impl(
286288
quote!(::chalk_ir::fold::TypeFoldable<#interner>),
287289
quote! {
288-
fn fold_with<E>(
290+
fn try_fold_with<E>(
289291
self,
290-
folder: &mut dyn ::chalk_ir::fold::TypeFolder < #interner, Error = E >,
292+
folder: &mut dyn ::chalk_ir::fold::FallibleTypeFolder < #interner, Error = E >,
291293
outer_binder: ::chalk_ir::DebruijnIndex,
292294
) -> ::std::result::Result<Self, E> {
293295
Ok(match self { #body })
294296
}
295297
},
296298
)
297299
}
300+
301+
fn derive_fallible_type_folder(mut s: synstructure::Structure) -> TokenStream {
302+
let interner = try_find_interner(&mut s).map_or_else(
303+
|| {
304+
s.add_impl_generic(parse_quote! { _I });
305+
s.add_where_predicate(parse_quote! { _I: ::chalk_ir::interner::Interner });
306+
quote! { _I }
307+
},
308+
|(interner, _)| interner,
309+
);
310+
s.underscore_const(true);
311+
s.unbound_impl(
312+
quote!(::chalk_ir::fold::FallibleTypeFolder<#interner>),
313+
quote! {
314+
type Error = ::core::convert::Infallible;
315+
316+
fn as_dyn(&mut self) -> &mut dyn ::chalk_ir::fold::FallibleTypeFolder<I, Error = Self::Error> {
317+
self
318+
}
319+
320+
fn try_fold_ty(
321+
&mut self,
322+
ty: ::chalk_ir::Ty<#interner>,
323+
outer_binder: ::chalk_ir::DebruijnIndex,
324+
) -> ::core::result::Result<::chalk_ir::Ty<#interner>, Self::Error> {
325+
::core::result::Result::Ok(::chalk_ir::fold::TypeFolder::fold_ty(self, ty, outer_binder))
326+
}
327+
328+
fn try_fold_lifetime(
329+
&mut self,
330+
lifetime: ::chalk_ir::Lifetime<#interner>,
331+
outer_binder: ::chalk_ir::DebruijnIndex,
332+
) -> ::core::result::Result<::chalk_ir::Lifetime<#interner>, Self::Error> {
333+
::core::result::Result::Ok(::chalk_ir::fold::TypeFolder::fold_lifetime(self, lifetime, outer_binder))
334+
}
335+
336+
fn try_fold_const(
337+
&mut self,
338+
constant: ::chalk_ir::Const<#interner>,
339+
outer_binder: ::chalk_ir::DebruijnIndex,
340+
) -> ::core::result::Result<::chalk_ir::Const<#interner>, Self::Error> {
341+
::core::result::Result::Ok(::chalk_ir::fold::TypeFolder::fold_const(self, constant, outer_binder))
342+
}
343+
344+
fn try_fold_program_clause(
345+
&mut self,
346+
clause: ::chalk_ir::ProgramClause<#interner>,
347+
outer_binder: ::chalk_ir::DebruijnIndex,
348+
) -> ::core::result::Result<::chalk_ir::ProgramClause<#interner>, Self::Error> {
349+
::core::result::Result::Ok(::chalk_ir::fold::TypeFolder::fold_program_clause(self, clause, outer_binder))
350+
}
351+
352+
fn try_fold_goal(
353+
&mut self,
354+
goal: ::chalk_ir::Goal<#interner>,
355+
outer_binder: ::chalk_ir::DebruijnIndex,
356+
) -> ::core::result::Result<::chalk_ir::Goal<#interner>, Self::Error> {
357+
::core::result::Result::Ok(::chalk_ir::fold::TypeFolder::fold_goal(self, goal, outer_binder))
358+
}
359+
360+
fn forbid_free_vars(&self) -> bool {
361+
::chalk_ir::fold::TypeFolder::forbid_free_vars(self)
362+
}
363+
364+
fn try_fold_free_var_ty(
365+
&mut self,
366+
bound_var: ::chalk_ir::BoundVar,
367+
outer_binder: ::chalk_ir::DebruijnIndex,
368+
) -> ::core::result::Result<::chalk_ir::Ty<#interner>, Self::Error> {
369+
::core::result::Result::Ok(::chalk_ir::fold::TypeFolder::fold_free_var_ty(self, bound_var, outer_binder))
370+
}
371+
372+
fn try_fold_free_var_lifetime(
373+
&mut self,
374+
bound_var: ::chalk_ir::BoundVar,
375+
outer_binder: ::chalk_ir::DebruijnIndex,
376+
) -> ::core::result::Result<::chalk_ir::Lifetime<#interner>, Self::Error> {
377+
::core::result::Result::Ok(::chalk_ir::fold::TypeFolder::fold_free_var_lifetime(self, bound_var, outer_binder))
378+
}
379+
380+
fn try_fold_free_var_const(
381+
&mut self,
382+
ty: ::chalk_ir::Ty<#interner>,
383+
bound_var: ::chalk_ir::BoundVar,
384+
outer_binder: ::chalk_ir::DebruijnIndex,
385+
) -> ::core::result::Result<::chalk_ir::Const<#interner>, Self::Error> {
386+
::core::result::Result::Ok(::chalk_ir::fold::TypeFolder::fold_free_var_const(self, ty, bound_var, outer_binder))
387+
}
388+
389+
fn forbid_free_placeholders(&self) -> bool {
390+
::chalk_ir::fold::TypeFolder::forbid_free_placeholders(self)
391+
}
392+
393+
fn try_fold_free_placeholder_ty(
394+
&mut self,
395+
universe: ::chalk_ir::PlaceholderIndex,
396+
outer_binder: ::chalk_ir::DebruijnIndex,
397+
) -> ::core::result::Result<::chalk_ir::Ty<#interner>, Self::Error> {
398+
::core::result::Result::Ok(::chalk_ir::fold::TypeFolder::fold_free_placeholder_ty(self, universe, outer_binder))
399+
}
400+
401+
fn try_fold_free_placeholder_lifetime(
402+
&mut self,
403+
universe: ::chalk_ir::PlaceholderIndex,
404+
outer_binder: ::chalk_ir::DebruijnIndex,
405+
) -> ::core::result::Result<::chalk_ir::Lifetime<#interner>, Self::Error> {
406+
::core::result::Result::Ok(::chalk_ir::fold::TypeFolder::fold_free_placeholder_lifetime(self, universe, outer_binder))
407+
}
408+
409+
fn try_fold_free_placeholder_const(
410+
&mut self,
411+
ty: ::chalk_ir::Ty<#interner>,
412+
universe: ::chalk_ir::PlaceholderIndex,
413+
outer_binder: ::chalk_ir::DebruijnIndex,
414+
) -> ::core::result::Result<::chalk_ir::Const<#interner>, Self::Error> {
415+
::core::result::Result::Ok(::chalk_ir::fold::TypeFolder::fold_free_placeholder_const(self, ty, universe, outer_binder))
416+
}
417+
418+
fn forbid_inference_vars(&self) -> bool {
419+
::chalk_ir::fold::TypeFolder::forbid_inference_vars(self)
420+
}
421+
422+
fn try_fold_inference_ty(
423+
&mut self,
424+
var: ::chalk_ir::InferenceVar,
425+
kind: ::chalk_ir::TyVariableKind,
426+
outer_binder: ::chalk_ir::DebruijnIndex,
427+
) -> ::core::result::Result<::chalk_ir::Ty<#interner>, Self::Error> {
428+
::core::result::Result::Ok(::chalk_ir::fold::TypeFolder::fold_inference_ty(self, var, kind, outer_binder))
429+
}
430+
431+
fn try_fold_inference_lifetime(
432+
&mut self,
433+
var: ::chalk_ir::InferenceVar,
434+
outer_binder: ::chalk_ir::DebruijnIndex,
435+
) -> ::core::result::Result<::chalk_ir::Lifetime<#interner>, Self::Error> {
436+
::core::result::Result::Ok(::chalk_ir::fold::TypeFolder::fold_inference_lifetime(self, var, outer_binder))
437+
}
438+
439+
fn try_fold_inference_const(
440+
&mut self,
441+
ty: ::chalk_ir::Ty<#interner>,
442+
var: ::chalk_ir::InferenceVar,
443+
outer_binder: ::chalk_ir::DebruijnIndex,
444+
) -> ::core::result::Result<::chalk_ir::Const<#interner>, Self::Error> {
445+
::core::result::Result::Ok(::chalk_ir::fold::TypeFolder::fold_inference_const(self, ty, var, outer_binder))
446+
}
447+
448+
fn interner(&self) -> #interner {
449+
::chalk_ir::fold::TypeFolder::interner(self)
450+
}
451+
},
452+
)
453+
}

0 commit comments

Comments
 (0)