66import java .util .Map ;
77import java .util .Optional ;
88
9+ import liquidjava .diagnostics .Diagnostics ;
910import liquidjava .diagnostics .errors .*;
11+ import liquidjava .diagnostics .warnings .UnsatisfiableRefinementWarning ;
1012import liquidjava .processor .context .AliasWrapper ;
1113import liquidjava .processor .context .Context ;
1214import liquidjava .processor .context .GhostFunction ;
1618import liquidjava .processor .facade .GhostDTO ;
1719import liquidjava .rj_language .Predicate ;
1820import liquidjava .rj_language .parsing .RefinementsParser ;
21+ import liquidjava .smt .SMTEvaluator ;
1922import liquidjava .smt .SMTResult ;
2023import liquidjava .utils .Utils ;
2124import liquidjava .utils .constants .Formats ;
2932import spoon .reflect .declaration .CtClass ;
3033import spoon .reflect .declaration .CtElement ;
3134import spoon .reflect .declaration .CtInterface ;
35+ import spoon .reflect .declaration .CtMethod ;
36+ import spoon .reflect .declaration .CtTypedElement ;
37+ import spoon .reflect .declaration .CtVariable ;
3238import spoon .reflect .declaration .CtType ;
3339import spoon .reflect .factory .Factory ;
3440import spoon .reflect .reference .CtTypeReference ;
@@ -41,6 +47,7 @@ public abstract class TypeChecker extends CtScanner {
4147 protected final Context context ;
4248 protected final Factory factory ;
4349 protected final VCChecker vcChecker ;
50+ private final Diagnostics diagnostics = Diagnostics .getInstance ();
4451
4552 public TypeChecker (Context context , Factory factory ) {
4653 this .context = context ;
@@ -88,11 +95,53 @@ public Optional<Predicate> getRefinementFromAnnotation(CtElement element) throws
8895 throw new InvalidRefinementError (position , "Refinement predicate must be a boolean expression" ,
8996 ref .get ());
9097 }
98+ if (!Boolean .TRUE .equals (element .getMetadata (Keys .REFINEMENT_SAT_CHECK )))
99+ checkRefinementSatisfiability (ref .get (), p , element );
91100 constr = Optional .of (p );
92101 }
93102 return constr ;
94103 }
95104
105+ /**
106+ * Performs a best-effort satisfiability check for a refinement reporting a warning if unsat. Runs an SMT check on a
107+ * temporary scope and if the refinement mentions other names that are still unavailable at this point, the SMT
108+ * check fails and that failure is ignored.
109+ */
110+ private void checkRefinementSatisfiability (String refinement , Predicate predicate , CtElement element ) {
111+ context .enterContext ();
112+ try {
113+ Predicate p = new Predicate ();
114+ CtTypeReference <?> annotationType = element instanceof CtTypedElement <?> typedElement
115+ ? typedElement .getType () : null ;
116+ if (annotationType != null && !context .hasVariable (Keys .WILDCARD ))
117+ context .addVarToContext (Keys .WILDCARD , annotationType , p , element );
118+
119+ if (element instanceof CtVariable <?> variable && !context .hasVariable (variable .getSimpleName ()))
120+ context .addVarToContext (variable .getSimpleName (), variable .getType (), p , element );
121+
122+ if (element instanceof CtMethod <?> method && method .getType () != null && !context .hasVariable ("return" ))
123+ context .addVarToContext ("return" , method .getType (), p , element );
124+
125+ String qualifiedClassName = getQualifiedClassName (element );
126+ if (qualifiedClassName != null && !context .hasVariable (Keys .THIS ))
127+ context .addVarToContext (Keys .THIS , factory .Type ().createReference (qualifiedClassName ), p , element );
128+
129+ Predicate refinementPredicate = predicate .changeStatesToRefinements (context .getGhostStates (),
130+ new String [] { Keys .WILDCARD , Keys .THIS });
131+ refinementPredicate = refinementPredicate .changeAliasToRefinement (context , factory );
132+
133+ if (new SMTEvaluator ().isUnsatisfiable (refinementPredicate , context )) {
134+ SourcePosition position = Utils .getLJAnnotationPosition (element , refinement );
135+ diagnostics .add (new UnsatisfiableRefinementWarning (position , refinement ));
136+ }
137+ element .putMetadata (Keys .REFINEMENT_SAT_CHECK , true ); // for caching the satisfiability check result
138+ } catch (Exception e ) {
139+ // ignore
140+ } finally {
141+ context .exitContext ();
142+ }
143+ }
144+
96145 @ SuppressWarnings ({ "rawtypes" })
97146 public Optional <String > getMessageFromAnnotation (CtElement element ) {
98147 for (CtAnnotation <? extends Annotation > ann : element .getAnnotations ()) {
0 commit comments