diff --git a/prism/src/parser/ast/Expression.java b/prism/src/parser/ast/Expression.java
index 8fbe7e2d..a1775a03 100644
--- a/prism/src/parser/ast/Expression.java
+++ b/prism/src/parser/ast/Expression.java
@@ -978,6 +978,11 @@ public abstract class Expression extends ASTElement
/**
* Test if an expression is a co-safe LTL formula, detected syntactically
* (i.e. if it is in positive normal form and only uses X, F and U).
+ *
+ * Treats nested non-Boolean state subformulas as opaque, i.e., considers
+ * only the top-level LTL formula without recursing into operators such as P, R, etc.
+ *
+ * Temporal operator bounds are considered to render the LTL formula unsafe.
*/
public static boolean isCoSafeLTLSyntactic(Expression expr)
{
@@ -990,6 +995,11 @@ public abstract class Expression extends ASTElement
* If {@code convert} is true, the expression is first converted into positive normal form,
* and then it is checked whether it only uses X, F and U.
* For example, a => ! (G b) would return true if (and only if) {@code convert} was true.
+ *
+ * Treats nested non-Boolean state subformulas as opaque, i.e., considers
+ * only the top-level LTL formula without recursing into operators such as P, R, etc.
+ *
+ * Temporal operator bounds are considered to render the LTL formula unsafe.
*/
public static boolean isCoSafeLTLSyntactic(Expression expr, boolean convert)
{
@@ -1000,12 +1010,17 @@ public abstract class Expression extends ASTElement
if (!isPositiveNormalFormLTL(expr))
return false;
}
- // Check temporal operators
+
+ // Check temporal operators, don't recurse into nested operators
try {
- ASTTraverse astt = new ASTTraverse()
+ ASTTraverse astt = new ExpressionTraverseNonNested()
{
public void visitPost(ExpressionTemporal e) throws PrismLangException
{
+ if (e.hasBounds()) {
+ throw new PrismLangException("Found temporal operator bound", e);
+ }
+
if (e.getOperator() == ExpressionTemporal.P_X)
return;
if (e.getOperator() == ExpressionTemporal.P_F)