1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
use std::{
cell::Cell,
collections::{HashMap, HashSet},
};
use sway_error::{
error::CompileError,
handler::{ErrorEmitted, Handler},
};
use sway_types::Spanned;
use crate::{
engine_threading::{GetCallPathWithEngines, PartialEqWithEngines, PartialEqWithEnginesContext},
language::CallPath,
Engines, IncludeSelf, SubstTypes, SubstTypesContext, TypeId, TypeInfo, TypeParameter,
TypeSubstMap, UnifyCheck,
};
use super::{Package, TraitMap};
// Given an impl of the form `impl<P1..=Pn> Trait<T1..=Tn>` for `T0`, the impl is allowed if:
// 1. Trait is a local trait
// or
// 2. All of
// a) At least one of the types `T0..=Tn` must be a local type. Let Ti be the first such type.
// b) No uncovered type parameters `P1..=Pn` may appear in `T0..Ti` (excluding `Ti`)
// This is already checked elsewhere in the compiler so no need to check here.
pub(crate) fn check_orphan_rules_for_impls(
handler: &Handler,
engines: &Engines,
current_package: &Package,
) -> Result<(), ErrorEmitted> {
let mut error: Option<ErrorEmitted> = None;
let module = ¤t_package.root_module();
module.walk_scope_chain(|lexical_scope| {
let trait_map = &lexical_scope.items.implemented_traits;
if let Err(err) =
check_orphan_rules_for_impls_in_scope(handler, engines, current_package, trait_map)
{
error = Some(err);
}
});
match error {
Some(err) => Err(err),
None => Ok(()),
}
}
fn check_orphan_rules_for_impls_in_scope(
handler: &Handler,
engines: &Engines,
current_package: &Package,
trait_map: &TraitMap,
) -> Result<(), ErrorEmitted> {
for key in trait_map.trait_impls.keys() {
for trait_entry in trait_map.trait_impls[key].iter() {
// 0. If it's a contract then skip it as it's not relevant to coherence.
if engines
.te()
.get(trait_entry.inner.key.type_id)
.is_contract()
{
continue;
}
// 1. Check if trait is local to the current package
let package_name = trait_entry.inner.key.name.prefixes.first().unwrap();
let package_program_id = current_package.program_id();
let trait_impl_program_id = match trait_entry.inner.value.impl_span.source_id() {
Some(source_id) => source_id.program_id(),
None => {
return Err(handler.emit_err(CompileError::Internal(
"Expected a valid source id",
trait_entry.inner.value.impl_span.clone(),
)))
}
};
if package_program_id != trait_impl_program_id {
continue;
}
if package_name == current_package.name() {
continue;
}
fn references_local_type(
engines: &Engines,
current_package: &Package,
type_id: TypeId,
) -> bool {
// Create a flag to track if a local type was foundt.
let found_local = Cell::new(false);
type_id.walk_inner_types(
engines,
IncludeSelf::Yes,
&|inner_type_id| {
// If we've already flagged a local type, no need to do further work.
if found_local.get() {
return;
}
let inner_type = engines.te().get(*inner_type_id);
let is_local = match *inner_type {
TypeInfo::Enum(decl_id) => {
let enum_decl = engines.de().get_enum(&decl_id);
is_from_local_package(current_package, &enum_decl.call_path)
}
TypeInfo::Struct(decl_id) => {
let struct_decl = engines.de().get_struct(&decl_id);
is_from_local_package(current_package, &struct_decl.call_path)
}
// FIXME: We treat arrays as a special case for now due to lack of const generics.
TypeInfo::Array(_, _) => true,
TypeInfo::StringArray(_) => true,
_ => false,
};
// Mark the flag if a local type is found.
if is_local {
found_local.set(true);
}
},
&|trait_constraint| {
// If we've already flagged a local type, no need to do further work.
if found_local.get() {
return;
}
let is_local =
is_from_local_package(current_package, &trait_constraint.trait_name);
// Mark the flag if a local type is found.
if is_local {
found_local.set(true);
}
},
);
found_local.get()
}
// 2. Now the trait is necessarily upstream to the current package
let mut has_local_type = false;
for arg in &trait_entry.inner.key.name.suffix.args {
has_local_type |= references_local_type(engines, current_package, arg.type_id());
if has_local_type {
break;
}
}
'tp: for type_id in &trait_entry.inner.key.impl_type_parameters {
let tp = engines.te().get(*type_id);
match tp.as_ref() {
TypeInfo::TypeParam(tp) => match tp {
TypeParameter::Type(tp) => {
for tc in &tp.trait_constraints {
has_local_type |=
is_from_local_package(current_package, &tc.trait_name);
if has_local_type {
break 'tp;
}
}
}
TypeParameter::Const(_tp) => {}
},
_ => unreachable!(),
}
}
has_local_type |=
references_local_type(engines, current_package, trait_entry.inner.key.type_id);
if !has_local_type {
let trait_name = trait_entry.inner.key.name.suffix.name.to_string();
let type_name = {
let ty = engines.te().get(trait_entry.inner.key.type_id);
ty.get_type_str(engines)
};
handler.emit_err(CompileError::IncoherentImplDueToOrphanRule {
trait_name,
type_name,
span: trait_entry.inner.value.impl_span.clone(),
});
}
}
}
Ok(())
}
fn is_from_local_package(current_package: &Package, call_path: &CallPath) -> bool {
let package_name = call_path.prefixes.first().unwrap();
let is_external =
current_package
.external_packages
.iter()
.any(|(external_package_name, _root)| {
external_package_name.as_str() == package_name.as_str()
});
if is_external {
return false;
}
assert_eq!(current_package.name().as_str(), package_name.as_str());
true
}
/// Given [TraitMap]s `self` and `other`, checks for overlaps between `self` and `other`.
/// If no overlaps are found extends `self` with `other`.
pub(crate) fn check_impls_for_overlap(
trait_map: &mut TraitMap,
handler: &Handler,
other: TraitMap,
engines: &Engines,
) -> Result<(), ErrorEmitted> {
let mut overlap_err = None;
let unify_check = UnifyCheck::constraint_subset(engines);
let mut traits_types = HashMap::<CallPath, HashSet<TypeId>>::new();
trait_map.get_traits_types(&mut traits_types)?;
other.get_traits_types(&mut traits_types)?;
for key in trait_map.trait_impls.keys() {
for self_entry in trait_map.trait_impls[key].iter() {
let self_tcs: Vec<(CallPath, TypeId)> = self_entry
.inner
.key
.impl_type_parameters
.iter()
.flat_map(|type_id| {
let ti = engines.te().get(*type_id);
match ti.as_ref() {
TypeInfo::TypeParam(tp) => match tp {
TypeParameter::Type(tp) => tp
.trait_constraints
.iter()
.map(|tc| (tc.trait_name.clone(), tp.type_id))
.collect::<Vec<_>>(),
TypeParameter::Const(_tp) => vec![],
},
_ => unreachable!(),
}
})
.collect::<Vec<_>>();
let self_call_path = engines
.te()
.get(self_entry.inner.key.type_id)
.call_path(engines);
other.for_each_impls(engines, self_entry.inner.key.type_id, true, |other_entry| {
let other_call_path = engines
.te()
.get(other_entry.inner.key.type_id)
.call_path(engines);
// This prevents us from checking duplicated types as might happen when
// compiling different versions of the same library.
let is_duplicated_type = matches!(
(&self_call_path, &other_call_path),
(Some(v1), Some(v2))
if v1.prefixes == v2.prefixes
&& v1.span().source_id() != v2.span().source_id()
);
if self_entry.inner.key.name.eq(
&*other_entry.inner.key.name,
&PartialEqWithEnginesContext::new(engines),
) && self_entry.inner.value.impl_span != other_entry.inner.value.impl_span
&& !is_duplicated_type
&& (unify_check
.check(self_entry.inner.key.type_id, other_entry.inner.key.type_id)
|| unify_check
.check(other_entry.inner.key.type_id, self_entry.inner.key.type_id))
{
let other_tcs: Vec<(CallPath, TypeId)> = other_entry
.inner
.key
.impl_type_parameters
.iter()
.flat_map(|type_id| {
let ti = engines.te().get(*type_id);
match ti.as_ref() {
TypeInfo::TypeParam(tp) => match tp {
TypeParameter::Type(tp) => tp
.trait_constraints
.iter()
.map(|tc| (tc.trait_name.clone(), tp.type_id))
.collect::<Vec<_>>(),
TypeParameter::Const(_tp) => vec![],
},
_ => unreachable!(),
}
})
.collect::<Vec<_>>();
let other_tcs_satisfied = other_tcs.iter().all(|(trait_name, tp_type_id)| {
if let Some(tc_type_ids) = traits_types.get(trait_name) {
tc_type_ids.iter().any(|tc_type_id| {
let mut type_mapping = TypeSubstMap::new();
type_mapping.insert(*tp_type_id, *tc_type_id);
let mut type_id = other_entry.inner.key.type_id;
type_id.subst(&SubstTypesContext::new(
handler,
engines,
&type_mapping,
false,
));
unify_check.check(self_entry.inner.key.type_id, type_id)
})
} else {
false
}
});
let self_tcs_satisfied = self_tcs.iter().all(|(trait_name, tp_type_id)| {
if let Some(tc_type_ids) = traits_types.get(trait_name) {
tc_type_ids.iter().any(|tc_type_id| {
let mut type_mapping = TypeSubstMap::new();
type_mapping.insert(*tp_type_id, *tc_type_id);
let mut type_id = self_entry.inner.key.type_id;
type_id.subst(&SubstTypesContext::new(
handler,
engines,
&type_mapping,
false,
));
unify_check.check(other_entry.inner.key.type_id, type_id)
})
} else {
false
}
});
if other_tcs_satisfied && self_tcs_satisfied {
for trait_item_name1 in self_entry.inner.value.trait_items.keys() {
for trait_item_name2 in other_entry.inner.value.trait_items.keys() {
if trait_item_name1 == trait_item_name2 {
overlap_err = Some(
handler.emit_err(
CompileError::ConflictingImplsForTraitAndType {
trait_name: engines
.help_out(self_entry.inner.key.name.as_ref())
.to_string(),
type_implementing_for: engines
.help_out(self_entry.inner.key.type_id)
.to_string(),
type_implementing_for_unaliased: engines
.help_out(self_entry.inner.key.type_id)
.to_string(),
existing_impl_span: self_entry
.inner
.value
.impl_span
.clone(),
second_impl_span: other_entry
.inner
.value
.impl_span
.clone(),
},
),
);
}
}
}
}
}
});
}
}
if let Some(overlap_err) = overlap_err {
return Err(overlap_err);
}
trait_map.extend(other, engines);
Ok(())
}