aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/language/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/language/src/main')
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/Problem.xtext15
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java10
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java49
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/typesystem/TypedModule.java12
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/utils/BuiltinSymbols.java6
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java26
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/validation/ExistsVariableCollector.java75
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java135
-rw-r--r--subprojects/language/src/main/resources/tools/refinery/language/library/builtin.refinery2
9 files changed, 159 insertions, 171 deletions
diff --git a/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext b/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext
index 10e994a0..7e3b3c83 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext
+++ b/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext
@@ -58,6 +58,7 @@ ReferenceMultiplicity returns Multiplicity:
58 "[" Multiplicity "]"; 58 "[" Multiplicity "]";
59 59
60PredicateDefinition: 60PredicateDefinition:
61 shadow?="shadow"?
61 ("pred" | error?="error" "pred"?) 62 ("pred" | error?="error" "pred"?)
62 name=Identifier 63 name=Identifier
63 "(" (parameters+=Parameter ("," parameters+=Parameter)*)? ")" 64 "(" (parameters+=Parameter ("," parameters+=Parameter)*)? ")"
@@ -88,13 +89,10 @@ RuleDefinition:
88 "."; 89 ".";
89 90
90enum ParameterBinding: 91enum ParameterBinding:
91 FOCUS="&" | MULTI="*"; 92 FOCUS="+" | MULTI="?";
92 93
93Parameter: 94Parameter:
94 ( 95 parameterType=[Relation|QualifiedName]? binding=ParameterBinding? name=Identifier;
95 (concreteness=Concreteness? modality=Modality)?
96 parameterType=[Relation|QualifiedName]
97 )? binding=ParameterBinding? name=Identifier;
98 96
99Consequent: 97Consequent:
100 actions+=Action ("," actions+=Action)*; 98 actions+=Action ("," actions+=Action)*;
@@ -183,13 +181,14 @@ AggregationExpr:
183 "{" value=Expr "|" condition=ComparisonExpr "}"; 181 "{" value=Expr "|" condition=ComparisonExpr "}";
184 182
185enum Concreteness: 183enum Concreteness:
186 CANDIDATE="candidate"; 184 PARTIAL="partial" | CANDIDATE="candidate";
187 185
188enum Modality: 186enum Modality:
189 MUST="must" | MAY="may"; 187 MUST="must" | MAY="may";
190 188
191ModalExpr: 189ModalExpr:
192 concreteness=Concreteness? modality=Modality body=UnaryExpr; 190 (concreteness=Concreteness => modality=Modality? | modality=Modality => concreteness=Concreteness?)
191 body=UnaryExpr;
193 192
194CastExpr returns Expr: 193CastExpr returns Expr:
195 CastExprBody ({CastExpr.body=current} "as" targetType=[Relation|QualifiedName])?; 194 CastExprBody ({CastExpr.body=current} "as" targetType=[Relation|QualifiedName])?;
@@ -295,7 +294,7 @@ Identifier:
295 294
296NonContainmentIdentifier: 295NonContainmentIdentifier:
297 ID | "atom" | "multi" | "contained" | "problem" | "module" | 296 ID | "atom" | "multi" | "contained" | "problem" | "module" |
298 "datatype" | "aggregator" | "decision" | "propagation"; 297 "datatype" | "aggregator" | "decision" | "propagation" | "computed";
299 298
300Real returns ecore::EDouble: 299Real returns ecore::EDouble:
301 EXPONENTIAL | INT "." (INT | EXPONENTIAL); 300 EXPONENTIAL | INT "." (INT | EXPONENTIAL);
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java
index 505c7787..8ebd34a5 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java
@@ -46,6 +46,8 @@ public class ProblemResourceDescriptionStrategy extends DefaultResourceDescripti
46 public static final String MODULE_KIND = DATA_PREFIX + "MODULE_KIND"; 46 public static final String MODULE_KIND = DATA_PREFIX + "MODULE_KIND";
47 public static final String COLOR_RELATION = DATA_PREFIX + "COLOR_RELATION"; 47 public static final String COLOR_RELATION = DATA_PREFIX + "COLOR_RELATION";
48 public static final String COLOR_RELATION_TRUE = "true"; 48 public static final String COLOR_RELATION_TRUE = "true";
49 public static final String SHADOW_PREDICATE = DATA_PREFIX + "COMPUTED_VALUE";
50 public static final String SHADOW_PREDICATE_TRUE = "true";
49 51
50 @Inject 52 @Inject
51 private IQualifiedNameConverter qualifiedNameConverter; 53 private IQualifiedNameConverter qualifiedNameConverter;
@@ -160,9 +162,12 @@ public class ProblemResourceDescriptionStrategy extends DefaultResourceDescripti
160 } else if (eObject instanceof AggregatorDeclaration) { 162 } else if (eObject instanceof AggregatorDeclaration) {
161 builder.put(SHADOWING_KEY, SHADOWING_KEY_AGGREGATOR); 163 builder.put(SHADOWING_KEY, SHADOWING_KEY_AGGREGATOR);
162 } 164 }
163 if (eObject instanceof PredicateDefinition predicateDefinition && predicateDefinition.isError()) { 165 if (ProblemUtil.isError(eObject)) {
164 builder.put(ERROR_PREDICATE, ERROR_PREDICATE_TRUE); 166 builder.put(ERROR_PREDICATE, ERROR_PREDICATE_TRUE);
165 } 167 }
168 if (ProblemUtil.isShadow(eObject)) {
169 builder.put(SHADOW_PREDICATE, SHADOW_PREDICATE_TRUE);
170 }
166 var documentationMap = documentationCommentParser.parseDocumentation(eObject); 171 var documentationMap = documentationCommentParser.parseDocumentation(eObject);
167 builder.putAll(documentationMap); 172 builder.putAll(documentationMap);
168 return builder.build(); 173 return builder.build();
@@ -173,7 +178,8 @@ public class ProblemResourceDescriptionStrategy extends DefaultResourceDescripti
173 return !ProblemUtil.isMultiNode(node); 178 return !ProblemUtil.isMultiNode(node);
174 } 179 }
175 if (eObject instanceof PredicateDefinition predicateDefinition) { 180 if (eObject instanceof PredicateDefinition predicateDefinition) {
176 return !ProblemUtil.isInvalidMultiplicityConstraint(predicateDefinition); 181 return !ProblemUtil.isInvalidMultiplicityConstraint(predicateDefinition) &&
182 !ProblemUtil.isComputedValuePredicate(predicateDefinition);
177 } 183 }
178 return true; 184 return true;
179 } 185 }
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java b/subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java
index efa77c50..44f55563 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java
@@ -22,6 +22,7 @@ import tools.refinery.language.utils.ProblemUtil;
22 22
23import java.util.*; 23import java.util.*;
24import java.util.function.Function; 24import java.util.function.Function;
25import java.util.function.UnaryOperator;
25 26
26@Singleton 27@Singleton
27public class ProblemDerivedStateComputer implements IDerivedStateComputer { 28public class ProblemDerivedStateComputer implements IDerivedStateComputer {
@@ -59,7 +60,7 @@ public class ProblemDerivedStateComputer implements IDerivedStateComputer {
59 } 60 }
60 61
61 protected void installDerivedProblemState(Problem problem, Adapter adapter, boolean preLinkingPhase) { 62 protected void installDerivedProblemState(Problem problem, Adapter adapter, boolean preLinkingPhase) {
62 installDerivedClassDeclarationState(problem, adapter); 63 installDerivedDeclarationState(problem, adapter);
63 if (preLinkingPhase) { 64 if (preLinkingPhase) {
64 return; 65 return;
65 } 66 }
@@ -67,13 +68,15 @@ public class ProblemDerivedStateComputer implements IDerivedStateComputer {
67 derivedVariableComputer.installDerivedVariables(problem, nodeNames); 68 derivedVariableComputer.installDerivedVariables(problem, nodeNames);
68 } 69 }
69 70
70 protected void installDerivedClassDeclarationState(Problem problem, Adapter adapter) { 71 protected void installDerivedDeclarationState(Problem problem, Adapter adapter) {
71 for (var statement : problem.getStatements()) { 72 for (var statement : problem.getStatements()) {
72 if (statement instanceof ClassDeclaration classDeclaration) { 73 if (statement instanceof ClassDeclaration classDeclaration) {
73 installOrRemoveNewNode(adapter, classDeclaration); 74 installOrRemoveNewNode(adapter, classDeclaration);
74 for (var referenceDeclaration : classDeclaration.getFeatureDeclarations()) { 75 for (var referenceDeclaration : classDeclaration.getFeatureDeclarations()) {
75 installOrRemoveInvalidMultiplicityPredicate(adapter, classDeclaration, referenceDeclaration); 76 installOrRemoveInvalidMultiplicityPredicate(adapter, classDeclaration, referenceDeclaration);
76 } 77 }
78 } else if (statement instanceof PredicateDefinition predicateDefinition) {
79 installOrRemoveComputedValuePredicate(adapter, predicateDefinition);
77 } 80 }
78 } 81 }
79 } 82 }
@@ -118,6 +121,27 @@ public class ProblemDerivedStateComputer implements IDerivedStateComputer {
118 } 121 }
119 } 122 }
120 123
124 protected void installOrRemoveComputedValuePredicate(Adapter adapter, PredicateDefinition predicateDefinition) {
125 if (ProblemUtil.hasComputedValue(predicateDefinition)) {
126 var computedValue = adapter.createComputedValuePredicateIfAbsent(predicateDefinition, key -> {
127 var predicate = ProblemFactory.eINSTANCE.createPredicateDefinition();
128 predicate.setShadow(true);
129 predicate.setName("computed");
130 return predicate;
131 });
132 var parameters = computedValue.getParameters();
133 parameters.clear();
134 parameters.addAll(EcoreUtil.copyAll(predicateDefinition.getParameters()));
135 predicateDefinition.setComputedValue(computedValue);
136 } else {
137 var computedValue = predicateDefinition.getComputedValue();
138 if (computedValue != null) {
139 predicateDefinition.setComputedValue(null);
140 adapter.removeComputedValuePredicate(computedValue);
141 }
142 }
143 }
144
121 protected Set<String> installDerivedNodes(Problem problem) { 145 protected Set<String> installDerivedNodes(Problem problem) {
122 var collector = nodeNameCollectorProvider.get(); 146 var collector = nodeNameCollectorProvider.get();
123 collector.collectNodeNames(problem); 147 collector.collectNodeNames(problem);
@@ -148,6 +172,7 @@ public class ProblemDerivedStateComputer implements IDerivedStateComputer {
148 protected void discardDerivedProblemState(Problem problem, Adapter adapter) { 172 protected void discardDerivedProblemState(Problem problem, Adapter adapter) {
149 var abstractClassDeclarations = new HashSet<ClassDeclaration>(); 173 var abstractClassDeclarations = new HashSet<ClassDeclaration>();
150 var referenceDeclarationsWithMultiplicity = new HashSet<ReferenceDeclaration>(); 174 var referenceDeclarationsWithMultiplicity = new HashSet<ReferenceDeclaration>();
175 var predicateDefinitionsWithComputedValue = new HashSet<PredicateDefinition>();
151 problem.getNodes().clear(); 176 problem.getNodes().clear();
152 for (var statement : problem.getStatements()) { 177 for (var statement : problem.getStatements()) {
153 if (statement instanceof ClassDeclaration classDeclaration) { 178 if (statement instanceof ClassDeclaration classDeclaration) {
@@ -160,9 +185,13 @@ public class ProblemDerivedStateComputer implements IDerivedStateComputer {
160 referenceDeclarationsWithMultiplicity.add(referenceDeclaration); 185 referenceDeclarationsWithMultiplicity.add(referenceDeclaration);
161 } 186 }
162 } 187 }
188 } else if (statement instanceof PredicateDefinition predicateDefinition &&
189 ProblemUtil.hasComputedValue(predicateDefinition)) {
190 predicateDefinitionsWithComputedValue.add(predicateDefinition);
163 } 191 }
164 } 192 }
165 adapter.retainAll(abstractClassDeclarations, referenceDeclarationsWithMultiplicity); 193 adapter.retainAll(abstractClassDeclarations, referenceDeclarationsWithMultiplicity,
194 predicateDefinitionsWithComputedValue);
166 derivedVariableComputer.discardDerivedVariables(problem); 195 derivedVariableComputer.discardDerivedVariables(problem);
167 } 196 }
168 197
@@ -185,6 +214,7 @@ public class ProblemDerivedStateComputer implements IDerivedStateComputer {
185 protected static class Adapter extends AdapterImpl { 214 protected static class Adapter extends AdapterImpl {
186 private final Map<ClassDeclaration, Node> newNodes = new HashMap<>(); 215 private final Map<ClassDeclaration, Node> newNodes = new HashMap<>();
187 private final Map<ReferenceDeclaration, PredicateDefinition> invalidMultiplicityPredicates = new HashMap<>(); 216 private final Map<ReferenceDeclaration, PredicateDefinition> invalidMultiplicityPredicates = new HashMap<>();
217 private final Map<PredicateDefinition, PredicateDefinition> computedValuePredicates = new HashMap<>();
188 218
189 public Node createNewNodeIfAbsent(ClassDeclaration classDeclaration, 219 public Node createNewNodeIfAbsent(ClassDeclaration classDeclaration,
190 Function<ClassDeclaration, Node> createNode) { 220 Function<ClassDeclaration, Node> createNode) {
@@ -205,10 +235,21 @@ public class ProblemDerivedStateComputer implements IDerivedStateComputer {
205 invalidMultiplicityPredicates.remove(referenceDeclaration); 235 invalidMultiplicityPredicates.remove(referenceDeclaration);
206 } 236 }
207 237
238 public PredicateDefinition createComputedValuePredicateIfAbsent(
239 PredicateDefinition predicateDefinition, UnaryOperator<PredicateDefinition> createPredicate) {
240 return computedValuePredicates.computeIfAbsent(predicateDefinition, createPredicate);
241 }
242
243 public void removeComputedValuePredicate(PredicateDefinition predicateDefinition) {
244 computedValuePredicates.remove(predicateDefinition);
245 }
246
208 public void retainAll(Collection<ClassDeclaration> abstractClassDeclarations, 247 public void retainAll(Collection<ClassDeclaration> abstractClassDeclarations,
209 Collection<ReferenceDeclaration> referenceDeclarationsWithMultiplicity) { 248 Collection<ReferenceDeclaration> referenceDeclarationsWithMultiplicity,
249 Collection<PredicateDefinition> predicateDefinitionsWithComputedValue) {
210 newNodes.keySet().retainAll(abstractClassDeclarations); 250 newNodes.keySet().retainAll(abstractClassDeclarations);
211 invalidMultiplicityPredicates.keySet().retainAll(referenceDeclarationsWithMultiplicity); 251 invalidMultiplicityPredicates.keySet().retainAll(referenceDeclarationsWithMultiplicity);
252 computedValuePredicates.keySet().retainAll(predicateDefinitionsWithComputedValue);
212 } 253 }
213 254
214 @Override 255 @Override
diff --git a/subprojects/language/src/main/java/tools/refinery/language/typesystem/TypedModule.java b/subprojects/language/src/main/java/tools/refinery/language/typesystem/TypedModule.java
index 63815506..f1e5a7ac 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/typesystem/TypedModule.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/typesystem/TypedModule.java
@@ -75,7 +75,7 @@ public class TypedModule {
75 private void checkTypes(PredicateDefinition predicateDefinition) { 75 private void checkTypes(PredicateDefinition predicateDefinition) {
76 for (var conjunction : predicateDefinition.getBodies()) { 76 for (var conjunction : predicateDefinition.getBodies()) {
77 for (var literal : conjunction.getLiterals()) { 77 for (var literal : conjunction.getLiterals()) {
78 coerceIntoLiteral(literal); 78 coerceIntoLiteral(literal, predicateDefinition.isShadow());
79 } 79 }
80 } 80 }
81 } 81 }
@@ -83,7 +83,7 @@ public class TypedModule {
83 private void checkTypes(RuleDefinition ruleDefinition) { 83 private void checkTypes(RuleDefinition ruleDefinition) {
84 for (var conjunction : ruleDefinition.getPreconditions()) { 84 for (var conjunction : ruleDefinition.getPreconditions()) {
85 for (var literal : conjunction.getLiterals()) { 85 for (var literal : conjunction.getLiterals()) {
86 expectType(literal, ExprType.MODAL_LITERAL); 86 coerceIntoLiteral(literal, true);
87 } 87 }
88 } 88 }
89 for (var consequent : ruleDefinition.getConsequents()) { 89 for (var consequent : ruleDefinition.getConsequents()) {
@@ -393,7 +393,7 @@ public class TypedModule {
393 393
394 @NotNull 394 @NotNull
395 private ExprType computeExpressionType(CountExpr countExpr) { 395 private ExprType computeExpressionType(CountExpr countExpr) {
396 return coerceIntoLiteral(countExpr.getBody()) ? BuiltinTermInterpreter.INT_TYPE : ExprType.INVALID; 396 return coerceIntoLiteral(countExpr.getBody(), false) ? BuiltinTermInterpreter.INT_TYPE : ExprType.INVALID;
397 } 397 }
398 398
399 @NotNull 399 @NotNull
@@ -403,7 +403,7 @@ public class TypedModule {
403 return ExprType.INVALID; 403 return ExprType.INVALID;
404 } 404 }
405 // Avoid short-circuiting to let us type check both the value and the condition. 405 // Avoid short-circuiting to let us type check both the value and the condition.
406 boolean ok = coerceIntoLiteral(expr.getCondition()); 406 boolean ok = coerceIntoLiteral(expr.getCondition(), false);
407 var value = expr.getValue(); 407 var value = expr.getValue();
408 var actualType = getExpressionType(value); 408 var actualType = getExpressionType(value);
409 if (actualType == ExprType.INVALID) { 409 if (actualType == ExprType.INVALID) {
@@ -617,12 +617,12 @@ public class TypedModule {
617 } 617 }
618 } 618 }
619 619
620 private boolean coerceIntoLiteral(Expr expr) { 620 private boolean coerceIntoLiteral(Expr expr, boolean allowModal) {
621 if (expr == null) { 621 if (expr == null) {
622 return false; 622 return false;
623 } 623 }
624 var actualType = getExpressionType(expr); 624 var actualType = getExpressionType(expr);
625 if (actualType == ExprType.LITERAL) { 625 if (actualType == ExprType.LITERAL || (allowModal && actualType == ExprType.MODAL_LITERAL)) {
626 return true; 626 return true;
627 } 627 }
628 return expectType(expr, actualType, BuiltinTermInterpreter.BOOLEAN_TYPE); 628 return expectType(expr, actualType, BuiltinTermInterpreter.BOOLEAN_TYPE);
diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/BuiltinSymbols.java b/subprojects/language/src/main/java/tools/refinery/language/utils/BuiltinSymbols.java
index 72f23e85..3b2ae707 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/utils/BuiltinSymbols.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/utils/BuiltinSymbols.java
@@ -12,6 +12,7 @@ public final class BuiltinSymbols {
12 private final ClassDeclaration node; 12 private final ClassDeclaration node;
13 private final PredicateDefinition equals; 13 private final PredicateDefinition equals;
14 private final PredicateDefinition exists; 14 private final PredicateDefinition exists;
15 private final ClassDeclaration container;
15 private final ClassDeclaration contained; 16 private final ClassDeclaration contained;
16 private final PredicateDefinition contains; 17 private final PredicateDefinition contains;
17 private final PredicateDefinition invalidContainer; 18 private final PredicateDefinition invalidContainer;
@@ -21,6 +22,7 @@ public final class BuiltinSymbols {
21 node = getDeclaration(ClassDeclaration.class, "node"); 22 node = getDeclaration(ClassDeclaration.class, "node");
22 equals = getDeclaration(PredicateDefinition.class, "equals"); 23 equals = getDeclaration(PredicateDefinition.class, "equals");
23 exists = getDeclaration(PredicateDefinition.class, "exists"); 24 exists = getDeclaration(PredicateDefinition.class, "exists");
25 container = getDeclaration(ClassDeclaration.class, "container");
24 contained = getDeclaration(ClassDeclaration.class, "contained"); 26 contained = getDeclaration(ClassDeclaration.class, "contained");
25 contains = getDeclaration(PredicateDefinition.class, "contains"); 27 contains = getDeclaration(PredicateDefinition.class, "contains");
26 invalidContainer = getDeclaration(PredicateDefinition.class, "invalidContainer"); 28 invalidContainer = getDeclaration(PredicateDefinition.class, "invalidContainer");
@@ -42,6 +44,10 @@ public final class BuiltinSymbols {
42 return exists; 44 return exists;
43 } 45 }
44 46
47 public ClassDeclaration container() {
48 return container;
49 }
50
45 public ClassDeclaration contained() { 51 public ClassDeclaration contained() {
46 return contained; 52 return contained;
47 } 53 }
diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java
index e1820261..067e684a 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java
@@ -53,6 +53,19 @@ public final class ProblemUtil {
53 return eObject instanceof PredicateDefinition predicateDefinition && predicateDefinition.isError(); 53 return eObject instanceof PredicateDefinition predicateDefinition && predicateDefinition.isError();
54 } 54 }
55 55
56 public static boolean isShadow(EObject eObject) {
57 return eObject instanceof PredicateDefinition predicateDefinition && predicateDefinition.isShadow();
58 }
59
60 public static boolean mayReferToShadow(EObject context) {
61 var definitionContext = EcoreUtil2.getContainerOfType(context, ParametricDefinition.class);
62 return switch (definitionContext) {
63 case PredicateDefinition predicateDefinition -> predicateDefinition.isShadow();
64 case RuleDefinition ignored -> true;
65 case null, default -> false;
66 };
67 }
68
56 public static boolean isAtomNode(Node node) { 69 public static boolean isAtomNode(Node node) {
57 var containingFeature = node.eContainingFeature(); 70 var containingFeature = node.eContainingFeature();
58 if (containingFeature == ProblemPackage.Literals.NODE_DECLARATION__NODES) { 71 if (containingFeature == ProblemPackage.Literals.NODE_DECLARATION__NODES) {
@@ -73,9 +86,12 @@ public final class ProblemUtil {
73 return node.eContainingFeature() == ProblemPackage.Literals.NODE_DECLARATION__NODES; 86 return node.eContainingFeature() == ProblemPackage.Literals.NODE_DECLARATION__NODES;
74 } 87 }
75 88
76 public static boolean isInvalidMultiplicityConstraint(PredicateDefinition predicateDefinition) { 89 public static boolean isInvalidMultiplicityConstraint(Relation relation) {
77 return predicateDefinition.eContainingFeature() == 90 return relation.eContainingFeature() == ProblemPackage.Literals.REFERENCE_DECLARATION__INVALID_MULTIPLICITY;
78 ProblemPackage.Literals.REFERENCE_DECLARATION__INVALID_MULTIPLICITY; 91 }
92
93 public static boolean isComputedValuePredicate(Relation relation) {
94 return relation.eContainingFeature() == ProblemPackage.Literals.PREDICATE_DEFINITION__COMPUTED_VALUE;
79 } 95 }
80 96
81 public static boolean hasMultiplicityConstraint(ReferenceDeclaration referenceDeclaration) { 97 public static boolean hasMultiplicityConstraint(ReferenceDeclaration referenceDeclaration) {
@@ -96,6 +112,10 @@ public final class ProblemUtil {
96 return true; 112 return true;
97 } 113 }
98 114
115 public static boolean hasComputedValue(PredicateDefinition predicateDefinition) {
116 return !predicateDefinition.isShadow() && !predicateDefinition.getBodies().isEmpty();
117 }
118
99 public static boolean isTypeLike(Relation relation) { 119 public static boolean isTypeLike(Relation relation) {
100 if (relation instanceof ClassDeclaration || relation instanceof EnumDeclaration || 120 if (relation instanceof ClassDeclaration || relation instanceof EnumDeclaration ||
101 relation instanceof DatatypeDeclaration) { 121 relation instanceof DatatypeDeclaration) {
diff --git a/subprojects/language/src/main/java/tools/refinery/language/validation/ExistsVariableCollector.java b/subprojects/language/src/main/java/tools/refinery/language/validation/ExistsVariableCollector.java
deleted file mode 100644
index 5812bdc4..00000000
--- a/subprojects/language/src/main/java/tools/refinery/language/validation/ExistsVariableCollector.java
+++ /dev/null
@@ -1,75 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.validation;
7
8import com.google.inject.Inject;
9import com.google.inject.Singleton;
10import org.eclipse.xtext.EcoreUtil2;
11import org.eclipse.xtext.util.IResourceScopeCache;
12import org.eclipse.xtext.util.Tuples;
13import tools.refinery.language.model.problem.*;
14import tools.refinery.language.scoping.imports.ImportAdapterProvider;
15import tools.refinery.language.utils.ProblemUtil;
16
17import java.util.HashSet;
18import java.util.Set;
19
20@Singleton
21public class ExistsVariableCollector {
22 private static final String EXISTS_VARIABLES =
23 "tools.refinery.language.validation.ExistsVariableCollector.EXISTS_VARIABLES";
24
25 @Inject
26 private IResourceScopeCache cache = IResourceScopeCache.NullImpl.INSTANCE;
27
28 @Inject
29 private ImportAdapterProvider importAdapterProvider;
30
31 public boolean missingExistsConstraint(Variable variable) {
32 if (variable instanceof Parameter) {
33 return false;
34 }
35 if (ProblemUtil.isImplicitVariable(variable)) {
36 var negation = EcoreUtil2.getContainerOfType(variable, NegationExpr.class);
37 if (negation != null) {
38 // Negations are implicitly quantified.
39 return false;
40 }
41 }
42 var conjunction = EcoreUtil2.getContainerOfType(variable, Conjunction.class);
43 if (conjunction == null) {
44 return true;
45 }
46 var variables = getExistsVariables(conjunction);
47 return !variables.contains(variable);
48 }
49
50 protected Set<Variable> getExistsVariables(Conjunction conjunction) {
51 var resource = conjunction.eResource();
52 if (resource == null) {
53 return doGetExistsVariables(conjunction);
54 }
55 return cache.get(Tuples.create(conjunction, EXISTS_VARIABLES), resource,
56 () -> doGetExistsVariables(conjunction));
57 }
58
59 protected Set<Variable> doGetExistsVariables(Conjunction conjunction) {
60 var builtinSymbols = importAdapterProvider.getBuiltinSymbols(conjunction);
61 var existsRelation = builtinSymbols.exists();
62 var set = new HashSet<Variable>();
63 for (var atom : EcoreUtil2.getAllContentsOfType(conjunction, Atom.class)) {
64 if (existsRelation.equals(atom.getRelation())) {
65 for (var argument : atom.getArguments()) {
66 if (argument instanceof VariableOrNodeExpr variableOrNodeExpr &&
67 variableOrNodeExpr.getVariableOrNode() instanceof Variable variable) {
68 set.add(variable);
69 }
70 }
71 }
72 }
73 return set;
74 }
75}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java b/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java
index 63c8631c..aef1e71d 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java
@@ -49,11 +49,11 @@ public class ProblemValidator extends AbstractProblemValidator {
49 public static final String INVALID_MODALITY_ISSUE = ISSUE_PREFIX + "INVALID_MODALITY"; 49 public static final String INVALID_MODALITY_ISSUE = ISSUE_PREFIX + "INVALID_MODALITY";
50 public static final String INVALID_RULE_ISSUE = ISSUE_PREFIX + "INVALID_RULE"; 50 public static final String INVALID_RULE_ISSUE = ISSUE_PREFIX + "INVALID_RULE";
51 public static final String INVALID_TRANSITIVE_CLOSURE_ISSUE = ISSUE_PREFIX + "INVALID_TRANSITIVE_CLOSURE"; 51 public static final String INVALID_TRANSITIVE_CLOSURE_ISSUE = ISSUE_PREFIX + "INVALID_TRANSITIVE_CLOSURE";
52 public static final String SHADOW_RELATION_ISSUE = ISSUE_PREFIX + "SHADOW_RELATION";
52 public static final String UNSUPPORTED_ASSERTION_ISSUE = ISSUE_PREFIX + "UNSUPPORTED_ASSERTION"; 53 public static final String UNSUPPORTED_ASSERTION_ISSUE = ISSUE_PREFIX + "UNSUPPORTED_ASSERTION";
53 public static final String UNKNOWN_EXPRESSION_ISSUE = ISSUE_PREFIX + "UNKNOWN_EXPRESSION"; 54 public static final String UNKNOWN_EXPRESSION_ISSUE = ISSUE_PREFIX + "UNKNOWN_EXPRESSION";
54 public static final String INVALID_ASSIGNMENT_ISSUE = ISSUE_PREFIX + "INVALID_ASSIGNMENT"; 55 public static final String INVALID_ASSIGNMENT_ISSUE = ISSUE_PREFIX + "INVALID_ASSIGNMENT";
55 public static final String TYPE_ERROR = ISSUE_PREFIX + "TYPE_ERROR"; 56 public static final String TYPE_ERROR = ISSUE_PREFIX + "TYPE_ERROR";
56 public static final String VARIABLE_WITHOUT_EXISTS = ISSUE_PREFIX + "VARIABLE_WITHOUT_EXISTS";
57 public static final String UNUSED_PARTIAL_RELATION = ISSUE_PREFIX + "UNUSED_PARTIAL_RELATION"; 57 public static final String UNUSED_PARTIAL_RELATION = ISSUE_PREFIX + "UNUSED_PARTIAL_RELATION";
58 public static final String UNUSED_PARAMETER = ISSUE_PREFIX + "UNUSED_PARAMETER"; 58 public static final String UNUSED_PARAMETER = ISSUE_PREFIX + "UNUSED_PARAMETER";
59 59
@@ -61,9 +61,6 @@ public class ProblemValidator extends AbstractProblemValidator {
61 private ReferenceCounter referenceCounter; 61 private ReferenceCounter referenceCounter;
62 62
63 @Inject 63 @Inject
64 private ExistsVariableCollector existsVariableCollector;
65
66 @Inject
67 private ActionTargetCollector actionTargetCollector; 64 private ActionTargetCollector actionTargetCollector;
68 65
69 @Inject 66 @Inject
@@ -135,14 +132,6 @@ public class ProblemValidator extends AbstractProblemValidator {
135 var variableOrNode = expr.getVariableOrNode(); 132 var variableOrNode = expr.getVariableOrNode();
136 if (variableOrNode instanceof Variable variable && ProblemUtil.isImplicitVariable(variable) 133 if (variableOrNode instanceof Variable variable && ProblemUtil.isImplicitVariable(variable)
137 && !ProblemUtil.isSingletonVariable(variable)) { 134 && !ProblemUtil.isSingletonVariable(variable)) {
138 if (EcoreUtil2.getContainerOfType(variable, ParametricDefinition.class) instanceof RuleDefinition &&
139 EcoreUtil2.getContainerOfType(variable, NegationExpr.class) == null &&
140 // If there is an exists constraint, it is the only constraint.
141 existsVariableCollector.missingExistsConstraint(variable)) {
142 // Existentially quantified variables in rules should not be singletons,
143 // because we have to add an {@code exists} constraint as well.
144 return;
145 }
146 var problem = EcoreUtil2.getContainerOfType(variable, Problem.class); 135 var problem = EcoreUtil2.getContainerOfType(variable, Problem.class);
147 if (problem != null && referenceCounter.countReferences(problem, variable) <= 1) { 136 if (problem != null && referenceCounter.countReferences(problem, variable) <= 1) {
148 var name = variable.getName(); 137 var name = variable.getName();
@@ -368,32 +357,48 @@ public class ProblemValidator extends AbstractProblemValidator {
368 357
369 @Check 358 @Check
370 public void checkReferenceType(ReferenceDeclaration referenceDeclaration) { 359 public void checkReferenceType(ReferenceDeclaration referenceDeclaration) {
360 var referenceType = referenceDeclaration.getReferenceType();
361 if (referenceType == null || referenceType.eIsProxy()) {
362 return;
363 }
371 boolean isDefaultReference = referenceDeclaration.getKind() == ReferenceKind.DEFAULT && 364 boolean isDefaultReference = referenceDeclaration.getKind() == ReferenceKind.DEFAULT &&
372 !ProblemUtil.isContainerReference(referenceDeclaration); 365 !ProblemUtil.isContainerReference(referenceDeclaration);
373 if (isDefaultReference || referenceDeclaration.getKind() == ReferenceKind.REFERENCE) { 366 if (isDefaultReference || referenceDeclaration.getKind() == ReferenceKind.REFERENCE) {
374 checkArity(referenceDeclaration, ProblemPackage.Literals.REFERENCE_DECLARATION__REFERENCE_TYPE, 1); 367 checkArity(referenceDeclaration, ProblemPackage.Literals.REFERENCE_DECLARATION__REFERENCE_TYPE, 1);
375 return; 368 if (ProblemUtil.isShadow(referenceType)) {
369 var message = "Shadow relations '%s' is not allowed in reference types."
370 .formatted(referenceType.getName());
371 acceptError(message, referenceDeclaration,
372 ProblemPackage.Literals.REFERENCE_DECLARATION__REFERENCE_TYPE, 0, SHADOW_RELATION_ISSUE);
373 }
374 } else if (!(referenceType instanceof ClassDeclaration)) {
375 var message = "Reference type '%s' of the containment or container reference '%s' is not a class."
376 .formatted(referenceType.getName(), referenceDeclaration.getName());
377 acceptError(message, referenceDeclaration, ProblemPackage.Literals.REFERENCE_DECLARATION__REFERENCE_TYPE,
378 0, INVALID_REFERENCE_TYPE_ISSUE);
376 } 379 }
377 var referenceType = referenceDeclaration.getReferenceType(); 380 }
378 if (referenceType == null || referenceType.eIsProxy() || referenceType instanceof ClassDeclaration) { 381
379 // Either correct, or a missing reference type where we are probably already emitting another error. 382 @Check
380 return; 383 public void checkPredicateDefinition(PredicateDefinition predicateDefinition) {
384 if (predicateDefinition.isError() && predicateDefinition.isShadow()) {
385 var message = "Shadow predicates cannot be marked as error predicates.";
386 acceptError(message, predicateDefinition, ProblemPackage.Literals.PREDICATE_DEFINITION__ERROR, 0,
387 SHADOW_RELATION_ISSUE);
381 } 388 }
382 var message = "Reference type '%s' of the containment or container reference '%s' is not a class."
383 .formatted(referenceType.getName(), referenceDeclaration.getName());
384 acceptError(message, referenceDeclaration, ProblemPackage.Literals.REFERENCE_DECLARATION__REFERENCE_TYPE, 0,
385 INVALID_REFERENCE_TYPE_ISSUE);
386 } 389 }
387 390
388 @Check 391 @Check
389 public void checkParameter(Parameter parameter) { 392 public void checkParameter(Parameter parameter) {
390 checkArity(parameter, ProblemPackage.Literals.PARAMETER__PARAMETER_TYPE, 1); 393 checkArity(parameter, ProblemPackage.Literals.PARAMETER__PARAMETER_TYPE, 1);
394 var type = parameter.getParameterType();
395 if (type != null && !type.eIsProxy() && ProblemUtil.isShadow(type)) {
396 var message = "Shadow relations '%s' is not allowed in parameter types.".formatted(type.getName());
397 acceptError(message, parameter, ProblemPackage.Literals.PARAMETER__PARAMETER_TYPE, 0,
398 SHADOW_RELATION_ISSUE);
399 }
391 var parametricDefinition = EcoreUtil2.getContainerOfType(parameter, ParametricDefinition.class); 400 var parametricDefinition = EcoreUtil2.getContainerOfType(parameter, ParametricDefinition.class);
392 if (parametricDefinition instanceof RuleDefinition rule) { 401 if (parametricDefinition instanceof RuleDefinition rule) {
393 if (parameter.getParameterType() != null && parameter.getModality() == Modality.NONE) {
394 acceptError("Parameter type modality must be specified.", parameter,
395 ProblemPackage.Literals.PARAMETER__PARAMETER_TYPE, 0, INVALID_MODALITY_ISSUE);
396 }
397 var kind = rule.getKind(); 402 var kind = rule.getKind();
398 var binding = parameter.getBinding(); 403 var binding = parameter.getBinding();
399 if (kind == RuleKind.PROPAGATION && binding != ParameterBinding.SINGLE) { 404 if (kind == RuleKind.PROPAGATION && binding != ParameterBinding.SINGLE) {
@@ -404,10 +409,6 @@ public class ProblemValidator extends AbstractProblemValidator {
404 ProblemPackage.Literals.PARAMETER__BINDING, 0, INVALID_MODALITY_ISSUE); 409 ProblemPackage.Literals.PARAMETER__BINDING, 0, INVALID_MODALITY_ISSUE);
405 } 410 }
406 } else { 411 } else {
407 if (parameter.getConcreteness() != Concreteness.PARTIAL || parameter.getModality() != Modality.NONE) {
408 acceptError("Modal parameter types are only supported in rule definitions.", parameter, null, 0,
409 INVALID_MODALITY_ISSUE);
410 }
411 if (parameter.getBinding() != ParameterBinding.SINGLE) { 412 if (parameter.getBinding() != ParameterBinding.SINGLE) {
412 acceptError("Parameter binding annotations are only supported in decision rules.", parameter, 413 acceptError("Parameter binding annotations are only supported in decision rules.", parameter,
413 ProblemPackage.PARAMETER__BINDING, 0, INVALID_MODALITY_ISSUE); 414 ProblemPackage.PARAMETER__BINDING, 0, INVALID_MODALITY_ISSUE);
@@ -418,13 +419,21 @@ public class ProblemValidator extends AbstractProblemValidator {
418 @Check 419 @Check
419 public void checkAtom(Atom atom) { 420 public void checkAtom(Atom atom) {
420 int argumentCount = atom.getArguments().size(); 421 int argumentCount = atom.getArguments().size();
421 checkArity(atom, ProblemPackage.Literals.ATOM__RELATION, argumentCount);
422 if (atom.isTransitiveClosure() && argumentCount != 2) { 422 if (atom.isTransitiveClosure() && argumentCount != 2) {
423 var message = "Transitive closure needs exactly 2 arguments, got %d arguments instead." 423 var message = "Transitive closure needs exactly 2 arguments, got %d arguments instead."
424 .formatted(argumentCount); 424 .formatted(argumentCount);
425 acceptError(message, atom, ProblemPackage.Literals.ATOM__TRANSITIVE_CLOSURE, 0, 425 acceptError(message, atom, ProblemPackage.Literals.ATOM__TRANSITIVE_CLOSURE, 0,
426 INVALID_TRANSITIVE_CLOSURE_ISSUE); 426 INVALID_TRANSITIVE_CLOSURE_ISSUE);
427 } 427 }
428 var target = atom.getRelation();
429 if (target == null || target.eIsProxy()) {
430 return;
431 }
432 checkArity(atom, ProblemPackage.Literals.ATOM__RELATION, argumentCount);
433 if (ProblemUtil.isShadow(target) && !ProblemUtil.mayReferToShadow(atom)) {
434 var message = "Shadow relation '%s' is not allowed in a non-shadow context.".formatted(target.getName());
435 acceptError(message, atom, ProblemPackage.Literals.ATOM__RELATION, 0, SHADOW_RELATION_ISSUE);
436 }
428 } 437 }
429 438
430 @Check 439 @Check
@@ -433,28 +442,6 @@ public class ProblemValidator extends AbstractProblemValidator {
433 acceptError("Rules must have exactly one consequent.", ruleDefinition, 442 acceptError("Rules must have exactly one consequent.", ruleDefinition,
434 ProblemPackage.Literals.NAMED_ELEMENT__NAME, 0, INVALID_RULE_ISSUE); 443 ProblemPackage.Literals.NAMED_ELEMENT__NAME, 0, INVALID_RULE_ISSUE);
435 } 444 }
436 var unquantifiedVariables = new HashSet<Variable>();
437 for (var variable : EcoreUtil2.getAllContentsOfType(ruleDefinition, Variable.class)) {
438 if (existsVariableCollector.missingExistsConstraint(variable)) {
439 unquantifiedVariables.add(variable);
440 }
441 }
442 for (var expr : EcoreUtil2.getAllContentsOfType(ruleDefinition, VariableOrNodeExpr.class)) {
443 if (expr.getVariableOrNode() instanceof Variable variable && unquantifiedVariables.contains(variable)) {
444 unquantifiedVariables.remove(variable);
445 var name = variable.getName();
446 String message;
447 if (ProblemUtil.isSingletonVariable(variable)) {
448 message = ("Remove the singleton variable marker '_' and clarify the quantification of variable " +
449 "'%s'.").formatted(name);
450 } else {
451 message = ("Add a 'must exists(%s)', 'may exists(%s)', or 'may !exists(%s)' constraint to " +
452 "clarify the quantification of variable '%s'.").formatted(name, name, name, name);
453 }
454 acceptWarning(message, expr, ProblemPackage.Literals.VARIABLE_OR_NODE_EXPR__VARIABLE_OR_NODE, 0,
455 VARIABLE_WITHOUT_EXISTS);
456 }
457 }
458 } 445 }
459 446
460 @Check 447 @Check
@@ -467,33 +454,17 @@ public class ProblemValidator extends AbstractProblemValidator {
467 for (var consequent : consequents) { 454 for (var consequent : consequents) {
468 countRuleParameterUsages(consequent, useCounts); 455 countRuleParameterUsages(consequent, useCounts);
469 } 456 }
470 var isError = ruleDefinition.getKind() == RuleKind.PROPAGATION;
471 int consequentCount = consequents.size(); 457 int consequentCount = consequents.size();
472 for (var entry : useCounts.entrySet()) { 458 for (var entry : useCounts.entrySet()) {
473 if (entry.getValue() < consequentCount) { 459 if (entry.getValue() < consequentCount) {
474 var parameter = entry.getKey(); 460 var parameter = entry.getKey();
475 var message = "Unused rule parameter '%s'.".formatted(parameter.getName()); 461 var message = "Unused rule parameter '%s'.".formatted(parameter.getName());
476 if (isError) { 462 acceptWarning(message, parameter, ProblemPackage.Literals.NAMED_ELEMENT__NAME, 0,
477 acceptError(message, parameter, ProblemPackage.Literals.NAMED_ELEMENT__NAME, 0, UNUSED_PARAMETER); 463 UNUSED_PARAMETER);
478 } else {
479 acceptWarning(message, parameter, ProblemPackage.Literals.NAMED_ELEMENT__NAME, 0,
480 UNUSED_PARAMETER);
481 }
482 } 464 }
483 } 465 }
484 } 466 }
485 467
486 @Check
487 public void checkPropagationRuleConsequent(Consequent consequent) {
488 var rule = EcoreUtil2.getContainerOfType(consequent, RuleDefinition.class);
489 if (rule == null || rule.getKind() != RuleKind.PROPAGATION) {
490 return;
491 }
492 if (consequent.getActions().size() > 1) {
493 acceptError("Propagation rules must have exactly one action.", consequent, null, 0, INVALID_RULE_ISSUE);
494 }
495 }
496
497 private static void countRuleParameterUsages(Consequent consequent, Map<Parameter, Integer> useCounts) { 468 private static void countRuleParameterUsages(Consequent consequent, Map<Parameter, Integer> useCounts) {
498 var usedParameters = new HashSet<Parameter>(); 469 var usedParameters = new HashSet<Parameter>();
499 for (var action : consequent.getActions()) { 470 for (var action : consequent.getActions()) {
@@ -502,14 +473,15 @@ public class ProblemValidator extends AbstractProblemValidator {
502 } 473 }
503 } 474 }
504 for (var usedParameter : usedParameters) { 475 for (var usedParameter : usedParameters) {
505 useCounts.compute(usedParameter, (ignored, value) -> value == null ? 0 : value + 1); 476 useCounts.compute(usedParameter, (ignored, value) -> value == null ? null : value + 1);
506 } 477 }
507 } 478 }
508 479
509 private static void collectUsedParameters(AssertionAction assertionAction, HashSet<Parameter> usedParameters) { 480 private static void collectUsedParameters(AssertionAction assertionAction, HashSet<Parameter> usedParameters) {
510 for (var argument : assertionAction.getArguments()) { 481 for (var argument : assertionAction.getArguments()) {
511 if (argument instanceof NodeAssertionArgument nodeAssertionArgument && 482 if (argument instanceof NodeAssertionArgument nodeAssertionArgument &&
512 nodeAssertionArgument.getNode() instanceof Parameter usedParameter) { 483 nodeAssertionArgument.getNode() instanceof Parameter usedParameter &&
484 !usedParameter.eIsProxy()) {
513 usedParameters.add(usedParameter); 485 usedParameters.add(usedParameter);
514 } 486 }
515 } 487 }
@@ -518,23 +490,40 @@ public class ProblemValidator extends AbstractProblemValidator {
518 @Check 490 @Check
519 public void checkAssertion(AbstractAssertion assertion) { 491 public void checkAssertion(AbstractAssertion assertion) {
520 var relation = assertion.getRelation(); 492 var relation = assertion.getRelation();
493 if (relation == null || relation.eIsProxy()) {
494 return;
495 }
521 if (relation instanceof DatatypeDeclaration) { 496 if (relation instanceof DatatypeDeclaration) {
522 var message = "Assertions for data types are not supported."; 497 var message = "Assertions for data types are not supported.";
523 acceptError(message, assertion, ProblemPackage.Literals.ABSTRACT_ASSERTION__RELATION, 0, 498 acceptError(message, assertion, ProblemPackage.Literals.ABSTRACT_ASSERTION__RELATION, 0,
524 UNSUPPORTED_ASSERTION_ISSUE); 499 UNSUPPORTED_ASSERTION_ISSUE);
525 return; 500 return;
526 } 501 }
502 if (ProblemUtil.isShadow(relation)) {
503 var message = "Shadow relation '%s' may not have any assertions.".formatted(relation.getName());
504 acceptError(message, assertion, ProblemPackage.Literals.ABSTRACT_ASSERTION__RELATION, 0,
505 SHADOW_RELATION_ISSUE);
506 }
527 int argumentCount = assertion.getArguments().size(); 507 int argumentCount = assertion.getArguments().size();
528 checkArity(assertion, ProblemPackage.Literals.ABSTRACT_ASSERTION__RELATION, argumentCount); 508 checkArity(assertion, ProblemPackage.Literals.ABSTRACT_ASSERTION__RELATION, argumentCount);
529 } 509 }
530 510
531 @Check 511 @Check
532 public void checkTypeScope(TypeScope typeScope) { 512 public void checkTypeScope(TypeScope typeScope) {
533 checkArity(typeScope, ProblemPackage.Literals.TYPE_SCOPE__TARGET_TYPE, 1);
534 if (typeScope.isIncrement() && ProblemUtil.isInModule(typeScope)) { 513 if (typeScope.isIncrement() && ProblemUtil.isInModule(typeScope)) {
535 acceptError("Incremental type scopes are not supported in modules", typeScope, null, 0, 514 acceptError("Incremental type scopes are not supported in modules", typeScope, null, 0,
536 INVALID_MULTIPLICITY_ISSUE); 515 INVALID_MULTIPLICITY_ISSUE);
537 } 516 }
517 var type = typeScope.getTargetType();
518 if (type == null || type.eIsProxy()) {
519 return;
520 }
521 checkArity(typeScope, ProblemPackage.Literals.TYPE_SCOPE__TARGET_TYPE, 1);
522 if (ProblemUtil.isShadow(type)) {
523 var message = "Shadow relations '%s' is not allowed in type scopes.".formatted(type.getName());
524 acceptError(message, typeScope, ProblemPackage.Literals.TYPE_SCOPE__TARGET_TYPE, 0,
525 SHADOW_RELATION_ISSUE);
526 }
538 } 527 }
539 528
540 private void checkArity(EObject eObject, EReference reference, int expectedArity) { 529 private void checkArity(EObject eObject, EReference reference, int expectedArity) {
diff --git a/subprojects/language/src/main/resources/tools/refinery/language/library/builtin.refinery b/subprojects/language/src/main/resources/tools/refinery/language/library/builtin.refinery
index 09c7d92b..6ae6beb1 100644
--- a/subprojects/language/src/main/resources/tools/refinery/language/library/builtin.refinery
+++ b/subprojects/language/src/main/resources/tools/refinery/language/library/builtin.refinery
@@ -8,6 +8,8 @@ pred exists(node).
8 8
9pred equals(left, right). 9pred equals(left, right).
10 10
11abstract class container extends node.
12
11abstract class contained extends node. 13abstract class contained extends node.
12 14
13pred contains(container, contained contained). 15pred contains(container, contained contained).