import org.simantics.scl.compiler.elaboration.contexts.TranslationContext;
import org.simantics.scl.compiler.elaboration.contexts.TypingContext;
import org.simantics.scl.compiler.elaboration.expressions.Expression;
+import org.simantics.scl.compiler.elaboration.expressions.ExpressionVisitor;
import org.simantics.scl.compiler.elaboration.expressions.Variable;
import org.simantics.scl.compiler.elaboration.expressions.printing.ExpressionToStringVisitor;
+import org.simantics.scl.compiler.elaboration.expressions.visitors.ForVariablesUsesVisitor;
+import org.simantics.scl.compiler.elaboration.expressions.visitors.StandardExpressionVisitor;
import org.simantics.scl.compiler.errors.Locations;
import org.simantics.scl.compiler.internal.parsing.Symbol;
newLiterals[i] = literals[i].replace(context);
return new CHRQuery(location, newLiterals);
}
+
+ public void accept(ExpressionVisitor visitor) {
+ for(CHRLiteral literal : literals) {
+ if(literal == null || literal.parameters == null)
+ continue; // FIXME why this happens?
+ for(Expression parameter : literal.parameters) {
+ if(parameter == null)
+ continue; // FIXME why this happens?
+ parameter.accept(visitor);
+ }
+ }
+ }
}
package org.simantics.scl.compiler.elaboration.chr;
import java.util.ArrayList;
+import java.util.HashMap;
import org.simantics.scl.compiler.compilation.CompilationContext;
import org.simantics.scl.compiler.elaboration.chr.plan.CHRSearchPlan;
import org.simantics.scl.compiler.elaboration.contexts.SimplificationContext;
import org.simantics.scl.compiler.elaboration.contexts.TranslationContext;
import org.simantics.scl.compiler.elaboration.contexts.TypingContext;
+import org.simantics.scl.compiler.elaboration.expressions.EAsPattern;
import org.simantics.scl.compiler.elaboration.expressions.EVariable;
+import org.simantics.scl.compiler.elaboration.expressions.Expression;
+import org.simantics.scl.compiler.elaboration.expressions.ExpressionVisitor;
import org.simantics.scl.compiler.elaboration.expressions.Variable;
import org.simantics.scl.compiler.elaboration.expressions.printing.ExpressionToStringVisitor;
+import org.simantics.scl.compiler.elaboration.expressions.visitors.StandardExpressionVisitor;
import org.simantics.scl.compiler.errors.Locations;
import org.simantics.scl.compiler.internal.parsing.Symbol;
import org.simantics.scl.compiler.types.Types;
context.disallowNewExistentials();
body.resolve(context);
existentialVariables = context.popExistentialFrame();
+
+ warnForExistentialsUsedOnlyOnce(context);
+ }
+
+ private static final Object NEVER_USED = new Object();
+
+ private void warnForExistentialsUsedOnlyOnce(TranslationContext context) {
+ // Initialize the hash map
+ HashMap<Variable, Object> usageCount = new HashMap<>(existentialVariables.length);
+ for(Variable var : existentialVariables)
+ if(!var.getName().equals("_"))
+ usageCount.put(var, NEVER_USED);
+
+ // Collect variable uses
+ ExpressionVisitor visitor = new StandardExpressionVisitor() {
+ private void handle(Expression expression, Variable variable) {
+ Object object = usageCount.remove(variable);
+ if(object == NEVER_USED)
+ usageCount.put(variable, expression);
+ }
+ @Override
+ public void visit(EVariable expression) {
+ if(expression.variable != null)
+ handle(expression, expression.variable);
+ }
+ @Override
+ public void visit(EAsPattern expression) {
+ expression.pattern.accept(this);
+ handle(expression, expression.var);
+ }
+ };
+ head.accept(visitor);
+ body.accept(visitor);
+
+ // Report as warnings
+ usageCount.forEach((variable, expression_) -> {
+ if(!(expression_ instanceof Expression))
+ return; // Should never happen
+ Expression expression = (Expression)expression_;
+ if(context.isExpandedFromWildcard(expression))
+ return;
+
+ context.getErrorLog().logWarning(expression.location,
+ "Existential variable " + variable.getName() + " is referred only once. Replace by _ if this is a wildcard.");
+ });
+
}
public void checkType(TypingContext context) {
TIntArrayList chrConstraintFrames = new TIntArrayList();
ArrayList<CHRConstraintEntry> chrConstraintEntries = new ArrayList<CHRConstraintEntry>();
+ private THashSet<Expression> expandedFromWildcard;
+
public CHRRuleset currentRuleset;
public ModuleDebugInfo moduleDebugInfo;
variable = new Variable(name);
variables.put(name, variable);
existentialFrame.variables.add(name);
- return new EVariable(variable);
+ return new EVariable(location, variable);
}
case '_': {
if(name.length()==1) {
return Environments.getRuleset(environment, name);
}
+ /**
+ * Tells that new existential variables are no longer allowed in this context.
+ */
public void disallowNewExistentials() {
getCurrentExistentialFrame().disallowNewExistentials = true;
}
+
+ /**
+ * Marks that the expression is a result of expanding .. wildcard pattern in records.
+ */
+ public void addExpandedFromWildcard(Expression expression) {
+ if(expandedFromWildcard == null)
+ expandedFromWildcard = new THashSet<>();
+ expandedFromWildcard.add(expression);
+ }
+
+ /**
+ * Asks if the expression is a result of expanding .. wildcard pattern in records.
+ */
+ public boolean isExpandedFromWildcard(Expression expression) {
+ if(expandedFromWildcard == null)
+ return false;
+ else
+ return expandedFromWildcard.contains(expression);
+ }
}
String variableName = fieldNames[i];
if(chrLiteral)
variableName = "?" + variableName;
- parameters[i] = new EVar(wildcardField.location, variableName);
+ EVar expandedVar = new EVar(wildcardField.location, variableName);
+ parameters[i] = expandedVar;
+ context.addExpandedFromWildcard(expandedVar);
}
}
if(!recordMap.isEmpty()) {
@Override
public Expression resolve(TranslationContext context) {
- return context.resolveVariable(location, name);
+ Expression resolved = context.resolveVariable(location, name);
+ if(context.isExpandedFromWildcard(this))
+ context.addExpandedFromWildcard(resolved);
+ return resolved;
}
@Override
@Override
public Expression resolveAsPattern(TranslationContext context) {
- return context.resolvePattern(this);
+ Expression resolved = context.resolvePattern(this);
+ if(context.isExpandedFromWildcard(this))
+ context.addExpandedFromWildcard(resolved);
+ return resolved;
}
@Override
package org.simantics.scl.compiler.elaboration.expressions;
+@FunctionalInterface
public interface VariableProcedure {
void execute(long location, Variable variable);
}
when ?x <- ?y
then True
--
+3:10-3:12: Existential variable ?x is referred only once. Replace by _ if this is a wildcard.
3:10-3:18: Cannot solve the query.
+3:16-3:18: Existential variable ?y is referred only once. Replace by _ if this is a wildcard.
--
import "Prelude"
where
X ?x => Y ?y
--
+6:7-6:9: Existential variable ?x is referred only once. Replace by _ if this is a wildcard.
6:15-6:17: New existential variables can be defined only in queries.
\ No newline at end of file