Skip to content

Commit 1bdc404

Browse files
committed
Improve the error message when an annotation is both inherited and
explicitly declared. This is mostly done, but still a bit of a WIP: 1. The todo!() is waiting on the merge of #128 2. This all got quite verbose, and should become its own function to improve readability 3. The test is written, but not added in lib.rs
1 parent f9ecf1f commit 1bdc404

File tree

3 files changed

+87
-2
lines changed

3 files changed

+87
-2
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
virtual resource tmp {}
2+
3+
@associate([tmp])
4+
virtual domain bar {}
5+
6+
@associate([tmp])
7+
domain foo inherits bar {
8+
allow(this, this.tmp, file, write);
9+
}
10+

src/compile.rs

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,12 +1252,70 @@ fn create_synthetic_resource(
12521252
.expressions
12531253
// If dup_res_decl is concrete, do not inherit virtual functions
12541254
.retain(|e| dup_res_is_virtual || !e.is_virtual_function());
1255-
if !global_exprs.insert(Expression::Decl(Declaration::Type(Box::new(dup_res_decl)))) {
1256-
return Err(InternalError::new().into());
1255+
if !global_exprs.insert(Expression::Decl(Declaration::Type(Box::new(
1256+
dup_res_decl.clone(),
1257+
)))) {
1258+
// The callers should be handling the situation where the same resource was declared at the
1259+
// same level of inheritance, but this can arise if a parent associated a resource and a
1260+
// child associated the same resource. We should find them and return and error message
1261+
return match make_duplicate_associate_error(types, dom_info, &res_name) {
1262+
Some(e) => Err(e.into()),
1263+
None => Err(InternalError::new().into()),
1264+
};
12571265
}
12581266
Ok(res_name)
12591267
}
12601268

1269+
fn make_duplicate_associate_error(
1270+
types: &TypeMap,
1271+
child_domain: &TypeInfo,
1272+
res_name: &CascadeString,
1273+
) -> Option<CompileError> {
1274+
let mut parent_ti = None;
1275+
let mut parent_associate_range = None;
1276+
for p in child_domain.get_all_parent_names(types) {
1277+
if let Some(p_ti) = types.get(p.as_ref()) {
1278+
parent_associate_range = p_ti.explicitly_associates(res_name.as_ref());
1279+
if parent_associate_range.is_some() {
1280+
parent_ti = Some(p_ti);
1281+
break;
1282+
}
1283+
}
1284+
}
1285+
let current_associate_range = match child_domain.explicitly_associates(res_name.as_ref()) {
1286+
Some(r) => r,
1287+
None => {
1288+
// This is an association via nested declaration, so find that
1289+
child_domain.decl.as_ref()?.expressions.iter().find_map(|e|
1290+
if let Expression::Decl(Declaration::Type(td)) = e {
1291+
if &td.name == res_name {
1292+
td.name.get_range()
1293+
} else {
1294+
None
1295+
}
1296+
} else {
1297+
None
1298+
})?
1299+
}
1300+
};
1301+
1302+
// If anything we need for a real error is None, all we can do is InternalError, so unwrap
1303+
// everything, returning InternalError on failure
1304+
let child_file = child_domain.declaration_file.as_ref()?;
1305+
let parent_file = parent_ti?.declaration_file.as_ref()?;
1306+
let parent_associate_range = parent_associate_range?;
1307+
1308+
Some(CompileError::new(
1309+
"This resource is explicitly associated to both the parent and child. (Perhaps you meant to extend the existing resource in the child?)",
1310+
child_file,
1311+
current_associate_range,
1312+
"Associated in the child here")
1313+
.add_additional_message(
1314+
parent_file,
1315+
parent_associate_range,
1316+
"But it was already associated in this parent"))
1317+
}
1318+
12611319
fn interpret_associate(
12621320
global_exprs: &mut HashSet<Expression>,
12631321
local_exprs: &mut HashSet<Expression>,

src/internal_rep.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,23 @@ impl TypeInfo {
312312
}
313313
ret
314314
}
315+
316+
// If this TI associates a type named associate_name, return its range
317+
// Note that if the association is synthetic, the range will be None, so this finds
318+
// specifically associations specifically in source, rather than somehow derived by the
319+
// compiler
320+
pub fn explicitly_associates(&self, associate_name: &str) -> Option<Range<usize>> {
321+
for ann in &self.annotations {
322+
if let AnnotationInfo::Associate(associations) = ann {
323+
for res in &associations.resources {
324+
if res.as_ref() == associate_name && res.get_range().is_some() {
325+
return res.get_range();
326+
}
327+
}
328+
}
329+
}
330+
None
331+
}
315332
}
316333

317334
// This is the sexp for *declaring* the type

0 commit comments

Comments
 (0)