1
- use formality_core:: { judgment_fn, set , Set } ;
1
+ use formality_core:: { judgment_fn, ProvenSet , Upcast } ;
2
2
use formality_types:: grammar:: {
3
- Lt , Parameter , RigidName , RigidTy , TraitRef , TyData , Variable , Wcs ,
3
+ AliasTy , BoundVar , Lt , Parameter , RigidName , RigidTy , TraitRef , TyData , Variable , Wcs ,
4
4
} ;
5
5
6
6
use crate :: {
@@ -37,24 +37,166 @@ use crate::{
37
37
// `Vec<Foo>` is not. `LocalType<ForeignType>` is local. Type aliases and trait
38
38
// aliases do not affect locality.
39
39
40
- /// True if `goal` may be remote. This is
41
- pub fn may_be_remote ( decls : Decls , env : Env , assumptions : Wcs , goal : TraitRef ) -> Set < Constraints > {
40
+ judgment_fn ! {
41
+ /// True if `goal` may be implemented in a crate that is not the current one.
42
+ /// This could be a downstream crate that we cannot see, or it could be a future
43
+ /// (semver-compatible) version of an upstream crate.
44
+ ///
45
+ /// Note that per RFC #2451, upstream crates are not permitted to add blanket impls
46
+ /// and so new upstream impls for local types cannot be added.
47
+ pub fn may_be_remote(
48
+ decls: Decls ,
49
+ env: Env ,
50
+ assumptions: Wcs ,
51
+ goal: TraitRef ,
52
+ ) => ( ) {
53
+ debug( assumptions, goal, decls, env)
54
+
55
+ (
56
+ ( may_be_downstream_trait_ref( decls, env, assumptions, goal) => ( ) )
57
+ --- ( "may be defined downstream" )
58
+ ( may_be_remote( decls, env, assumptions, goal) => ( ) )
59
+ )
60
+
61
+ (
62
+ // In principle this rule could be removed and preserve soundness,
63
+ // but then we would accept code that is very prone to semver failures.
64
+ ( may_not_be_provable( is_local_trait_ref( decls, env, assumptions, goal) ) => ( ) )
65
+ --- ( "may be added by upstream in a minor release" )
66
+ ( may_be_remote( decls, env, assumptions, goal) => ( ) )
67
+ )
68
+ }
69
+ }
70
+
71
+ judgment_fn ! {
72
+ /// True if an impl defining this trait-reference could appear in a downstream crate.
73
+ fn may_be_downstream_trait_ref(
74
+ decls: Decls ,
75
+ env: Env ,
76
+ assumptions: Wcs ,
77
+ goal: TraitRef ,
78
+ ) => ( ) {
79
+ debug( goal, assumptions, env, decls)
80
+
81
+ (
82
+ // There may be a downstream parameter at position i...
83
+ ( & goal. parameters => p)
84
+ ( may_be_downstream_parameter( & decls, & env, & assumptions, p) => ( ) )
85
+ --- ( "may_be_downstream_trait_ref" )
86
+ ( may_be_downstream_trait_ref( decls, env, assumptions, goal) => ( ) )
87
+ )
88
+ }
89
+ }
90
+
91
+ judgment_fn ! {
92
+ fn may_be_downstream_parameter(
93
+ decls: Decls ,
94
+ env: Env ,
95
+ assumptions: Wcs ,
96
+ parameter: Parameter ,
97
+ ) => ( ) {
98
+ debug( parameter, assumptions, env, decls)
99
+
100
+ (
101
+ // existential variables *could* be inferred to downstream types; depends on the substitution
102
+ // we ultimately have.
103
+ --- ( "type variable" )
104
+ ( may_be_downstream_parameter( _decls, _env, _assumptions, TyData :: Variable ( Variable :: ExistentialVar ( _) ) ) => ( ) )
105
+ )
106
+
107
+ // If we can normalize `goal` to something else, and that normalized
108
+ // form may be downstream.
109
+ (
110
+ // (a) there is some parameter in the alias that may be downstream
111
+ ( parameters. iter( ) => p)
112
+ ( if may_contain_downstream_type( & decls, & env, & assumptions, p) )
113
+
114
+ // (b) the alias cannot be normalized to something that may not be downstream
115
+ ( may_not_be_provable( normalizes_to_not_downstream( & decls, & env, & assumptions, AliasTy { name: name. clone( ) , parameters: parameters. clone( ) } ) ) => ( ) )
116
+ --- ( "via normalize" )
117
+ ( may_be_downstream_parameter( decls, env, assumptions, AliasTy { name, parameters } ) => ( ) )
118
+ )
119
+ }
120
+ }
121
+
122
+ fn may_contain_downstream_type (
123
+ decls : & Decls ,
124
+ env : & Env ,
125
+ assumptions : & Wcs ,
126
+ parameter : impl Upcast < Parameter > ,
127
+ ) -> bool {
42
128
assert ! ( env. is_in_coherence_mode( ) ) ;
129
+ let parameter = parameter. upcast ( ) ;
43
130
44
- let c = is_local_trait_ref ( decls, & env, assumptions, goal) ;
131
+ let Parameter :: Ty ( ty) = parameter else {
132
+ return false ;
133
+ } ;
45
134
46
- if !c. is_proven ( ) {
47
- // Cannot possibly be local, so always remote.
48
- return set ! [ Constraints :: none( env) ] ;
135
+ match ty. data ( ) {
136
+ TyData :: RigidTy ( RigidTy {
137
+ name : _,
138
+ parameters,
139
+ } ) => parameters
140
+ . iter ( )
141
+ . any ( |p| may_contain_downstream_type ( decls, env, assumptions, p) ) ,
142
+ TyData :: AliasTy ( _) => prove_normalize ( decls, env, assumptions, ty)
143
+ . iter ( )
144
+ . any ( |( c, p) | {
145
+ let assumptions = c. substitution ( ) . apply ( assumptions) ;
146
+ may_contain_downstream_type ( decls, env, & assumptions, p)
147
+ } ) ,
148
+ TyData :: PredicateTy ( p) => match p {
149
+ formality_types:: grammar:: PredicateTy :: ForAll ( binder) => {
150
+ let ( _, ty) = binder. open ( ) ;
151
+ may_contain_downstream_type ( decls, env, assumptions, ty)
152
+ }
153
+ } ,
154
+ TyData :: Variable ( v) => match v {
155
+ Variable :: ExistentialVar ( _) => true ,
156
+ Variable :: UniversalVar ( _) => panic ! ( "universals are unexpected" ) ,
157
+ Variable :: BoundVar ( BoundVar {
158
+ debruijn,
159
+ var_index : _,
160
+ kind : _,
161
+ } ) => {
162
+ assert ! ( debruijn. is_none( ) , "must have been opened on the way down" ) ;
163
+ true
164
+ }
165
+ } ,
49
166
}
167
+ }
50
168
51
- if c. iter ( ) . any ( Constraints :: unconditionally_true) {
52
- // If this is unconditionally known to be local, then it is never remote.
53
- return set ! [ ] ;
169
+ fn may_not_be_provable ( op : ProvenSet < Constraints > ) -> ProvenSet < ( ) > {
170
+ if let Some ( constraints) = op
171
+ . iter ( )
172
+ . find ( |constraints| constraints. unconditionally_true ( ) )
173
+ {
174
+ ProvenSet :: failed (
175
+ "may_not_be_provable" ,
176
+ format ! ( "found a solution {constraints:?}" ) ,
177
+ )
178
+ } else {
179
+ ProvenSet :: singleton ( ( ) )
54
180
}
181
+ }
55
182
56
- // Otherwise it is ambiguous
57
- set ! [ Constraints :: none( env) . ambiguous( ) ]
183
+ judgment_fn ! {
184
+ fn normalizes_to_not_downstream(
185
+ decls: Decls ,
186
+ env: Env ,
187
+ assumptions: Wcs ,
188
+ parameter: Parameter ,
189
+ ) => Constraints {
190
+ debug( parameter, assumptions, env, decls)
191
+
192
+ (
193
+ ( prove_normalize( & decls, & env, & assumptions, parameter) => ( c1, parameter) )
194
+ ( let assumptions = c1. substitution( ) . apply( & assumptions) )
195
+ ( is_not_downstream( & decls, & env, assumptions, parameter) => c2)
196
+ --- ( "ambiguous" )
197
+ ( normalizes_to_not_downstream( decls, env, assumptions, parameter) => c1. seq( c2) )
198
+ )
199
+ }
58
200
}
59
201
60
202
judgment_fn ! {
@@ -73,26 +215,29 @@ judgment_fn! {
73
215
)
74
216
75
217
(
218
+ // There is a local parameter at position i...
76
219
( 0 .. goal. parameters. len( ) => i)
77
220
( is_local_parameter( & decls, & env, & assumptions, & goal. parameters[ i] ) => c1)
221
+
222
+ // ...and in positions 0..i, there are no downstream parameters.
78
223
( let assumptions = c1. substitution( ) . apply( & assumptions) )
79
224
( let goal = c1. substitution( ) . apply( & goal) )
80
- ( for_all( & decls, & env, & assumptions, & goal. parameters[ ..i] , & not_downstream ) => c2)
225
+ ( for_all( & decls, & env, & assumptions, & goal. parameters[ ..i] , & is_not_downstream ) => c2)
81
226
--- ( "local parameter" )
82
227
( is_local_trait_ref( decls, env, assumptions, goal) => c1. seq( c2) )
83
228
)
84
229
}
85
230
}
86
231
87
232
judgment_fn ! {
88
- /// "not_downstream (..., P)" means that `P` cannot be instantiated with a type from
233
+ /// `is_not_downstream (..., P)` means that `P` cannot be instantiated with a type from
89
234
/// a downstream crate (i.e., a crate that has us as a dependency).
90
235
///
91
236
/// NB. Since RFC 2451, the judgment applies to the outermost type only. In other words,
92
237
/// the judgment holds for (e.g.) `Vec<T>`, which could be instantiated
93
238
/// with something like `Vec<DownstreamType>`, but that is not considered downstream
94
239
/// as the outermost type (`Vec`) is upstream.
95
- fn not_downstream (
240
+ fn is_not_downstream (
96
241
decls: Decls ,
97
242
env: Env ,
98
243
assumptions: Wcs ,
@@ -104,20 +249,20 @@ judgment_fn! {
104
249
// Since https://rust-lang.github.io/rfcs/2451-re-rebalancing-coherence.html,
105
250
// any rigid type is adequate.
106
251
--- ( "rigid" )
107
- ( not_downstream ( _decls, env, _assumptions, RigidTy { .. } ) => Constraints :: none( env) )
252
+ ( is_not_downstream ( _decls, env, _assumptions, RigidTy { .. } ) => Constraints :: none( env) )
108
253
)
109
254
110
255
(
111
256
// Lifetimes are not relevant.
112
257
--- ( "lifetime" )
113
- ( not_downstream ( _decls, env, _assumptions, _l: Lt ) => Constraints :: none( env) )
258
+ ( is_not_downstream ( _decls, env, _assumptions, _l: Lt ) => Constraints :: none( env) )
114
259
)
115
260
116
261
(
117
262
// existential variables *could* be inferred to downstream types; depends on the substitution
118
263
// we ultimately have.
119
264
--- ( "type variable" )
120
- ( not_downstream ( _decls, env, _assumptions, TyData :: Variable ( Variable :: ExistentialVar ( _) ) ) => Constraints :: none( env) . ambiguous( ) )
265
+ ( is_not_downstream ( _decls, env, _assumptions, TyData :: Variable ( Variable :: ExistentialVar ( _) ) ) => Constraints :: none( env) . ambiguous( ) )
121
266
)
122
267
}
123
268
}
0 commit comments