diff --git a/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/BlockContractValidityTermLabelUpdate.java b/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/BlockContractValidityTermLabelUpdate.java index 88408fc4a4f..973d0456e8d 100644 --- a/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/BlockContractValidityTermLabelUpdate.java +++ b/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/BlockContractValidityTermLabelUpdate.java @@ -5,19 +5,15 @@ import java.util.Set; -import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.label.BlockContractValidityTermLabel; import de.uka.ilkd.key.logic.label.TermLabel; -import de.uka.ilkd.key.logic.label.TermLabelState; +import de.uka.ilkd.key.logic.label.TermLabelContext; import de.uka.ilkd.key.rule.BlockContractInternalRule; import de.uka.ilkd.key.rule.LoopContractInternalRule; import de.uka.ilkd.key.symbolic_execution.util.SymbolicExecutionUtil; import org.key_project.logic.Name; -import org.key_project.prover.rules.Rule; -import org.key_project.prover.rules.RuleApp; -import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.util.collection.ImmutableList; import org.key_project.util.java.CollectionUtil; @@ -40,18 +36,17 @@ public ImmutableList getSupportedRuleNames() { * {@inheritDoc} */ @Override - public void updateLabels(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, JTerm applicationTerm, JTerm modalityTerm, - Rule rule, RuleApp ruleApp, Object hint, JTerm tacletTerm, JTerm newTerm, - Set labels) { - if ((rule instanceof BlockContractInternalRule || rule instanceof LoopContractInternalRule) - && ((BlockContractInternalRule.BlockContractHint) hint) + public void updateLabels(TermLabelContext context, JTerm newTerm, Set labels) { + if ((context.rule() instanceof BlockContractInternalRule + || context.rule() instanceof LoopContractInternalRule) + && ((BlockContractInternalRule.BlockContractHint) context.hint()) .getExceptionalVariable() != null - && SymbolicExecutionUtil.hasSymbolicExecutionLabel(modalityTerm)) { + && SymbolicExecutionUtil.hasSymbolicExecutionLabel(context.modalityTerm())) { if (CollectionUtil.search(labels, element -> element instanceof BlockContractValidityTermLabel) == null) { labels.add(new BlockContractValidityTermLabel( - ((BlockContractInternalRule.BlockContractHint) hint).getExceptionalVariable())); + ((BlockContractInternalRule.BlockContractHint) context.hint()) + .getExceptionalVariable())); } } } diff --git a/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/FormulaTermLabelRefactoring.java b/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/FormulaTermLabelRefactoring.java index c9607d88c29..e9c48f72845 100644 --- a/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/FormulaTermLabelRefactoring.java +++ b/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/FormulaTermLabelRefactoring.java @@ -10,6 +10,7 @@ import de.uka.ilkd.key.logic.label.FormulaTermLabel; import de.uka.ilkd.key.logic.label.LabelCollection; import de.uka.ilkd.key.logic.label.TermLabel; +import de.uka.ilkd.key.logic.label.TermLabelContext; import de.uka.ilkd.key.logic.label.TermLabelState; import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.proof.Proof; @@ -112,19 +113,17 @@ public ImmutableList getSupportedRuleNames() { * {@inheritDoc} */ @Override - public RefactoringScope defineRefactoringScope(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm) { - if (shouldRefactorSpecificationApplication(rule, goal, hint)) { + public RefactoringScope defineRefactoringScope(TermLabelContext context) { + if (shouldRefactorSpecificationApplication(context.rule(), context.goal(), + context.hint())) { return RefactoringScope.APPLICATION_CHILDREN_AND_GRANDCHILDREN_SUBTREE; - } else if (isParentRefactoringRequired(state)) { + } else if (isParentRefactoringRequired(context.state())) { return RefactoringScope.APPLICATION_CHILDREN_AND_GRANDCHILDREN_SUBTREE_AND_PARENTS; - } else if (isUpdateRefactoringRequired(state)) { + } else if (isUpdateRefactoringRequired(context.state())) { return RefactoringScope.APPLICATION_BELOW_UPDATES; - } else if (containsSequentFormulasToRefactor(state)) { + } else if (containsSequentFormulasToRefactor(context.state())) { return RefactoringScope.SEQUENT; - } else if (SyntacticalReplaceVisitor.SUBSTITUTION_WITH_LABELS_HINT.equals(hint)) { + } else if (SyntacticalReplaceVisitor.SUBSTITUTION_WITH_LABELS_HINT.equals(context.hint())) { return RefactoringScope.APPLICATION_BELOW_UPDATES; } else { return RefactoringScope.NONE; @@ -147,20 +146,19 @@ private boolean shouldRefactorSpecificationApplication(Rule rule, Goal goal, Obj * {@inheritDoc} */ @Override - public void refactorLabels(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm, JTerm term, LabelCollection labels) { - if (shouldRefactorSpecificationApplication(rule, goal, hint)) { - refactorSpecificationApplication(term, services, labels, hint); - } else if (isParentRefactoringRequired(state)) { - refactorInCaseOfNewIdRequired(state, goal, term, services, labels); - } else if (isUpdateRefactoringRequired(state)) { - refactorBelowUpdates(applicationPosInOccurrence, term, labels); - } else if (containsSequentFormulasToRefactor(state)) { - refactorSequentFormulas(state, services, term, labels); - } else if (SyntacticalReplaceVisitor.SUBSTITUTION_WITH_LABELS_HINT.equals(hint)) { - refactorSubstitution(term, tacletTerm, labels); + public void refactorLabels(TermLabelContext context, JTerm term, LabelCollection labels) { + if (shouldRefactorSpecificationApplication(context.rule(), context.goal(), + context.hint())) { + refactorSpecificationApplication(term, context.services(), labels, context.hint()); + } else if (isParentRefactoringRequired(context.state())) { + refactorInCaseOfNewIdRequired(context.state(), context.goal(), term, context.services(), + labels); + } else if (isUpdateRefactoringRequired(context.state())) { + refactorBelowUpdates(context.applicationPosInOccurrence(), term, labels); + } else if (containsSequentFormulasToRefactor(context.state())) { + refactorSequentFormulas(context.state(), context.services(), term, labels); + } else if (SyntacticalReplaceVisitor.SUBSTITUTION_WITH_LABELS_HINT.equals(context.hint())) { + refactorSubstitution(term, context.tacletTerm(), labels); } } diff --git a/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/FormulaTermLabelUpdate.java b/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/FormulaTermLabelUpdate.java index d80e5fe0a9f..147c4acce62 100644 --- a/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/FormulaTermLabelUpdate.java +++ b/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/FormulaTermLabelUpdate.java @@ -9,22 +9,18 @@ import java.util.Map.Entry; import java.util.Set; -import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.logic.*; import de.uka.ilkd.key.logic.label.FormulaTermLabel; import de.uka.ilkd.key.logic.label.TermLabel; +import de.uka.ilkd.key.logic.label.TermLabelContext; import de.uka.ilkd.key.logic.label.TermLabelManager; -import de.uka.ilkd.key.logic.label.TermLabelState; import de.uka.ilkd.key.rule.Taclet.TacletLabelHint; import de.uka.ilkd.key.rule.Taclet.TacletLabelHint.TacletOperation; import de.uka.ilkd.key.rule.TacletApp; import de.uka.ilkd.key.symbolic_execution.TruthValueTracingUtil; import org.key_project.logic.Name; -import org.key_project.prover.rules.Rule; -import org.key_project.prover.rules.RuleApp; import org.key_project.prover.rules.instantiation.AssumesFormulaInstantiation; -import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.prover.sequent.SequentFormula; import org.key_project.util.collection.ImmutableList; import org.key_project.util.java.CollectionUtil; @@ -48,12 +44,8 @@ public ImmutableList getSupportedRuleNames() { * {@inheritDoc} */ @Override - public void updateLabels(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, JTerm modalityTerm, - Rule rule, RuleApp ruleApp, Object hint, JTerm tacletTerm, JTerm newTerm, - Set labels) { - if (hint instanceof TacletLabelHint tacletHint) { + public void updateLabels(TermLabelContext context, JTerm newTerm, Set labels) { + if (context.hint() instanceof TacletLabelHint tacletHint) { if ((TacletOperation.ADD_ANTECEDENT.equals(tacletHint.getTacletOperation()) || TacletOperation.ADD_SUCCEDENT.equals(tacletHint.getTacletOperation())) && (TruthValueTracingUtil.isPredicate(newTerm) @@ -61,20 +53,22 @@ public void updateLabels(TermLabelState state, Services services, newTerm.subs()))) { if (getTermLabel(labels, FormulaTermLabel.NAME) == null) { TermLabel label = TermLabelManager.findInnerMostParentLabel( - applicationPosInOccurrence, FormulaTermLabel.NAME); + context.applicationPosInOccurrence(), FormulaTermLabel.NAME); if (label instanceof FormulaTermLabel oldLabel) { - int labelSubID = FormulaTermLabel.newLabelSubID(services, oldLabel); + int labelSubID = + FormulaTermLabel.newLabelSubID(context.services(), oldLabel); FormulaTermLabel newLabel = new FormulaTermLabel(oldLabel.getMajorId(), labelSubID, Collections.singletonList(oldLabel.getId())); labels.add(newLabel); // Let the PredicateTermLabelRefactoring perform the refactoring, see also // PredicateTermLabelRefactoring#PARENT_REFACTORING_REQUIRED - FormulaTermLabelRefactoring.setParentRefactoringRequired(state, true); + FormulaTermLabelRefactoring.setParentRefactoringRequired(context.state(), + true); } } } } - if (ruleApp instanceof TacletApp ta) { + if (context.ruleApp() instanceof TacletApp ta) { if (ta.assumesInstantionsComplete() && ta.assumesFormulaInstantiations() != null) { Map ifLabels = new LinkedHashMap<>(); @@ -92,11 +86,12 @@ public void updateLabels(TermLabelState state, Services services, for (Entry ifEntry : ifLabels .entrySet()) { FormulaTermLabel ifLabel = ifEntry.getValue(); - int labelSubID = FormulaTermLabel.newLabelSubID(services, ifLabel); + int labelSubID = + FormulaTermLabel.newLabelSubID(context.services(), ifLabel); FormulaTermLabel newLabel = new FormulaTermLabel(ifLabel.getMajorId(), labelSubID, Collections.singletonList(ifLabel.getId())); labels.add(newLabel); - FormulaTermLabelRefactoring.addSequentFormulaToRefactor(state, + FormulaTermLabelRefactoring.addSequentFormulaToRefactor(context.state(), ifEntry.getKey()); } } diff --git a/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/LoopBodyTermLabelUpdate.java b/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/LoopBodyTermLabelUpdate.java index 70faae66f69..097d368dc88 100644 --- a/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/LoopBodyTermLabelUpdate.java +++ b/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/LoopBodyTermLabelUpdate.java @@ -5,17 +5,13 @@ import java.util.Set; -import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.label.TermLabel; -import de.uka.ilkd.key.logic.label.TermLabelState; +import de.uka.ilkd.key.logic.label.TermLabelContext; import de.uka.ilkd.key.rule.WhileInvariantRule; import de.uka.ilkd.key.symbolic_execution.util.SymbolicExecutionUtil; import org.key_project.logic.Name; -import org.key_project.prover.rules.Rule; -import org.key_project.prover.rules.RuleApp; -import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.util.collection.ImmutableList; /** @@ -37,12 +33,10 @@ public ImmutableList getSupportedRuleNames() { * {@inheritDoc} */ @Override - public void updateLabels(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, JTerm applicationTerm, JTerm modalityTerm, - Rule rule, RuleApp ruleApp, Object hint, JTerm tacletTerm, JTerm newTerm, - Set labels) { - if (rule instanceof WhileInvariantRule && "LoopBodyModality".equals(hint) - && SymbolicExecutionUtil.hasSymbolicExecutionLabel(modalityTerm)) { + public void updateLabels(TermLabelContext context, JTerm newTerm, Set labels) { + if (context.rule() instanceof WhileInvariantRule + && "LoopBodyModality".equals(context.hint()) + && SymbolicExecutionUtil.hasSymbolicExecutionLabel(context.modalityTerm())) { labels.add(SymbolicExecutionUtil.LOOP_BODY_LABEL); } } diff --git a/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/LoopInvariantNormalBehaviorTermLabelUpdate.java b/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/LoopInvariantNormalBehaviorTermLabelUpdate.java index d5022ed9661..307c316f5ee 100644 --- a/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/LoopInvariantNormalBehaviorTermLabelUpdate.java +++ b/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/LoopInvariantNormalBehaviorTermLabelUpdate.java @@ -5,17 +5,13 @@ import java.util.Set; -import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.label.TermLabel; -import de.uka.ilkd.key.logic.label.TermLabelState; +import de.uka.ilkd.key.logic.label.TermLabelContext; import de.uka.ilkd.key.rule.WhileInvariantRule; import de.uka.ilkd.key.symbolic_execution.util.SymbolicExecutionUtil; import org.key_project.logic.Name; -import org.key_project.prover.rules.Rule; -import org.key_project.prover.rules.RuleApp; -import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.util.collection.ImmutableList; /** @@ -37,12 +33,10 @@ public ImmutableList getSupportedRuleNames() { * {@inheritDoc} */ @Override - public void updateLabels(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, JTerm applicationTerm, JTerm modalityTerm, - Rule rule, RuleApp ruleApp, Object hint, JTerm tacletTerm, JTerm newTerm, - Set labels) { - if (rule instanceof WhileInvariantRule && "LoopBodyImplication".equals(hint) - && SymbolicExecutionUtil.hasSymbolicExecutionLabel(modalityTerm)) { + public void updateLabels(TermLabelContext context, JTerm newTerm, Set labels) { + if (context.rule() instanceof WhileInvariantRule + && "LoopBodyImplication".equals(context.hint()) + && SymbolicExecutionUtil.hasSymbolicExecutionLabel(context.modalityTerm())) { labels.add(SymbolicExecutionUtil.LOOP_INVARIANT_NORMAL_BEHAVIOR_LABEL); } } diff --git a/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/RemoveInCheckBranchesTermLabelRefactoring.java b/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/RemoveInCheckBranchesTermLabelRefactoring.java index 17064ef97bc..a40839c1dc2 100644 --- a/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/RemoveInCheckBranchesTermLabelRefactoring.java +++ b/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/RemoveInCheckBranchesTermLabelRefactoring.java @@ -3,12 +3,10 @@ * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.rule.label; -import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.label.LabelCollection; import de.uka.ilkd.key.logic.label.TermLabel; -import de.uka.ilkd.key.logic.label.TermLabelState; -import de.uka.ilkd.key.proof.Goal; +import de.uka.ilkd.key.logic.label.TermLabelContext; import de.uka.ilkd.key.rule.AbstractAuxiliaryContractRule; import de.uka.ilkd.key.rule.BlockContractExternalRule; import de.uka.ilkd.key.rule.BlockContractInternalRule; @@ -18,8 +16,6 @@ import de.uka.ilkd.key.rule.WhileInvariantRule; import org.key_project.logic.Name; -import org.key_project.prover.rules.Rule; -import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.util.collection.ImmutableList; /** @@ -67,13 +63,10 @@ public ImmutableList getSupportedRuleNames() { * {@inheritDoc} */ @Override - public RefactoringScope defineRefactoringScope(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm) { - if (goal != null) { - final String branchLabel = goal.node().getNodeInfo().getBranchLabel(); - return switch (rule) { + public RefactoringScope defineRefactoringScope(TermLabelContext context) { + if (context.goal() != null) { + final String branchLabel = context.goal().node().getNodeInfo().getBranchLabel(); + return switch (context.rule()) { case UseOperationContractRule ignored when (branchLabel.startsWith("Pre") || branchLabel.startsWith("Null reference")) -> RefactoringScope.SEQUENT; @@ -94,9 +87,7 @@ case UseOperationContractRule ignored when (branchLabel.startsWith("Pre") || * {@inheritDoc} */ @Override - public void refactorLabels(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm, JTerm term, LabelCollection labels) { + public void refactorLabels(TermLabelContext context, JTerm term, LabelCollection labels) { labels.removeIf(next -> termLabelNameToRemove.equals(next.name())); } } diff --git a/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/StayOnFormulaTermLabelPolicy.java b/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/StayOnFormulaTermLabelPolicy.java index 58c2c392fb2..25e755faec4 100644 --- a/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/StayOnFormulaTermLabelPolicy.java +++ b/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/StayOnFormulaTermLabelPolicy.java @@ -7,19 +7,15 @@ import java.util.LinkedHashSet; import java.util.Set; -import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.label.FormulaTermLabel; import de.uka.ilkd.key.logic.label.TermLabel; -import de.uka.ilkd.key.logic.label.TermLabelState; +import de.uka.ilkd.key.logic.label.TermLabelContext; import de.uka.ilkd.key.logic.op.*; -import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.rule.Taclet.TacletLabelHint; import de.uka.ilkd.key.rule.Taclet.TacletLabelHint.TacletOperation; import de.uka.ilkd.key.symbolic_execution.TruthValueTracingUtil; -import org.key_project.prover.rules.Rule; -import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.util.collection.ImmutableArray; import org.key_project.util.java.CollectionUtil; @@ -33,10 +29,8 @@ public class StayOnFormulaTermLabelPolicy implements TermLabelPolicy { * {@inheritDoc} */ @Override - public TermLabel keepLabel(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm, - JTerm newTerm, TermLabel label) { + public TermLabel keepLabel(TermLabelContext context, JTerm sourceTerm, JTerm newTerm, + TermLabel label) { // Maintain label if new Term is a predicate if (TruthValueTracingUtil.isPredicate(newTerm.op()) || TruthValueTracingUtil.isLogicOperator(newTerm.op(), newTerm.subs())) { @@ -48,7 +42,7 @@ public TermLabel keepLabel(TermLabelState state, Services services, // May change sub ID if logical operators like junctors are used boolean newLabelIdRequired = false; Set originalLabelIds = new LinkedHashSet<>(); - if (hint instanceof TacletLabelHint tacletHint) { + if (context.hint() instanceof TacletLabelHint tacletHint) { if (isBelowIfThenElse(tacletHint.getTacletTermStack())) { return null; // Do not label children of if-then-else. They are labeled when a // rule rewrites them outside of the if-then-else. @@ -74,7 +68,7 @@ public TermLabel keepLabel(TermLabelState state, Services services, newLabelIdRequired = true; } } else if (tacletHint.getTerm() != null) { - boolean topLevel = isTopLevel(tacletHint, tacletTerm); + boolean topLevel = isTopLevel(tacletHint, context.tacletTerm()); if (!topLevel && !TruthValueTracingUtil.isPredicate(tacletHint.getTerm())) { newLabelIdRequired = true; } @@ -91,7 +85,8 @@ public TermLabel keepLabel(TermLabelState state, Services services, if (originalLabel != null) { originalLabelIds.add(originalLabel.getId()); } - int labelSubID = FormulaTermLabel.newLabelSubID(services, mostImportantLabel); + int labelSubID = + FormulaTermLabel.newLabelSubID(context.services(), mostImportantLabel); if (!originalLabelIds.isEmpty()) { return new FormulaTermLabel(mostImportantLabel.getMajorId(), labelSubID, originalLabelIds); @@ -110,13 +105,14 @@ public TermLabel keepLabel(TermLabelState state, Services services, JTerm target = newTerm.subs().get(UpdateApplication.targetPos()); TermLabel targetLabel = target.getLabel(FormulaTermLabel.NAME); if (targetLabel instanceof FormulaTermLabel) { - if (applicationPosInOccurrence != null) { - JTerm appliationTerm = (JTerm) applicationPosInOccurrence.subTerm(); + if (context.applicationPosInOccurrence() != null) { + JTerm appliationTerm = (JTerm) context.applicationPosInOccurrence().subTerm(); TermLabel applicationLabel = appliationTerm.getLabel(FormulaTermLabel.NAME); if (applicationLabel instanceof FormulaTermLabel) { // Let the PredicateTermLabelRefactoring perform the refactoring, see also // PredicateTermLabelRefactoring#UPDATE_REFACTORING_REQUIRED - FormulaTermLabelRefactoring.setUpdateRefactoringRequired(state, true); + FormulaTermLabelRefactoring.setUpdateRefactoringRequired(context.state(), + true); } } } diff --git a/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/SymbolicExecutionTermLabelUpdate.java b/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/SymbolicExecutionTermLabelUpdate.java index 381dada7ebe..f5398644b82 100644 --- a/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/SymbolicExecutionTermLabelUpdate.java +++ b/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/rule/label/SymbolicExecutionTermLabelUpdate.java @@ -5,17 +5,13 @@ import java.util.Set; -import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.label.SymbolicExecutionTermLabel; import de.uka.ilkd.key.logic.label.TermLabel; -import de.uka.ilkd.key.logic.label.TermLabelState; +import de.uka.ilkd.key.logic.label.TermLabelContext; import de.uka.ilkd.key.rule.*; import org.key_project.logic.Name; -import org.key_project.prover.rules.Rule; -import org.key_project.prover.rules.RuleApp; -import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.util.collection.ImmutableList; import org.key_project.util.java.CollectionUtil; @@ -42,20 +38,18 @@ public ImmutableList getSupportedRuleNames() { * {@inheritDoc} */ @Override - public void updateLabels(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, JTerm applicationTerm, JTerm modalityTerm, - Rule rule, RuleApp ruleApp, Object hint, JTerm tacletTerm, - JTerm newTerm, - Set labels) { - if (rule instanceof WhileInvariantRule && "LoopBodyModality".equals(hint) - || (rule instanceof AbstractAuxiliaryContractRule - && ((AbstractBlockContractRule.BlockContractHint) hint) + public void updateLabels(TermLabelContext context, JTerm newTerm, Set labels) { + if (context.rule() instanceof WhileInvariantRule + && "LoopBodyModality".equals(context.hint()) + || (context.rule() instanceof AbstractAuxiliaryContractRule + && ((AbstractBlockContractRule.BlockContractHint) context.hint()) .getExceptionalVariable() != null)) { TermLabel label = CollectionUtil.searchAndRemove(labels, element -> element instanceof SymbolicExecutionTermLabel); if (label instanceof SymbolicExecutionTermLabel) { - int labelID = services.getCounter(SymbolicExecutionTermLabel.PROOF_COUNTER_NAME) - .getCountPlusPlus(); + int labelID = + context.services().getCounter(SymbolicExecutionTermLabel.PROOF_COUNTER_NAME) + .getCountPlusPlus(); labels.add(new SymbolicExecutionTermLabel(labelID)); } } diff --git a/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/symbolic_execution/profile/SimplifyTermProfile.java b/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/symbolic_execution/profile/SimplifyTermProfile.java index 00f9e5f051f..20af2c594a5 100644 --- a/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/symbolic_execution/profile/SimplifyTermProfile.java +++ b/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/symbolic_execution/profile/SimplifyTermProfile.java @@ -61,12 +61,10 @@ protected ImmutableList computeTermLabelConfiguration() ImmutableList result = super.computeTermLabelConfiguration(); ImmutableList symExcPolicies = ImmutableList.nil() - .prepend((state, services, applicationPosInOccurrence, applicationTerm, rule, - goal, hint, tacletTerm, - newTerm, label) -> label); + .prepend((context, sourceTerm, newTerm, label) -> label); result = result.prepend(new TermLabelConfiguration(SymbolicExecutionUtil.RESULT_LABEL_NAME, new SingletonLabelFactory<>(SymbolicExecutionUtil.RESULT_LABEL), null, - symExcPolicies, null, null, null, null, null)); + symExcPolicies, null, null, null)); return result; } diff --git a/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/symbolic_execution/profile/SymbolicExecutionJavaProfile.java b/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/symbolic_execution/profile/SymbolicExecutionJavaProfile.java index 021dd65a003..cac2c5d6f4a 100644 --- a/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/symbolic_execution/profile/SymbolicExecutionJavaProfile.java +++ b/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/symbolic_execution/profile/SymbolicExecutionJavaProfile.java @@ -168,19 +168,19 @@ public static ImmutableList getSymbolicExecutionTermLabe ImmutableList result = ImmutableList.nil(); result = result.prepend(new TermLabelConfiguration(BlockContractValidityTermLabel.NAME, - new BlockContractValidityTermLabelFactory(), null, symExcPolicies, null, null, bcUps, + new BlockContractValidityTermLabelFactory(), null, symExcPolicies, bcUps, bcRefs, null)); result = result.prepend(new TermLabelConfiguration(SymbolicExecutionUtil.LOOP_BODY_LABEL_NAME, new SingletonLabelFactory<>(SymbolicExecutionUtil.LOOP_BODY_LABEL), null, - symExcPolicies, null, null, lbUps, lbRefs, null)); + symExcPolicies, lbUps, lbRefs, null)); result = result.prepend(new TermLabelConfiguration( SymbolicExecutionUtil.LOOP_INVARIANT_NORMAL_BEHAVIOR_LABEL_NAME, new SingletonLabelFactory<>( SymbolicExecutionUtil.LOOP_INVARIANT_NORMAL_BEHAVIOR_LABEL), - null, symExcPolicies, null, null, nbUps, nbRefs, null)); + null, symExcPolicies, nbUps, nbRefs, null)); result = result.prepend(new TermLabelConfiguration(SymbolicExecutionTermLabel.NAME, - new SymbolicExecutionTermLabelFactory(), null, symExcPolicies, null, null, seUps, + new SymbolicExecutionTermLabelFactory(), null, symExcPolicies, seUps, seRefs, null)); if (predicateEvaluationEnabled) { ImmutableList predPolicies = @@ -190,7 +190,7 @@ public static ImmutableList getSymbolicExecutionTermLabe ImmutableList predRefs = ImmutableList .nil().prepend(new FormulaTermLabelRefactoring()); result = result.prepend(new TermLabelConfiguration(FormulaTermLabel.NAME, - new FormulaTermLabelFactory(), null, predPolicies, null, null, predUpdates, + new FormulaTermLabelFactory(), null, predPolicies, predUpdates, predRefs, new FormulaTermLabelMerger())); } return result; diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/ServiceCaches.java b/key.core/src/main/java/de/uka/ilkd/key/java/ServiceCaches.java index d8e7739c803..8e05563e558 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/ServiceCaches.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/ServiceCaches.java @@ -8,6 +8,7 @@ import java.util.WeakHashMap; import de.uka.ilkd.key.logic.JTerm; +import de.uka.ilkd.key.logic.StrictTermKey; import de.uka.ilkd.key.logic.label.OriginTermLabel.Origin; import de.uka.ilkd.key.proof.Node; import de.uka.ilkd.key.proof.PrefixTermTacletAppIndexCacheImpl.CacheKey; @@ -117,9 +118,11 @@ public class ServiceCaches implements SessionCaches { private final Map graphCache = new LRUCache<>(1000); /** - * Cache used by the TermFactory to avoid unnecessary creation of terms + * Cache used by the TermFactory to avoid unnecessary creation of terms. Keyed on + * {@link StrictTermKey} (label-sensitive equality) so that term labels are preserved and + * label variants are interned separately. */ - private final Map termCache = new LRUCache<>(20000); + private final Map termCache = new LRUCache<>(20000); /** * Cache used by TypeComparisonCondition @@ -162,9 +165,10 @@ public class ServiceCaches implements SessionCaches { /** * Cache used by {@link de.uka.ilkd.key.rule.label.OriginTermLabelRefactoring}: the * origins of a term and all its subterms. Terms are immutable, so the set never - * changes for a given term. + * changes for a given term. The key is label-sensitive ({@link StrictTermKey}) as the + * cached value is derived from the term's labels. */ - private final Map> subtermOriginsCache = new LRUCache<>(20000); + private final Map> subtermOriginsCache = new LRUCache<>(20000); /** @@ -182,7 +186,7 @@ public final Map getTermTacletAppIndexCache() { * * @return map from a term to the origins of the term and all its subterms */ - public final Map> getSubtermOriginsCache() { + public final Map> getSubtermOriginsCache() { return subtermOriginsCache; } @@ -214,7 +218,7 @@ public final Map getGraphCache() { return graphCache; } - public final Map getTermFactoryCache() { + public final Map getTermFactoryCache() { return termCache; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/logic/JTerm.java b/key.core/src/main/java/de/uka/ilkd/key/logic/JTerm.java index 621b2b335e7..b201ee7e433 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/logic/JTerm.java +++ b/key.core/src/main/java/de/uka/ilkd/key/logic/JTerm.java @@ -118,6 +118,36 @@ public interface JTerm */ boolean containsJavaBlockRecursive(); + /** + * Checks if this {@link JTerm} or one of its direct or indirect children carries a + * {@link TermLabel}. Cached; used as a cheap guard by label-sensitive consumers since + * {@link #equals(Object)} ignores term labels. + * + * @return {@code true} iff a term label occurs anywhere in the term tree + */ + boolean containsLabelsRecursive(); + + /** + * Checks term equality including {@link TermLabel}s on this term and all subterms + * (labels compared as sets). This is the label-sensitive counterpart of + * {@link #equals(Object)}. + * + * @param o the object to compare with + * @return {@code true} iff {@code o} is a term syntactically equal to this one with equal + * label sets on all subterms + */ + boolean equalsIncludingLabels(Object o); + + /** + * A hash code consistent with {@link #equalsIncludingLabels(Object)}: two terms that are + * equal including labels have the same labeled hash code. Cached. This is the label-sensitive + * counterpart of {@link #hashCode()} and is used to key the term-factory cache so that label + * variants are interned separately (and hence still shared via {@code ==}). + * + * @return the label-sensitive hash code + */ + int labeledHashCode(); + /** * Checks if this {@link JTerm} or one of its direct or indirect children has a * {@link de.uka.ilkd.key.logic.op.Transformer} operator. Cached; used by diff --git a/key.core/src/main/java/de/uka/ilkd/key/logic/LabeledTermImpl.java b/key.core/src/main/java/de/uka/ilkd/key/logic/LabeledTermImpl.java deleted file mode 100644 index 4f1dba590ea..00000000000 --- a/key.core/src/main/java/de/uka/ilkd/key/logic/LabeledTermImpl.java +++ /dev/null @@ -1,170 +0,0 @@ -/* This file is part of KeY - https://key-project.org - * KeY is licensed under the GNU General Public License Version 2 - * SPDX-License-Identifier: GPL-2.0-only */ -package de.uka.ilkd.key.logic; - -import java.util.Objects; -import java.util.stream.Collectors; - -import de.uka.ilkd.key.logic.equality.EqualsModProperty; -import de.uka.ilkd.key.logic.equality.RenamingTermProperty; -import de.uka.ilkd.key.logic.label.TermLabel; - -import org.key_project.logic.Name; -import org.key_project.logic.Property; -import org.key_project.logic.op.Operator; -import org.key_project.logic.op.QuantifiableVariable; -import org.key_project.util.collection.ImmutableArray; -import org.key_project.util.java.CollectionUtil; - -/** - *

- * The labeled term class is used for terms that have a label attached. - *

- * - * Two labeled terms are equal if they have equal term structure and equal annotations. In contrast, - * the method {@link EqualsModProperty#equalsModProperty(Object, Property, Object[])} can be used to - * compare terms - * while ignoring certain - * given properties. E.g. by using {@link RenamingTermProperty#RENAMING_TERM_PROPERTY}, just the - * term structures modulo - * renaming are compared whilst ignoring annotations. * - * - * @see JTerm - * @see TermImpl - */ -public class LabeledTermImpl extends TermImpl { - - /** - * @see #getLabels() - */ - private final ImmutableArray labels; - - /** - * creates an instance of a labeled term. - * - * @param op the top level operator - * @param subs the Term that are the subterms of this term - * @param boundVars logic variables bound by the operator - * @param labels the term's labels (must not be null or empty) - * @param origin a String with origin information - */ - public LabeledTermImpl(Operator op, ImmutableArray subs, - ImmutableArray boundVars, - ImmutableArray labels, String origin) { - super(op, subs, boundVars); - assert labels != null : "Term labels must not be null"; - assert !labels.isEmpty() : "There must be at least one term label"; - this.labels = labels; - } - - /** - * creates an instance of a labeled term. - * - * @param op the top level operator - * @param subs the Term that are the subterms of this term - * @param boundVars logic variables bound by the operator - * @param labels the terms labels (must not be null or empty) - */ - public LabeledTermImpl(Operator op, ImmutableArray subs, - ImmutableArray boundVars, - ImmutableArray labels) { - super(op, subs, boundVars); - assert labels != null : "Term labels must not be null"; - assert !labels.isEmpty() : "There must be at least one term label"; - this.labels = labels; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean hasLabels() { - return true; - } - - /** - * returns the labels attached to this term - */ - @Override - public ImmutableArray getLabels() { - return labels; - } - - @Override - public TermLabel getLabel(final Name termLabelName) { - return CollectionUtil.search(labels, - element -> Objects.equals(element.name(), termLabelName)); - } - - /** - * returns true if the given label is attached - * - * @param label the TermLabel for which to look (must not be null) - * @return true iff. the label is attached to this term - */ - @Override - public boolean containsLabel(TermLabel label) { - assert label != null : "Label must not be null"; - for (int i = 0, sz = labels.size(); i < sz; i++) { - if (label.equals(labels.get(i))) { - return true; - } - } - return false; - } - - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - - if (o instanceof final LabeledTermImpl cmp) { - if (labels.size() != cmp.labels.size()) { - return false; - } - - if (!super.equals(o)) { - return false; - } - - if (labels.size() == cmp.labels.size()) { - for (int i = 0, sz = labels.size(); i < sz; i++) { - // this is not optimal, but as long as number of labels limited ok - if (!cmp.labels.contains(labels.get(i))) { - return false; - } - } - return true; - } - } - return false; - } - - @Override - public int computeHashCode() { - int hash = super.computeHashCode(); - for (int i = 0, sz = labels.size(); i < sz; i++) { - hash += 7 * labels.get(i).hashCode(); - } - return hash; - } - - @Override - public String toString() { - StringBuilder result = new StringBuilder(super.toString()); - - String labelsStr = labels.stream() - // .filter(TermLabel::isProofRelevant) - .map(TermLabel::toString).collect(Collectors.joining(", ")); - - if (!labelsStr.isEmpty()) { - result.append("<<"); - result.append(labelsStr); - result.append(">>"); - } - - return result.toString(); - } -} diff --git a/key.core/src/main/java/de/uka/ilkd/key/logic/StrictTermKey.java b/key.core/src/main/java/de/uka/ilkd/key/logic/StrictTermKey.java new file mode 100644 index 00000000000..ebbf7d4cc00 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/logic/StrictTermKey.java @@ -0,0 +1,30 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.logic; + +/** + * A map key wrapping a {@link JTerm} with label-sensitive equality. Since + * {@link JTerm#equals(Object)} ignores term labels, caches whose values depend on the labels of + * the keyed term must not conflate label variants; wrapping the key in this record restores the + * distinction via {@link JTerm#equalsIncludingLabels(Object)}. + * + *

+ * The hash code is the term's {@link JTerm#labeledHashCode() label-sensitive} hash code, so label + * variants land in different buckets just as they did when term equality itself was + * label-sensitive. + *

+ * + * @param term the term to use as a label-sensitive key (must not be null) + */ +public record StrictTermKey(JTerm term) { + @Override + public boolean equals(Object o) { + return o instanceof StrictTermKey other && term.equalsIncludingLabels(other.term); + } + + @Override + public int hashCode() { + return term.labeledHashCode(); + } +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/logic/TermFactory.java b/key.core/src/main/java/de/uka/ilkd/key/logic/TermFactory.java index bdd83aacbe3..5a6d05c04a3 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/logic/TermFactory.java +++ b/key.core/src/main/java/de/uka/ilkd/key/logic/TermFactory.java @@ -29,7 +29,7 @@ public final class TermFactory { private static final ImmutableArray NO_SUBTERMS = new ImmutableArray<>(); - private final Map cache; + private final Map cache; // ------------------------------------------------------------------------- @@ -41,7 +41,7 @@ public TermFactory() { this.cache = null; } - public TermFactory(Map cache) { + public TermFactory(Map cache) { this.cache = cache; } @@ -119,22 +119,26 @@ private JTerm doCreateTerm(Operator op, ImmutableArray subs, ImmutableArray boundVars, ImmutableArray labels, String origin) { - final TermImpl newTerm = - (labels == null || labels.isEmpty() - ? new TermImpl(op, subs, boundVars) - : new LabeledTermImpl(op, subs, boundVars, labels, origin)); + final TermImpl newTerm = new TermImpl(op, subs, boundVars, labels); // Check if caching is possible. It is not possible if a non-empty JavaBlock is available // in the term or in one of its children because the meta information like PositionInfos // may be different. + // + // Labeled terms are cached too: the cache is keyed on StrictTermKey, i.e. label-sensitive + // equality, so a term is only shared with a structurally AND label identical one. This + // reproduces the interning behaviour from when term equality itself was label-sensitive + // (and keeps the == fast paths and memory sharing) while term.equals() stays + // label-agnostic for the logic layer. if (cache != null && !newTerm.containsJavaBlockRecursive()) { + final StrictTermKey key = new StrictTermKey(newTerm); JTerm term; synchronized (cache) { - term = cache.get(newTerm); + term = cache.get(key); } if (term == null) { term = newTerm.checked(); synchronized (cache) { - cache.put(term, term); + cache.put(key, term); } } return term; diff --git a/key.core/src/main/java/de/uka/ilkd/key/logic/TermImpl.java b/key.core/src/main/java/de/uka/ilkd/key/logic/TermImpl.java index 916f466ac31..73283f40677 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/logic/TermImpl.java +++ b/key.core/src/main/java/de/uka/ilkd/key/logic/TermImpl.java @@ -3,9 +3,11 @@ * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.logic; +import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; +import java.util.stream.Collectors; -import de.uka.ilkd.key.java.ast.PositionInfo; import de.uka.ilkd.key.logic.label.TermLabel; import de.uka.ilkd.key.logic.op.*; @@ -19,12 +21,14 @@ import org.key_project.util.collection.DefaultImmutableSet; import org.key_project.util.collection.ImmutableArray; import org.key_project.util.collection.ImmutableSet; +import org.key_project.util.java.CollectionUtil; import org.jspecify.annotations.NonNull; /** - * The currently only class implementing the Term interface. TermFactory should be the only class + * The only class implementing the {@link JTerm} interface. A term may carry {@link TermLabel}s; + * unlabeled terms share a single empty label array. {@link TermFactory} should be the only class * dealing directly with the TermImpl class. */ class TermImpl implements JTerm { @@ -54,34 +58,68 @@ class TermImpl implements JTerm { private final ImmutableArray subs; private final ImmutableArray boundVars; + /** + * The labels attached to this term, or the shared {@link #EMPTY_LABEL_LIST} if there are + * none. Never {@code null}. Term labels do not participate in {@link #equals(Object)} / + * {@link #hashCode()}; use {@link #equalsIncludingLabels(Object)} for a label-sensitive + * comparison. + */ + private final ImmutableArray labels; + // caches - private enum ThreeValuedTruth { - TRUE, FALSE, UNKNOWN - } private int depth = -1; - /** - * A cached value for computing the term's rigidness. - */ - private ThreeValuedTruth rigid = ThreeValuedTruth.UNKNOWN; private ImmutableSet freeVars = null; /** * Cached {@link #hashCode()} value. */ private int hashcode = -1; + /** + * Cached {@link #labeledHashCode()} value (label-sensitive); only used for terms that + * actually carry a label somewhere in their subtree. + */ + private int labeledHashcode = -1; + private Sort sort; /** - * This flag indicates that the {@link JTerm} itself or one of its children contains a non-empty - * {@link JavaBlock}. {@link JTerm}s which provides a {@link JavaBlock} directly or indirectly - * can't be cached because it is possible that the contained meta information inside the - * {@link JavaBlock}, e.g. {@link PositionInfo}s, are different. + * Packed lazily-computed tri-state predicates, two bits each (see {@link #FLAG_UNKNOWN} / + * {@link #FLAG_FALSE} / {@link #FLAG_TRUE}): rigidness, and whether this term or one of its + * children contains a {@link JavaBlock}, a {@link Transformer}, or a {@link TermLabel}. + * Written at most once per flag via {@link #FLAGS} CAS so that concurrent computation of + * different flags on the same (shared, immutable) term cannot lose updates. */ - private ThreeValuedTruth containsJavaBlockRecursive = ThreeValuedTruth.UNKNOWN; + private volatile int flags = 0; + + private static final AtomicIntegerFieldUpdater FLAGS = + AtomicIntegerFieldUpdater.newUpdater(TermImpl.class, "flags"); + + private static final int FLAG_UNKNOWN = 0; + private static final int FLAG_FALSE = 1; + private static final int FLAG_TRUE = 2; + private static final int FLAG_MASK = 3; - /** caches whether this term or a (direct/indirect) child has a {@link Transformer} operator. */ - private ThreeValuedTruth containsTransformerRecursive = ThreeValuedTruth.UNKNOWN; + private static final int SHIFT_RIGID = 0; + private static final int SHIFT_JAVA_BLOCK = 2; + private static final int SHIFT_TRANSFORMER = 4; + private static final int SHIFT_LABELS = 6; + + /** @return the two-bit state of the flag at {@code shift} */ + private int getFlag(int shift) { + return (flags >>> shift) & FLAG_MASK; + } + + /** Stores {@code value} into the flag at {@code shift}, unless it has meanwhile been set. */ + private void setFlag(int shift, int value) { + int prev; + do { + prev = flags; + if (((prev >>> shift) & FLAG_MASK) != FLAG_UNKNOWN) { + return; + } + } while (!FLAGS.compareAndSet(this, prev, prev | (value << shift))); + } // ------------------------------------------------------------------------- // constructors @@ -98,11 +136,25 @@ private enum ThreeValuedTruth { */ public TermImpl(Operator op, ImmutableArray subs, ImmutableArray boundVars) { + this(op, subs, boundVars, null); + } + + /** + * Constructs a term that may carry term labels. + * + * @param op the operator of the term + * @param subs the sub terms of the constructed term + * @param boundVars the bound variables (if applicable) + * @param labels the term's labels, or {@code null}/empty for an unlabeled term + */ + public TermImpl(Operator op, ImmutableArray subs, + ImmutableArray boundVars, ImmutableArray labels) { assert op != null; assert subs != null; this.op = op; this.subs = subs.isEmpty() ? EMPTY_TERM_LIST : subs; this.boundVars = boundVars == null ? EMPTY_VAR_LIST : boundVars; + this.labels = (labels == null || labels.isEmpty()) ? EMPTY_LABEL_LIST : labels; } private ImmutableSet determineFreeVars() { @@ -228,22 +280,21 @@ public int depth() { @Override public boolean isRigid() { - if (rigid == ThreeValuedTruth.UNKNOWN) { - if (!op.isRigid()) { - rigid = ThreeValuedTruth.FALSE; - } else { - ThreeValuedTruth localIsRigid = ThreeValuedTruth.TRUE; + int flag = getFlag(SHIFT_RIGID); + if (flag == FLAG_UNKNOWN) { + boolean result = op.isRigid(); + if (result) { for (int i = 0, n = arity(); i < n; i++) { if (!sub(i).isRigid()) { - localIsRigid = ThreeValuedTruth.FALSE; + result = false; break; } } - rigid = localIsRigid; } + flag = result ? FLAG_TRUE : FLAG_FALSE; + setFlag(SHIFT_RIGID, flag); } - - return rigid == ThreeValuedTruth.TRUE; + return flag == FLAG_TRUE; } @@ -281,7 +332,8 @@ public void execPreOrder(Visitor visitor) { } /** - * true iff o is syntactically equal to this term + * true iff o is syntactically equal to this term; {@link TermLabel}s are + * ignored. Use {@link #equalsIncludingLabels(Object)} for a label-sensitive comparison. */ @Override public boolean equals(Object o) { @@ -289,18 +341,72 @@ public boolean equals(Object o) { return true; } - if (o == null || o.getClass() != getClass() || hashCode() != o.hashCode()) { + if (!(o instanceof final TermImpl t) || hashCode() != o.hashCode()) { return false; } - final TermImpl t = (TermImpl) o; - - return op.equals(t.op) && t.hasLabels() == hasLabels() && subs.equals(t.subs) + return op.equals(t.op) && subs.equals(t.subs) && boundVars.equals(t.boundVars) // TODO (DD): below is no longer necessary && javaBlock().equals(t.javaBlock()); } + /** + * true iff o is syntactically equal to this term including all + * {@link TermLabel}s attached to this term or any subterm. Labels are compared as sets + * (order-insensitive). This is the label-sensitive counterpart of {@link #equals(Object)} + * needed by the few consumers which must distinguish label variants (e.g. the term + * factory cache and taclet index caches). + */ + @Override + public boolean equalsIncludingLabels(Object o) { + if (o == this) { + return true; + } + if (!equals(o)) { + return false; + } + final TermImpl t = (TermImpl) o; + if (!containsLabelsRecursive() && !t.containsLabelsRecursive()) { + return true; + } + return labelsEqualRecursive(this, t); + } + + /** + * Compares the {@link TermLabel}s of two structurally equal terms recursively. + * + * @param t1 a term + * @param t2 a term already known to be equal to {@code t1} modulo term labels + * @return true iff all (sub)terms carry equal label sets + */ + private static boolean labelsEqualRecursive(JTerm t1, JTerm t2) { + // interned (shared) subterms are reference-identical: prune the whole subtree. This is + // the common case, as the term factory interns terms label-sensitively. + if (t1 == t2) { + return true; + } + final ImmutableArray labels1 = t1.getLabels(); + final ImmutableArray labels2 = t2.getLabels(); + if (labels1.size() != labels2.size()) { + return false; + } + for (int i = 0, sz = labels1.size(); i < sz; i++) { + if (!labels2.contains(labels1.get(i))) { + return false; + } + } + if (!t1.containsLabelsRecursive() && !t2.containsLabelsRecursive()) { + return true; + } + for (int i = 0, ar = t1.arity(); i < ar; i++) { + if (!labelsEqualRecursive(t1.sub(i), t2.sub(i))) { + return false; + } + } + return true; + } + @Override public final int hashCode() { if (hashcode == -1) { @@ -327,6 +433,33 @@ protected int computeHashCode() { return hashcode; } + @Override + public int labeledHashCode() { + // no labels anywhere: the label-sensitive hash coincides with the plain one + if (!containsLabelsRecursive()) { + return hashCode(); + } + if (labeledHashcode == -1) { + this.labeledHashcode = computeLabeledHashCode(); + } + return labeledHashcode; + } + + /** {@link #hashCode()} refined by the labels of this term and all its subterms. */ + private int computeLabeledHashCode() { + int result = hashCode(); + for (int i = 0, sz = labels.size(); i < sz; i++) { + result = result * 17 + labels.get(i).hashCode(); + } + for (int i = 0, ar = arity(); i < ar; i++) { + result = result * 17 + sub(i).labeledHashCode(); + } + if (result == -1) { + result = 0; + } + return result; + } + @Override public boolean equalsModProperty(Object o, Property property, V... v) { if (!(o instanceof JTerm other)) { @@ -357,16 +490,22 @@ public String toString() { sb.append(op()).append("|{").append(javaBlock()).append("}| "); } sb.append("(").append(sub(0)).append(")"); - return sb.toString(); } else { sb.append(op().name()); if (!boundVars.isEmpty()) { sb.append(Strings.formatAsList(boundVars(), "{", ",", "}")); } - if (arity() == 0) { - return sb.toString(); + if (arity() != 0) { + sb.append(Strings.formatAsList(subs(), "(", ",", ")")); + } + } + + if (hasLabels()) { + final String labelsStr = + labels.stream().map(TermLabel::toString).collect(Collectors.joining(", ")); + if (!labelsStr.isEmpty()) { + sb.append("<<").append(labelsStr).append(">>"); } - sb.append(Strings.formatAsList(subs(), "(", ",", ")")); } return sb.toString(); @@ -380,22 +519,29 @@ public int serialNumber() { @Override public boolean hasLabels() { - return false; + return !labels.isEmpty(); } @Override public boolean containsLabel(TermLabel label) { + assert label != null : "Label must not be null"; + for (int i = 0, sz = labels.size(); i < sz; i++) { + if (label.equals(labels.get(i))) { + return true; + } + } return false; } @Override public TermLabel getLabel(Name termLabelName) { - return null; + return CollectionUtil.search(labels, + element -> Objects.equals(element.name(), termLabelName)); } @Override public ImmutableArray getLabels() { - return EMPTY_LABEL_LIST; + return labels; } /** @@ -403,40 +549,62 @@ public ImmutableArray getLabels() { */ @Override public boolean containsJavaBlockRecursive() { - if (containsJavaBlockRecursive == ThreeValuedTruth.UNKNOWN) { - ThreeValuedTruth result = ThreeValuedTruth.FALSE; - if (!javaBlock().isEmpty()) { - result = ThreeValuedTruth.TRUE; - } else { + int flag = getFlag(SHIFT_JAVA_BLOCK); + if (flag == FLAG_UNKNOWN) { + boolean result = !javaBlock().isEmpty(); + if (!result) { for (int i = 0, arity = subs.size(); i < arity; i++) { if (subs.get(i).containsJavaBlockRecursive()) { - result = ThreeValuedTruth.TRUE; + result = true; break; } } } - this.containsJavaBlockRecursive = result; + flag = result ? FLAG_TRUE : FLAG_FALSE; + setFlag(SHIFT_JAVA_BLOCK, flag); } - return containsJavaBlockRecursive == ThreeValuedTruth.TRUE; + return flag == FLAG_TRUE; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean containsLabelsRecursive() { + int flag = getFlag(SHIFT_LABELS); + if (flag == FLAG_UNKNOWN) { + boolean result = hasLabels(); + if (!result) { + for (int i = 0, arity = subs.size(); i < arity; i++) { + if (subs.get(i).containsLabelsRecursive()) { + result = true; + break; + } + } + } + flag = result ? FLAG_TRUE : FLAG_FALSE; + setFlag(SHIFT_LABELS, flag); + } + return flag == FLAG_TRUE; } @Override public boolean containsTransformerRecursive() { - if (containsTransformerRecursive == ThreeValuedTruth.UNKNOWN) { - ThreeValuedTruth result = ThreeValuedTruth.FALSE; - if (op instanceof Transformer) { - result = ThreeValuedTruth.TRUE; - } else { + int flag = getFlag(SHIFT_TRANSFORMER); + if (flag == FLAG_UNKNOWN) { + boolean result = op instanceof Transformer; + if (!result) { for (int i = 0, arity = subs.size(); i < arity; i++) { if (subs.get(i).containsTransformerRecursive()) { - result = ThreeValuedTruth.TRUE; + result = true; break; } } } - this.containsTransformerRecursive = result; + flag = result ? FLAG_TRUE : FLAG_FALSE; + setFlag(SHIFT_TRANSFORMER, flag); } - return containsTransformerRecursive == ThreeValuedTruth.TRUE; + return flag == FLAG_TRUE; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/logic/equality/IrrelevantTermLabelsProperty.java b/key.core/src/main/java/de/uka/ilkd/key/logic/equality/IrrelevantTermLabelsProperty.java index 30b0b4a067c..71eda7f01f0 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/logic/equality/IrrelevantTermLabelsProperty.java +++ b/key.core/src/main/java/de/uka/ilkd/key/logic/equality/IrrelevantTermLabelsProperty.java @@ -13,7 +13,17 @@ /** * A property that can be used in * {@link EqualsModProperty#equalsModProperty(Object, Property, Object[])} for terms. - * All irrelevant term labels are ignored in this equality check. + * All irrelevant term labels are ignored in this equality check, while labels that are + * {@link TermLabel#isProofRelevant() proof relevant} still count. + *

+ * This is the middle one of the three label comparison modes: plain + * {@link de.uka.ilkd.key.logic.JTerm#equals(Object)} ignores all term labels, this + * property ignores only the non-proof-relevant (cosmetic) ones, and + * {@link de.uka.ilkd.key.logic.JTerm#equalsIncludingLabels(Object)} respects all labels. Use this + * property when a comparison must distinguish, e.g., an {@code anonHeapFunction}-labeled term + * from an unlabeled one, but must not be affected by origin labels. It deliberately lives in a + * {@link Property} (and not on the term) because it needs label-kind knowledge + * ({@code isProofRelevant()}). *

* The single instance of this property can be accessed through * {@link IrrelevantTermLabelsProperty#IRRELEVANT_TERM_LABELS_PROPERTY}. diff --git a/key.core/src/main/java/de/uka/ilkd/key/logic/equality/TermLabelsProperty.java b/key.core/src/main/java/de/uka/ilkd/key/logic/equality/TermLabelsProperty.java index 0e29bf80cb6..909ab1c9e0f 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/logic/equality/TermLabelsProperty.java +++ b/key.core/src/main/java/de/uka/ilkd/key/logic/equality/TermLabelsProperty.java @@ -14,6 +14,13 @@ * {@link EqualsModProperty#equalsModProperty(Object, Property, Object[])} for terms. * All term labels are ignored in this equality check. *

+ * Note: since the standard term equality {@link Object#equals(Object)} ignores all term + * labels as well, this property is equivalent to plain {@code equals} for + * {@link de.uka.ilkd.key.logic.JTerm}s — prefer plain {@code equals} in new code. The property is + * kept for the generic {@link org.key_project.logic.Term} level and for symmetry with + * {@link IrrelevantTermLabelsProperty} (which is not equivalent to {@code equals}: it + * keeps proof-relevant labels significant). + *

* The single instance of this property can be accessed through * {@link TermLabelsProperty#TERM_LABELS_PROPERTY}. * diff --git a/key.core/src/main/java/de/uka/ilkd/key/logic/label/TermLabel.java b/key.core/src/main/java/de/uka/ilkd/key/logic/label/TermLabel.java index 7f57d8d7903..e27fd6c8fc3 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/logic/label/TermLabel.java +++ b/key.core/src/main/java/de/uka/ilkd/key/logic/label/TermLabel.java @@ -9,7 +9,6 @@ import de.uka.ilkd.key.logic.op.JModality; import de.uka.ilkd.key.proof.init.Profile; import de.uka.ilkd.key.rule.BuiltInRule; -import de.uka.ilkd.key.rule.label.ChildTermLabelPolicy; import de.uka.ilkd.key.rule.label.TermLabelMerger; import de.uka.ilkd.key.rule.label.TermLabelPolicy; import de.uka.ilkd.key.rule.label.TermLabelRefactoring; @@ -49,21 +48,31 @@ * {@link TermLabelManager} provides also the functionality to parse and maintain them during prove. *

*

- * The {@link TermLabelManager} is responsible during prove to maintain term labels. - * This means that labels of new {@link JTerm}s created during rule application are computed - * via {@link TermLabelManager#instantiateLabels} - * and of existing {@link JTerm}s are refactored (added or removed) via - * {@link TermLabelManager#instantiateLabels}. + * The {@link TermLabelManager} is responsible for maintaining term labels during proof + * construction. This means that labels of new {@link JTerm}s created during rule application are + * computed via {@link TermLabelManager#instantiateLabels} and labels of existing {@link JTerm}s + * are refactored (added or removed) via the refactoring methods of {@link TermLabelManager}. *

*

- * Antecedent and succedent of a {@link Sequent} are sets. The equality check if a - * {@link SequentFormula} is already contained ignores {@link TermLabel}s. To ensure that - * {@link TermLabel}s are not lost, + * Equality: since term labels are not soundness relevant, the standard term equality + * {@link JTerm#equals(Object)} (and {@link JTerm#hashCode()}) ignores all term labels: + * two terms differing only in labels are the same formula. Code that must distinguish label + * variants has two options: {@link JTerm#equalsIncludingLabels(Object)} together with + * {@link JTerm#labeledHashCode()} respects all labels (used for term interning and index caches, + * see {@link de.uka.ilkd.key.logic.StrictTermKey}), while + * {@code equalsModProperty(t, IRRELEVANT_TERM_LABELS_PROPERTY)} ignores only labels that are not + * {@link #isProofRelevant() proof relevant}. + *

+ *

+ * Antecedent and succedent of a {@link Sequent} are sets. The redundancy check whether a + * {@link SequentFormula} is already contained ignores {@link TermLabel}s (consistent with the + * standard term equality). To ensure that {@link TermLabel}s are not lost when a formula is + * rejected as redundant, * {@link TermLabelManager#mergeLabels(de.uka.ilkd.key.java.Services, * de.uka.ilkd.key.logic.SequentChangeInfo)} * merges the labels of the existing {@link SequentFormula} with those of the rejected * {@link SequentFormula}. How this is done in detail is implemented by a {@link TermLabelMerger}. - * If no {@link TermLabelMerger} is available, the {@link TermLabel} of the rejected + * If no {@link TermLabelMerger} is available, the {@link TermLabel}s of the rejected * {@link SequentFormula} are lost. *

*

@@ -108,15 +117,6 @@ *

  • {@code 2 + 3 ~~> 5<>a>>}: A new label has to be added which is not provided by the * rule. Implement a {@link TermLabelUpdate} which adds, sorts or removes * {@link TermLabel} before a new {@link JTerm} is created.
  • - *
  • {@code 2<> + 3<> ~~> 5<>}: A direct child of the application {@link JTerm} - * {@code a} contains the label before. Use a direct {@link ChildTermLabelPolicy} to - * ensure that it is added also to the new term.
  • - *
  • {@code 2 + (3<> - 1<>) ~~> 4<>}: A child or grandchild of the application - * {@link JTerm} {@code a} contains the label before. Use a direct - * {@link ChildTermLabelPolicy} to ensure that it is added also to the new term.
  • - *
  • {@code 2<> + 3<> ~~> 2<> - 3}: Implement a {@link TermLabelRefactoring} - * which works on {@link RefactoringScope#APPLICATION_DIRECT_CHILDREN} to freely add or - * remove {@link TermLabel}s on direct children of the application {@link JTerm}.
  • *
  • {@code 2 + (3<> - 1<>) ~~> 2 * (3<> - 1)}: Implement a * {@link TermLabelRefactoring} which works on * {@link RefactoringScope#APPLICATION_CHILDREN_AND_GRANDCHILDREN_SUBTREE} to freely diff --git a/key.core/src/main/java/de/uka/ilkd/key/logic/label/TermLabelContext.java b/key.core/src/main/java/de/uka/ilkd/key/logic/label/TermLabelContext.java new file mode 100644 index 00000000000..2c02059ea31 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/logic/label/TermLabelContext.java @@ -0,0 +1,54 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.logic.label; + +import de.uka.ilkd.key.java.Services; +import de.uka.ilkd.key.logic.JTerm; +import de.uka.ilkd.key.proof.Goal; + +import org.key_project.prover.rules.Rule; +import org.key_project.prover.rules.RuleApp; +import org.key_project.prover.sequent.PosInOccurrence; +import org.key_project.prover.sequent.Sequent; + +/** + * The rule application for which term labels are computed or refactored. One instance is created + * per {@link TermLabelManager} entry point invocation and passed unchanged to all term label + * hooks ({@link de.uka.ilkd.key.rule.label.TermLabelPolicy}, + * {@link de.uka.ilkd.key.rule.label.TermLabelUpdate}, + * {@link de.uka.ilkd.key.rule.label.TermLabelRefactoring}); the hooks receive their varying + * payload (the term being created or refactored, the label set) as separate parameters. + * + * @param state the {@link TermLabelState} of the current rule application (mutable scratchpad + * shared by all hook invocations of one rule application) + * @param services the {@link Services} of the proof the rule is applied in + * @param applicationPosInOccurrence the {@link PosInOccurrence} in the previous {@link Sequent} + * which defines the term that is rewritten, or {@code null} if not available (e.g. proof + * obligation creation) + * @param applicationTerm the term defined by {@code applicationPosInOccurrence}, or {@code null} + * @param modalityTerm the modality term below the updates of {@code applicationTerm} + * (see {@code TermBuilder.goBelowUpdates}), or {@code null} where it was not computed + * (refactoring entry points; no modality hooks registered) + * @param rule the {@link Rule} which is applied, or {@code null} + * @param ruleApp the {@link RuleApp} which is currently performed, or {@code null} where the + * entry point does not receive it (refactoring entry points) + * @param goal the optional {@link Goal} on which the new or refactored term will be used, or + * {@code null} + * @param hint an optional hint passed from the active rule to describe the term which should be + * created, or {@code null} + * @param tacletTerm the taclet term which is responsible for the new term, or {@code null} in + * case of built-in rules + */ +public record TermLabelContext( + TermLabelState state, + Services services, + PosInOccurrence applicationPosInOccurrence, + JTerm applicationTerm, + JTerm modalityTerm, + Rule rule, + RuleApp ruleApp, + Goal goal, + Object hint, + JTerm tacletTerm) { +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/logic/label/TermLabelManager.java b/key.core/src/main/java/de/uka/ilkd/key/logic/label/TermLabelManager.java index ffa332c3164..c72ca97c164 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/logic/label/TermLabelManager.java +++ b/key.core/src/main/java/de/uka/ilkd/key/logic/label/TermLabelManager.java @@ -4,7 +4,6 @@ package de.uka.ilkd.key.logic.label; import java.util.*; -import java.util.Map.Entry; import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.logic.*; @@ -57,9 +56,16 @@ *
  • * *

    + * The labels of a new {@link JTerm} are computed by {@link #instantiateLabels} in a fixed order + * which is part of the contract: taclet-provided labels, then the application-term + * {@link TermLabelPolicy} instances, then the modality-term {@link TermLabelPolicy} instances, + * then the rule-specific {@link TermLabelUpdate}s and finally the rule-independent + * {@link TermLabelUpdate}s. {@link TermLabelRefactoring}s run afterwards on the surrounding + * terms, with the scope they request per rule application. + *

    *

    - * For more information about {@link TermLabel}s and how they are maintained during prove read the - * documentation of interface {@link TermLabel}. + * For more information about {@link TermLabel}s and how they are maintained during proof + * construction read the documentation of interface {@link TermLabel}. *

    * * @author Mattias Ulbrich @@ -85,30 +91,6 @@ public class TermLabelManager { private final Map modalityTermPolicyMap = new LinkedHashMap<>(); - /** - * All rule specific direct {@link ChildTermLabelPolicy}s. - */ - private final Map> ruleSpecificDirectChildTermLabelPolicies = - new LinkedHashMap<>(); - - /** - * All rule independent direct {@link ChildTermLabelPolicy}s. - */ - private final Map allRulesDirectChildTermLabelPolicies = - new LinkedHashMap<>(); - - /** - * All rule specific child and grandchild {@link ChildTermLabelPolicy}s. - */ - private final Map> ruleSpecificChildAndGrandchildTermLabelPolicies = - new LinkedHashMap<>(); - - /** - * All rule independent child and grandchild {@link ChildTermLabelPolicy}s. - */ - private final Map allRulesChildAndGrandchildTermLabelPolicies = - new LinkedHashMap<>(); - /** * All rule independent {@link TermLabelUpdate}s. */ @@ -157,13 +139,6 @@ public TermLabelManager(ImmutableList configurations) { applicationTermPolicyMap); analyzeTermPolicies(conf.getTermLabelName(), conf.getModalityTermPolicies(), modalityTermPolicyMap); - analyzeChildTermPolicies(conf.getTermLabelName(), - conf.getDirectChildTermLabelPolicies(), allRulesDirectChildTermLabelPolicies, - ruleSpecificDirectChildTermLabelPolicies); - analyzeChildTermPolicies(conf.getTermLabelName(), - conf.getChildAndGrandchildTermLabelPolicies(), - allRulesChildAndGrandchildTermLabelPolicies, - ruleSpecificChildAndGrandchildTermLabelPolicies); analyzeUpdates(conf.getTermLabelUpdates()); analyzeRefactorings(conf.getTermLabelRefactorings()); analyzeMerger(conf.getTermLabelName(), conf.getTermLabelMerger()); @@ -204,42 +179,6 @@ private void analyzeTermPolicies(Name termLabelName, ImmutableList - * Analyzes the given {@link ChildTermLabelPolicy} and adds it to the given policy {@link Map}s. - *

    - *

    - * This is a helper {@link Map} of {@link #TermLabelManager(ImmutableList)}. - *

    - * - * @param termLabelName The name of the supported {@link TermLabel}. - * @param policies The {@link ChildTermLabelPolicy} instances to analyze. - * @param allRulesPolicyMap The policy {@link Map} with all rules to update. - * @param ruleSpecificPolicyMap The rule specific policy {@link Map} to update. - */ - private void analyzeChildTermPolicies(Name termLabelName, - ImmutableList policies, - Map allRulesPolicyMap, - Map> ruleSpecificPolicyMap) { - if (policies != null) { - for (ChildTermLabelPolicy policy : policies) { - ImmutableList supportedRules = policy.getSupportedRuleNames(); - if (supportedRules == null || supportedRules.isEmpty()) { - allRulesPolicyMap.put(termLabelName, policy); - } else { - for (Name rule : supportedRules) { - Map ruleMap = ruleSpecificPolicyMap.get(rule); - if (ruleMap == null) { - ruleMap = new LinkedHashMap<>(); - ruleSpecificPolicyMap.put(rule, ruleMap); - } - ruleMap.put(termLabelName, policy); - } - } - } - } - } - /** *

    * Analyzes the given {@link TermLabelUpdate} and updates {@link #allRulesUpdates} and @@ -573,54 +512,19 @@ public static ImmutableArray instantiateLabels(TermLabelState state, /** * Do application term specific stuff. * - * @param state The {@link TermLabelState} of the current rule application. - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param rule The {@link Rule} which is applied. - * @param goal The optional {@link Goal} on which the {@link JTerm} to create will be used. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional {@link JTerm} in the taclet which is responsible to - * instantiate - * the new {@link JTerm} for the new proof node or {@code null} in case of built in - * rules. + * @param context the {@link TermLabelContext} of the current rule application * @param newTerm the template for the new {@link JTerm} to create * @param newLabels The set accumulating the {@link TermLabel}s to add to the new {@link JTerm} * which should be created. */ - private void addLabelsBasedOnApplicationTerm(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm, JTerm newTerm, Set newLabels) { - if (applicationTerm == null) { + private void addLabelsBasedOnApplicationTerm(TermLabelContext context, JTerm newTerm, + Set newLabels) { + if (context.applicationTerm() == null) { return; } // Re-add exiting application term labels based on application term policies. - performTermLabelPolicies(state, services, applicationPosInOccurrence, applicationTerm, rule, - goal, hint, tacletTerm, newTerm, applicationTermPolicyMap, newLabels); - // Add labels from direct child term policies. - Map activeDirectChildPolicies = - computeActiveChildPolicies(services, applicationPosInOccurrence, applicationTerm, rule, - goal, hint, tacletTerm, newTerm, ruleSpecificDirectChildTermLabelPolicies, - allRulesDirectChildTermLabelPolicies); - if (!activeDirectChildPolicies.isEmpty()) { - performDirectChildPolicies(services, applicationPosInOccurrence, applicationTerm, rule, - goal, hint, tacletTerm, newTerm, activeDirectChildPolicies, newLabels); - } - - // Add labels from child and grandchild term policies. - Map activeChildAndGrandchildPolicies = - computeActiveChildPolicies(services, applicationPosInOccurrence, applicationTerm, rule, - goal, hint, tacletTerm, newTerm, ruleSpecificChildAndGrandchildTermLabelPolicies, - allRulesChildAndGrandchildTermLabelPolicies); - if (!activeChildAndGrandchildPolicies.isEmpty()) { - performChildAndGrandchildPolicies(services, applicationPosInOccurrence, applicationTerm, - rule, goal, hint, tacletTerm, newTerm, activeChildAndGrandchildPolicies, newLabels); - } + performTermLabelPolicies(context, context.applicationTerm(), newTerm, + applicationTermPolicyMap, newLabels); } /** @@ -672,29 +576,28 @@ public ImmutableArray instantiateLabels(TermLabelState state, Service || !allRulesUpdates.isEmpty() || currentRuleSpecificUpdates != null) ? TermBuilder.goBelowUpdates(applicationTerm) : null; + final TermLabelContext context = new TermLabelContext(state, services, + applicationPosInOccurrence, applicationTerm, modalityTerm, rule, ruleApp, goal, hint, + tacletTerm); // Instantiate empty result Set newLabels = new LinkedHashSet<>(); // Add labels from taclet if (tacletTerm != null && tacletTerm.hasLabels()) { performTacletTerm(tacletTerm, newLabels); } - addLabelsBasedOnApplicationTerm(state, services, applicationPosInOccurrence, - applicationTerm, rule, goal, hint, tacletTerm, newTerm, newLabels); + addLabelsBasedOnApplicationTerm(context, newTerm, newLabels); // Re-add exiting modality term labels based on symbolic execution term policies. if (modalityTerm != null) { - performTermLabelPolicies(state, services, applicationPosInOccurrence, modalityTerm, - rule, goal, hint, tacletTerm, newTerm, modalityTermPolicyMap, newLabels); + performTermLabelPolicies(context, modalityTerm, newTerm, modalityTermPolicyMap, + newLabels); } // Allow rule specific updater to remove and add labels if (currentRuleSpecificUpdates != null) { - performUpdater(state, services, applicationPosInOccurrence, applicationTerm, - modalityTerm, rule, ruleApp, hint, tacletTerm, newTerm, currentRuleSpecificUpdates, - newLabels); + performUpdater(context, newTerm, currentRuleSpecificUpdates, newLabels); } // Allow all rule updater to remove and add labels if (!allRulesUpdates.isEmpty()) { - performUpdater(state, services, applicationPosInOccurrence, applicationTerm, - modalityTerm, rule, ruleApp, hint, tacletTerm, newTerm, allRulesUpdates, newLabels); + performUpdater(context, newTerm, allRulesUpdates, newLabels); } // Return result return new ImmutableArray<>(newLabels.toArray(new TermLabel[0])); @@ -730,273 +633,46 @@ protected void performTacletTerm(JTerm tacletTerm, Set newLabels) { * {@link #instantiateLabels(TermLabelState, Services, PosInOccurrence, Rule, RuleApp, Goal, Object, JTerm, JTerm)} *

    * - * @param state The {@link TermLabelState} of the current rule application. - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param rule The {@link Rule} which is applied. - * @param goal The optional {@link Goal} on which the {@link JTerm} to create will be used. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional {@link JTerm} in the taclet which is responsible to - * instantiate - * the new {@link JTerm} for the new proof node or {@code null} in case of built in - * rules. - * @param newTerm the template for the new {@link JTerm} to create - * @param policies The {@link TermLabelPolicy} instances to perform. - * @param newLabels The result {@link Set} with the {@link TermLabel}s of the new {@link JTerm}. - */ - protected void performTermLabelPolicies(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm, JTerm newTerm, Map policies, - Set newLabels) { - if (applicationTerm.hasLabels() && !policies.isEmpty()) { - for (TermLabel label : applicationTerm.getLabels()) { - performTermLabelPolicies(state, services, applicationPosInOccurrence, - applicationTerm, rule, goal, hint, tacletTerm, newTerm, policies, newLabels, - label); - } - } - } - - /** - *

    - * Performs the given {@link TermLabelPolicy} instances. - *

    - *

    - * This is a helper method of - * {@link #performTermLabelPolicies(TermLabelState, Services, PosInOccurrence, JTerm, Rule, Goal, Object, JTerm, JTerm, Map, Set)} - *

    - * - * @param state The {@link TermLabelState} of the current rule application. - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param rule The {@link Rule} which is applied. - * @param goal The optional {@link Goal} on which the {@link JTerm} to create will be used. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional {@link JTerm} in the taclet which is responsible to - * instantiate - * the new {@link JTerm} for the new proof node or {@code null} in case of built in - * rules. + * @param context the {@link TermLabelContext} of the current rule application + * @param sourceTerm the {@link JTerm} whose labels are under consideration (application or + * modality term) * @param newTerm the template for the new {@link JTerm} to create * @param policies The {@link TermLabelPolicy} instances to perform. * @param newLabels The result {@link Set} with the {@link TermLabel}s of the new {@link JTerm}. - * @param label The current {@link TermLabel} to ask its {@link TermLabelPolicy}. */ - protected void performTermLabelPolicies(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm, JTerm newTerm, Map policies, - Set newLabels, TermLabel label) { - TermLabelPolicy policy = policies.get(label.name()); - if (policy != null) { - label = policy.keepLabel(state, services, applicationPosInOccurrence, applicationTerm, - rule, goal, hint, tacletTerm, newTerm, label); - if (label != null) { - newLabels.add(label); - } - } - } - - /** - *

    - * Computes active {@link ChildTermLabelPolicy} instances which have to be executed during the - * given rule application. - *

    - *

    - * This is a helper {@link Map} of - * {@link #instantiateLabels(TermLabelState, Services, PosInOccurrence, Rule, RuleApp, Goal, Object, JTerm, JTerm)} - *

    - * - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param rule The {@link Rule} which is applied. - * @param goal The optional {@link Goal} on which the {@link JTerm} to create will be used. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional {@link JTerm} in the taclet which is responsible to - * instantiate - * the new {@link JTerm} for the new proof node or {@code null} in case of built in - * rules. - * @param newTerm the template for the new {@link JTerm} to create - * @param ruleSpecificPolicies Rule specific {@link ChildTermLabelPolicy} instances. - * @param ruleIndependentPolicies All rules {@link ChildTermLabelPolicy} instances. - * @return The active {@link ChildTermLabelPolicy} which have to be performed. - */ - protected Map computeActiveChildPolicies(TermServices services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm, JTerm newTerm, - Map> ruleSpecificPolicies, - Map ruleIndependentPolicies) { - Map activeDirectChildPolicies = - new LinkedHashMap<>(); - if (rule != null) { - Map rulePolicies = ruleSpecificPolicies.get(rule.name()); - if (rulePolicies != null) { - for (Entry entry : rulePolicies.entrySet()) { - if (entry.getValue().isRuleApplicationSupported(services, - applicationPosInOccurrence, applicationTerm, rule, goal, hint, tacletTerm, - newTerm)) { - activeDirectChildPolicies.put(entry.getKey(), entry.getValue()); + protected void performTermLabelPolicies(TermLabelContext context, JTerm sourceTerm, + JTerm newTerm, Map policies, Set newLabels) { + if (sourceTerm.hasLabels() && !policies.isEmpty()) { + for (TermLabel label : sourceTerm.getLabels()) { + TermLabelPolicy policy = policies.get(label.name()); + if (policy != null) { + label = policy.keepLabel(context, sourceTerm, newTerm, label); + if (label != null) { + newLabels.add(label); } } } } - if (!ruleIndependentPolicies.isEmpty()) { - for (Entry entry : ruleIndependentPolicies.entrySet()) { - if (entry.getValue().isRuleApplicationSupported(services, - applicationPosInOccurrence, applicationTerm, rule, goal, hint, tacletTerm, - newTerm)) { - activeDirectChildPolicies.put(entry.getKey(), entry.getValue()); - } - } - } - return activeDirectChildPolicies; - } - - /** - *

    - * Performs the given direct {@link ChildTermLabelPolicy} instances. - *

    - *

    - * This is a helper {@link Map} of - * {@link #instantiateLabels(TermLabelState, Services, PosInOccurrence, Rule, RuleApp, Goal, Object, JTerm, JTerm)} - *

    - * - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param rule The {@link Rule} which is applied. - * @param goal The optional {@link Goal} on which the {@link JTerm} to create will be used. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional {@link JTerm} in the taclet which is responsible to - * instantiate - * the new {@link JTerm} for the new proof node or {@code null} in case of built in - * rules. - * @param newTerm the template for the new {@link JTerm} to create - * @param policies The {@link ChildTermLabelPolicy} instances to perform. - * @param newLabels The result {@link Set} with the {@link TermLabel}s of the new {@link JTerm}. - */ - protected void performDirectChildPolicies(TermServices services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm, JTerm newTerm, - Map policies, Set newLabels) { - for (JTerm child : applicationTerm.subs()) { - for (TermLabel label : child.getLabels()) { - ChildTermLabelPolicy policy = policies.get(label.name()); - if (policy != null && policy.addLabel(services, applicationPosInOccurrence, - applicationTerm, rule, goal, hint, tacletTerm, newTerm, child, label)) { - newLabels.add(label); - } - } - } } /** *

    - * Performs the given child and grandchild {@link ChildTermLabelPolicy} instances. + * Performs the given {@link TermLabelUpdate} instances. *

    *

    - * This is a helper {@link Map} of + * This is a helper method of * {@link #instantiateLabels(TermLabelState, Services, PosInOccurrence, Rule, RuleApp, Goal, Object, JTerm, JTerm)} *

    * - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param rule The {@link Rule} which is applied. - * @param goal The optional {@link Goal} on which the {@link JTerm} to create will be used. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional {@link JTerm} in the taclet which is responsible to - * instantiate - * the new {@link JTerm} for the new proof node or {@code null} in case of built in - * rules. + * @param context the {@link TermLabelContext} of the current rule application * @param newTerm the template for the new {@link JTerm} to create - * @param policies The {@link ChildTermLabelPolicy} instances to perform. + * @param updater The {@link TermLabelUpdate} instances to perform. * @param newLabels The result {@link Set} with the {@link TermLabel}s of the new {@link JTerm}. */ - protected void performChildAndGrandchildPolicies(final TermServices services, - final PosInOccurrence applicationPosInOccurrence, - final JTerm applicationTerm, - final Rule rule, final Goal goal, final Object hint, final JTerm tacletTerm, - final JTerm newTerm, final Map policies, - final Set newLabels) { - applicationTerm.execPreOrder((DefaultVisitor) p_visited -> { - final JTerm visited = (JTerm) p_visited; - if (visited != applicationTerm) { - for (TermLabel label : visited.getLabels()) { - ChildTermLabelPolicy policy = policies.get(label.name()); - if (policy != null && policy.addLabel(services, applicationPosInOccurrence, - applicationTerm, rule, goal, hint, tacletTerm, newTerm, visited, - label)) { - newLabels.add(label); - } - } - } - }); - } - - /** - *

    - * Performs the given child and grandchild {@link TermLabelUpdate} instances. - *

    - *

    - * This is a helper {@link Map} of - * {@link #instantiateLabels(TermLabelState, Services, PosInOccurrence, Rule, RuleApp, Goal, Object, JTerm, JTerm)} - *

    - * - * @param state The {@link TermLabelState} of the current rule application. - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param modalityTerm The optional modality {@link JTerm}. - * @param rule The {@link Rule} which is applied. - * @param ruleApp The {@link RuleApp} which is currently performed. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional {@link JTerm} in the taclet which is responsible to - * instantiate - * the new {@link JTerm} for the new proof node or {@code null} in case of built in - * rules. - * @param newTerm the template for the new {@link JTerm} to create - * @param newLabels The result {@link Set} with the {@link TermLabel}s of the new {@link JTerm}. - */ - protected void performUpdater(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, JTerm modalityTerm, - Rule rule, RuleApp ruleApp, Object hint, JTerm tacletTerm, - JTerm newTerm, - ImmutableList updater, - Set newLabels) { + protected void performUpdater(TermLabelContext context, JTerm newTerm, + ImmutableList updater, Set newLabels) { for (TermLabelUpdate update : updater) { - update.updateLabels(state, services, applicationPosInOccurrence, applicationTerm, - modalityTerm, rule, ruleApp, hint, tacletTerm, newTerm, newLabels); + update.updateLabels(context, newTerm, newLabels); } } @@ -1053,23 +729,21 @@ public JTerm refactorSequentFormula(TermLabelState state, Services services, JTerm tacletTerm) { final PosInTerm pos = applicationPosInOccurrence.posInTerm(); final JTerm oldTerm = (JTerm) pos.getSubTerm(sequentFormula); + final TermLabelContext context = new TermLabelContext(state, services, + applicationPosInOccurrence, oldTerm, null, rule, null, goal, hint, tacletTerm); // Compute active refactorings - RefactoringsContainer refactorings = computeRefactorings(state, services, - applicationPosInOccurrence, oldTerm, rule, goal, hint, tacletTerm); + RefactoringsContainer refactorings = computeRefactorings(context); // Perform refactoring JTerm newTerm = - refactorApplicationTerm(state, services, applicationPosInOccurrence, oldTerm, - rule, goal, hint, tacletTerm, refactorings, services.getTermFactory()); - if (newTerm != null && !newTerm.equals(oldTerm)) { - return replaceTerm(state, applicationPosInOccurrence, newTerm, + refactorApplicationTerm(context, refactorings, services.getTermFactory()); + if (newTerm != null && !newTerm.equalsIncludingLabels(oldTerm)) { + return replaceTerm(context, applicationPosInOccurrence, newTerm, services.getTermFactory(), - refactorings.childAndGrandchildRefactoringsAndParents(), services, - applicationPosInOccurrence, oldTerm, rule, goal, hint, tacletTerm); + refactorings.childAndGrandchildRefactoringsAndParents()); } else if (!refactorings.childAndGrandchildRefactoringsAndParents().isEmpty()) { - return replaceTerm(state, applicationPosInOccurrence, oldTerm, + return replaceTerm(context, applicationPosInOccurrence, oldTerm, services.getTermFactory(), - refactorings.childAndGrandchildRefactoringsAndParents(), services, - applicationPosInOccurrence, oldTerm, rule, goal, hint, tacletTerm); + refactorings.childAndGrandchildRefactoringsAndParents()); } else { return sequentFormula; } @@ -1131,13 +805,13 @@ public JTerm refactorTerm(TermLabelState state, Services services, PosInOccurrence applicationPosInOccurrence, JTerm applicationTerm, Goal goal, Object hint, Rule rule, JTerm tacletTerm) { + final TermLabelContext context = new TermLabelContext(state, services, + applicationPosInOccurrence, applicationTerm, null, rule, null, goal, hint, tacletTerm); // Compute active refactorings - RefactoringsContainer refactorings = computeRefactorings(state, services, - applicationPosInOccurrence, applicationTerm, rule, goal, hint, tacletTerm); + RefactoringsContainer refactorings = computeRefactorings(context); // Refactor application term JTerm newApplicationTerm = - refactorApplicationTerm(state, services, applicationPosInOccurrence, applicationTerm, - rule, goal, hint, tacletTerm, refactorings, services.getTermFactory()); + refactorApplicationTerm(context, refactorings, services.getTermFactory()); return newApplicationTerm != null ? newApplicationTerm : applicationTerm; } @@ -1203,33 +877,29 @@ public void refactorGoal(TermLabelState state, Services services, PosInOccurrence applicationPosInOccurrence, JTerm applicationTerm, Rule rule, Goal goal, Object hint, JTerm tacletTerm) { + final TermLabelContext context = new TermLabelContext(state, services, + applicationPosInOccurrence, applicationTerm, null, rule, null, goal, hint, tacletTerm); // Compute active refactorings - RefactoringsContainer refactorings = computeRefactorings(state, services, - applicationPosInOccurrence, applicationTerm, rule, goal, hint, tacletTerm); + RefactoringsContainer refactorings = computeRefactorings(context); // Refactor application term final TermFactory tf = services.getTermFactory(); - JTerm newApplicationTerm = - refactorApplicationTerm(state, services, applicationPosInOccurrence, applicationTerm, - rule, goal, hint, tacletTerm, refactorings, tf); - if (newApplicationTerm != null && !newApplicationTerm.equals(applicationTerm)) { - JTerm root = replaceTerm(state, applicationPosInOccurrence, newApplicationTerm, tf, - refactorings.childAndGrandchildRefactoringsAndParents(), services, - applicationPosInOccurrence, newApplicationTerm, rule, goal, hint, tacletTerm); + JTerm newApplicationTerm = refactorApplicationTerm(context, refactorings, tf); + if (newApplicationTerm != null + && !newApplicationTerm.equalsIncludingLabels(applicationTerm)) { + JTerm root = replaceTerm(context, applicationPosInOccurrence, newApplicationTerm, tf, + refactorings.childAndGrandchildRefactoringsAndParents()); goal.changeFormula(new SequentFormula(root), applicationPosInOccurrence.topLevel()); } else if (!refactorings.childAndGrandchildRefactoringsAndParents().isEmpty()) { - JTerm root = replaceTerm(state, applicationPosInOccurrence, applicationTerm, tf, - refactorings.childAndGrandchildRefactoringsAndParents(), services, - applicationPosInOccurrence, newApplicationTerm, rule, goal, hint, tacletTerm); + JTerm root = replaceTerm(context, applicationPosInOccurrence, applicationTerm, tf, + refactorings.childAndGrandchildRefactoringsAndParents()); goal.changeFormula(new SequentFormula(root), applicationPosInOccurrence.topLevel()); } // Do sequent refactoring if required if (!refactorings.sequentRefactorings().isEmpty() && goal != null) { Sequent sequent = goal.sequent(); - refactorSemisequent(state, services, applicationPosInOccurrence, applicationTerm, rule, - goal, hint, tacletTerm, sequent.antecedent(), true, + refactorSemisequent(context, sequent.antecedent(), true, refactorings.sequentRefactorings()); - refactorSemisequent(state, services, applicationPosInOccurrence, applicationTerm, rule, - goal, hint, tacletTerm, sequent.succedent(), false, + refactorSemisequent(context, sequent.succedent(), false, refactorings.sequentRefactorings()); } } @@ -1296,17 +966,16 @@ public void refactorSequent(TermLabelState state, Services services, PosInOccurrence applicationPosInOccurrence, JTerm applicationTerm, Rule rule, Goal goal, Object hint, JTerm tacletTerm) { + final TermLabelContext context = new TermLabelContext(state, services, + applicationPosInOccurrence, applicationTerm, null, rule, null, goal, hint, tacletTerm); // Compute active refactorings - RefactoringsContainer refactorings = computeRefactorings(state, services, - applicationPosInOccurrence, applicationTerm, rule, goal, hint, tacletTerm); + RefactoringsContainer refactorings = computeRefactorings(context); // Do sequent refactoring if required if (!refactorings.sequentRefactorings().isEmpty()) { Sequent sequent = goal.sequent(); - refactorSemisequent(state, services, applicationPosInOccurrence, applicationTerm, rule, - goal, hint, tacletTerm, sequent.antecedent(), true, + refactorSemisequent(context, sequent.antecedent(), true, refactorings.sequentRefactorings()); - refactorSemisequent(state, services, applicationPosInOccurrence, applicationTerm, rule, - goal, hint, tacletTerm, sequent.succedent(), false, + refactorSemisequent(context, sequent.succedent(), false, refactorings.sequentRefactorings()); } } @@ -1314,32 +983,17 @@ public void refactorSequent(TermLabelState state, Services services, /** * Replaces the {@link JTerm} at the specified {@link PosInOccurrence}. * - * @param state The {@link TermLabelState} of the current rule application. + * @param context the {@link TermLabelContext} of the current rule application * @param pio The {@link PosInOccurrence} to replace {@link JTerm} at. * @param newTerm The new {@link JTerm} to set. * @param tf The {@link TermFactory} to use. - * @param parentRefactorings The {@link RefactoringsContainer} to consider. - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param rule The {@link Rule} which is applied. - * @param goal The optional {@link Goal} on which the {@link JTerm} to create will be used. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional taclet {@link JTerm}. + * @param parentRefactorings The parent {@link TermLabelRefactoring}s to consider. * @return The root of the {@link PosInOccurrence} containing the new {@link JTerm} at the * specified {@link PosInOccurrence}. */ - protected JTerm replaceTerm(TermLabelState state, + protected JTerm replaceTerm(TermLabelContext context, PosInOccurrence pio, JTerm newTerm, - TermFactory tf, Set parentRefactorings, - Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, - Rule rule, Goal goal, Object hint, JTerm tacletTerm) { + TermFactory tf, Set parentRefactorings) { do { if (pio.isTopLevel()) { pio = null; @@ -1350,8 +1004,7 @@ protected JTerm replaceTerm(TermLabelState state, newTerm = (JTerm) pio.subTerm(); ImmutableArray newLabels; if (!parentRefactorings.isEmpty()) { - newLabels = performRefactoring(state, services, applicationPosInOccurrence, - applicationTerm, rule, goal, hint, tacletTerm, newTerm, parentRefactorings); + newLabels = performRefactoring(context, newTerm, parentRefactorings); } else { newLabels = newTerm.getLabels(); } @@ -1359,7 +1012,7 @@ protected JTerm replaceTerm(TermLabelState state, newSubs[childIndex] = newChild; ImmutableArray newSubsImmutable = new ImmutableArray<>(newSubs); - if (!newSubsImmutable.equals(newTerm.subs()) + if (!newSubs[childIndex].equalsIncludingLabels(newTerm.sub(childIndex)) || !newLabels.equals(newTerm.getLabels())) { newTerm = tf.createTerm(newTerm.op(), newSubsImmutable, newTerm.boundVars(), newLabels); @@ -1370,123 +1023,74 @@ protected JTerm replaceTerm(TermLabelState state, } /** - * Computes the rule-independent {@link TermLabelRefactoring} to consider. + * Computes the rule-specific {@link TermLabelRefactoring} to consider. * - * @param state The {@link TermLabelState} of the current rule application. - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param rule The {@link Rule} which is applied. - * @param goal The optional {@link Goal} on which the {@link JTerm} to create will be used. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional taclet {@link JTerm}. + * @param context the {@link TermLabelContext} of the current rule application * @param refactorings The already accumulated refactorings to be expanded with rule specific * refactorings */ - private void determineAndCollectRuleSpecificRefactorings(TermLabelState state, - Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, - Rule rule, Goal goal, Object hint, JTerm tacletTerm, + private void determineAndCollectRuleSpecificRefactorings(TermLabelContext context, RefactoringsContainer refactorings) { + final Rule rule = context.rule(); if (rule != null) { ImmutableList ruleRefactorings = ruleSpecificRefactorings.get(rule.name()); if (ruleRefactorings != null) { for (TermLabelRefactoring refactoring : ruleRefactorings) { - RefactoringScope scope = refactoring.defineRefactoringScope(state, services, - applicationPosInOccurrence, applicationTerm, rule, goal, hint, tacletTerm); - if (RefactoringScope.SEQUENT.equals(scope)) { - refactorings.sequentRefactorings.add(refactoring); - } else if (RefactoringScope.APPLICATION_BELOW_UPDATES.equals(scope)) { - refactorings.belowUpdatesRefactorings.add(refactoring); - } else if (RefactoringScope.APPLICATION_CHILDREN_AND_GRANDCHILDREN_SUBTREE - .equals(scope)) { - refactorings.childAndGrandchildRefactorings.add(refactoring); - } else if (RefactoringScope.APPLICATION_DIRECT_CHILDREN.equals(scope)) { - refactorings.directChildRefactorings.add(refactoring); - } else if (RefactoringScope.APPLICATION_CHILDREN_AND_GRANDCHILDREN_SUBTREE_AND_PARENTS - .equals(scope)) { - refactorings.childAndGrandchildRefactoringsAndParents.add(refactoring); - } + collectRefactoring(context, refactoring, refactorings); } } } } + /** + * Sorts the given {@link TermLabelRefactoring} into the bucket of the + * {@link RefactoringScope} it requests for the current rule application. + * + * @param context the {@link TermLabelContext} of the current rule application + * @param refactoring the {@link TermLabelRefactoring} to sort in + * @param refactorings the accumulated refactorings + */ + private void collectRefactoring(TermLabelContext context, TermLabelRefactoring refactoring, + RefactoringsContainer refactorings) { + RefactoringScope scope = refactoring.defineRefactoringScope(context); + if (RefactoringScope.SEQUENT.equals(scope)) { + refactorings.sequentRefactorings.add(refactoring); + } else if (RefactoringScope.APPLICATION_BELOW_UPDATES.equals(scope)) { + refactorings.belowUpdatesRefactorings.add(refactoring); + } else if (RefactoringScope.APPLICATION_CHILDREN_AND_GRANDCHILDREN_SUBTREE + .equals(scope)) { + refactorings.childAndGrandchildRefactorings.add(refactoring); + } else if (RefactoringScope.APPLICATION_CHILDREN_AND_GRANDCHILDREN_SUBTREE_AND_PARENTS + .equals(scope)) { + refactorings.childAndGrandchildRefactoringsAndParents.add(refactoring); + } + } + /** * Computes the rule-independent {@link TermLabelRefactoring} to consider. * - * @param state The {@link TermLabelState} of the current rule application. - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param rule The {@link Rule} which is applied. - * @param goal The optional {@link Goal} on which the {@link JTerm} to create will be used. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional taclet {@link JTerm}. + * @param context the {@link TermLabelContext} of the current rule application * @param refactorings The already accumulated refactorings to be expanded with rule independent * refactorings */ - private void determineAndRuleIndependentRefactorings(TermLabelState state, - Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, - Rule rule, Goal goal, Object hint, JTerm tacletTerm, + private void determineAndRuleIndependentRefactorings(TermLabelContext context, RefactoringsContainer refactorings) { for (TermLabelRefactoring refactoring : allRulesRefactorings) { - RefactoringScope scope = refactoring.defineRefactoringScope(state, services, - applicationPosInOccurrence, applicationTerm, rule, goal, hint, tacletTerm); - if (RefactoringScope.SEQUENT.equals(scope)) { - refactorings.sequentRefactorings.add(refactoring); - } else if (RefactoringScope.APPLICATION_BELOW_UPDATES.equals(scope)) { - refactorings.belowUpdatesRefactorings.add(refactoring); - } else if (RefactoringScope.APPLICATION_CHILDREN_AND_GRANDCHILDREN_SUBTREE - .equals(scope)) { - refactorings.childAndGrandchildRefactorings.add(refactoring); - } else if (RefactoringScope.APPLICATION_DIRECT_CHILDREN.equals(scope)) { - refactorings.directChildRefactorings.add(refactoring); - } else if (RefactoringScope.APPLICATION_CHILDREN_AND_GRANDCHILDREN_SUBTREE_AND_PARENTS - .equals(scope)) { - refactorings.childAndGrandchildRefactoringsAndParents.add(refactoring); - } + collectRefactoring(context, refactoring, refactorings); } } /** * Computes the {@link TermLabelRefactoring} to consider. * - * @param state The {@link TermLabelState} of the current rule application. - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param rule The {@link Rule} which is applied. - * @param goal The optional {@link Goal} on which the {@link JTerm} to create will be used. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional taclet {@link JTerm}. + * @param context the {@link TermLabelContext} of the current rule application * @return The {@link RefactoringsContainer} with the {@link TermLabelRefactoring}s to consider. */ - protected RefactoringsContainer computeRefactorings(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm) { + protected RefactoringsContainer computeRefactorings(TermLabelContext context) { final RefactoringsContainer refactorings = new RefactoringsContainer(); - determineAndCollectRuleSpecificRefactorings(state, services, applicationPosInOccurrence, - applicationTerm, rule, goal, hint, tacletTerm, refactorings); - determineAndRuleIndependentRefactorings(state, services, applicationPosInOccurrence, - applicationTerm, rule, goal, hint, tacletTerm, refactorings); + determineAndCollectRuleSpecificRefactorings(context, refactorings); + determineAndRuleIndependentRefactorings(context, refactorings); return refactorings; } @@ -1495,8 +1099,7 @@ public Map> getFactories() { } /** - * Utility class used by - * {@link TermLabelManager#computeRefactorings(TermLabelState, Services, PosInOccurrence, JTerm, Rule, Goal, Object, JTerm)} + * Utility class used by {@link TermLabelManager#computeRefactorings(TermLabelContext)} * * @param sequentRefactorings The {@link TermLabelRefactoring} for * {@link RefactoringScope#SEQUENT}. @@ -1506,18 +1109,15 @@ public Map> getFactories() { * {@link RefactoringScope#APPLICATION_CHILDREN_AND_GRANDCHILDREN_SUBTREE}. * @param childAndGrandchildRefactoringsAndParents The {@link TermLabelRefactoring} for * {@link RefactoringScope#APPLICATION_CHILDREN_AND_GRANDCHILDREN_SUBTREE_AND_PARENTS}. - * @param directChildRefactorings The {@link TermLabelRefactoring} for - * {@link RefactoringScope#APPLICATION_DIRECT_CHILDREN}. * @author Martin Hentschel */ protected record RefactoringsContainer(Set sequentRefactorings, Set belowUpdatesRefactorings, Set childAndGrandchildRefactorings, - Set childAndGrandchildRefactoringsAndParents, - Set directChildRefactorings) { + Set childAndGrandchildRefactoringsAndParents) { public RefactoringsContainer() { this(new LinkedHashSet<>(), new LinkedHashSet<>(), new LinkedHashSet<>(), - new LinkedHashSet<>(), new LinkedHashSet<>()); + new LinkedHashSet<>()); } /** @@ -1535,94 +1135,28 @@ public Set getAllApplicationChildAndGrandchildRefactorings } } - /** - * Do direct child refactoring if required. - * - * @param state The {@link TermLabelState} of the current rule application. - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param rule The {@link Rule} which is applied. - * @param goal The optional {@link Goal} on which the {@link JTerm} to create will be used. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional taclet {@link JTerm}. - * @param refactorings The {@link RefactoringsContainer} with the {@link TermLabelRefactoring}s - * to consider. - * @param tf The {@link TermFactory} to create the term. - * @return The new application {@link JTerm} or {@code null} if no refactoring was performed. - */ - private JTerm refactorChildTerms(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm, RefactoringsContainer refactorings, TermFactory tf) { - JTerm newApplicationTerm = applicationTerm; - if (!refactorings.directChildRefactorings().isEmpty()) { - boolean changed = false; - JTerm[] newSubs = new JTerm[newApplicationTerm.arity()]; - for (int i = 0; i < newSubs.length; i++) { - final JTerm sub = newApplicationTerm.sub(i); - ImmutableArray newLabels = performRefactoring(state, services, - applicationPosInOccurrence, applicationTerm, rule, goal, hint, tacletTerm, sub, - refactorings.directChildRefactorings()); - - if (newLabels != sub.getLabels()) { - newSubs[i] = - tf.createTerm(sub.op(), sub.subs(), sub.boundVars(), - newLabels); - changed = true; - } else { - newSubs[i] = sub; - } - } - newApplicationTerm = changed ? tf.createTerm(newApplicationTerm.op(), newSubs, - newApplicationTerm.boundVars(), - newApplicationTerm.getLabels()) : applicationTerm; - } - return newApplicationTerm; - } - - /** * Perform below-updates refactoring if required. * - * @param state The {@link TermLabelState} of the current rule application. - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param rule The {@link Rule} which is applied. - * @param goal The optional {@link Goal} on which the {@link JTerm} to create will be used. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional taclet {@link JTerm}. + * @param context the {@link TermLabelContext} of the current rule application * @param refactorings The {@link RefactoringsContainer} with the {@link TermLabelRefactoring}s * to consider. * @param tf The {@link TermFactory} to create the term. * @param newApplicationTerm The refactored application term until now. * @return The new application {@link JTerm} or {@code null} if no refactoring was performed. */ - private JTerm refactorBelowUpdates(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm, RefactoringsContainer refactorings, TermFactory tf, - JTerm newApplicationTerm) { + private JTerm refactorBelowUpdates(TermLabelContext context, + RefactoringsContainer refactorings, TermFactory tf, JTerm newApplicationTerm) { if (!refactorings.belowUpdatesRefactorings().isEmpty()) { Pair, JTerm> pair = TermBuilder.goBelowUpdates2(newApplicationTerm); - ImmutableArray newLabels = performRefactoring(state, services, - applicationPosInOccurrence, applicationTerm, rule, goal, hint, tacletTerm, - pair.second, refactorings.belowUpdatesRefactorings()); + ImmutableArray newLabels = performRefactoring(context, pair.second, + refactorings.belowUpdatesRefactorings()); if (newLabels != pair.second.getLabels()) { JTerm newModality = tf.createTerm(pair.second.op(), pair.second.subs(), pair.second.boundVars(), newLabels); newApplicationTerm = - services.getTermBuilder().applyParallel(pair.first, newModality, + context.services().getTermBuilder().applyParallel(pair.first, newModality, newApplicationTerm.getLabels()); } } @@ -1632,29 +1166,15 @@ private JTerm refactorBelowUpdates(TermLabelState state, Services services, /** * Do child and grandchild refactoring if required. * - * @param state The {@link TermLabelState} of the current rule application. - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param rule The {@link Rule} which is applied. - * @param goal The optional {@link Goal} on which the {@link JTerm} to create will be used. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional taclet {@link JTerm}. + * @param context the {@link TermLabelContext} of the current rule application * @param refactorings The {@link RefactoringsContainer} with the {@link TermLabelRefactoring}s * to consider. * @param tf The {@link TermFactory} to create the term. * @param newApplicationTerm The refactored application term until now. * @return The new application {@link JTerm} or {@code null} if no refactoring was performed. */ - private JTerm refactorChildrenRecursively(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm, RefactoringsContainer refactorings, TermFactory tf, - JTerm newApplicationTerm) { + private JTerm refactorChildrenRecursively(TermLabelContext context, + RefactoringsContainer refactorings, TermFactory tf, JTerm newApplicationTerm) { final Set allChildAndGrandchildRefactorings = refactorings.getAllApplicationChildAndGrandchildRefactorings(); if (!allChildAndGrandchildRefactorings.isEmpty()) { @@ -1662,10 +1182,9 @@ private JTerm refactorChildrenRecursively(TermLabelState state, Services service JTerm[] newSubs = new JTerm[newApplicationTerm.arity()]; for (int i = 0; i < newSubs.length; i++) { JTerm sub = newApplicationTerm.sub(i); - newSubs[i] = refactorLabelsRecursive(state, services, applicationPosInOccurrence, - applicationTerm, rule, goal, hint, tacletTerm, sub, - allChildAndGrandchildRefactorings); - if (!newSubs[i].equals(sub)) { + newSubs[i] = + refactorLabelsRecursive(context, sub, allChildAndGrandchildRefactorings); + if (!newSubs[i].equalsIncludingLabels(sub)) { changed = true; } } @@ -1680,42 +1199,24 @@ private JTerm refactorChildrenRecursively(TermLabelState state, Services service /** * Refactors the labels of the application term. * - * @param state The {@link TermLabelState} of the current rule application. - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param rule The {@link Rule} which is applied. - * @param goal The optional {@link Goal} on which the {@link JTerm} to create will be used. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional taclet {@link JTerm}. + * @param context the {@link TermLabelContext} of the current rule application * @param refactorings The {@link RefactoringsContainer} with the {@link TermLabelRefactoring}s * to consider. * @param tf The {@link TermFactory} to create the term. * @return The new application {@link JTerm} or {@code null} if no refactoring was performed. */ - protected JTerm refactorApplicationTerm(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm, RefactoringsContainer refactorings, TermFactory tf) { - if (applicationTerm != null && (!refactorings.directChildRefactorings().isEmpty() - || !refactorings.childAndGrandchildRefactorings().isEmpty() + protected JTerm refactorApplicationTerm(TermLabelContext context, + RefactoringsContainer refactorings, TermFactory tf) { + final JTerm applicationTerm = context.applicationTerm(); + if (applicationTerm != null && (!refactorings.childAndGrandchildRefactorings().isEmpty() || !refactorings.belowUpdatesRefactorings().isEmpty())) { - JTerm newApplicationTerm; - // Do direct child refactoring if required - newApplicationTerm = refactorChildTerms(state, services, applicationPosInOccurrence, - applicationTerm, rule, goal, hint, tacletTerm, refactorings, tf); + JTerm newApplicationTerm = applicationTerm; // Perform below-updates refactoring newApplicationTerm = - refactorBelowUpdates(state, services, applicationPosInOccurrence, applicationTerm, - rule, goal, hint, tacletTerm, refactorings, tf, newApplicationTerm); + refactorBelowUpdates(context, refactorings, tf, newApplicationTerm); // Do child and grandchild refactoring if required - newApplicationTerm = refactorChildrenRecursively(state, services, - applicationPosInOccurrence, applicationTerm, rule, goal, hint, tacletTerm, - refactorings, tf, newApplicationTerm); + newApplicationTerm = + refactorChildrenRecursively(context, refactorings, tf, newApplicationTerm); return newApplicationTerm; } else { return null; @@ -1725,35 +1226,19 @@ protected JTerm refactorApplicationTerm(TermLabelState state, Services services, /** * Performs a {@link TermLabel} refactoring on the given {@link Semisequent}. * - * @param state The {@link TermLabelState} of the current rule application. - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param rule The {@link Rule} which is applied. - * @param goal The optional {@link Goal} on which the {@link JTerm} to create will be used. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional taclet {@link JTerm}. + * @param context the {@link TermLabelContext} of the current rule application * @param semisequent The {@link Semisequent} to refactor. * @param inAntec {@code true} antecedent, {@code false} succedent. * @param activeRefactorings The active {@link TermLabelRefactoring}s to execute. */ - protected void refactorSemisequent(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm, Semisequent semisequent, boolean inAntec, - Set activeRefactorings) { + protected void refactorSemisequent(TermLabelContext context, Semisequent semisequent, + boolean inAntec, Set activeRefactorings) { if (!activeRefactorings.isEmpty()) { for (SequentFormula sfa : semisequent) { JTerm updatedTerm = - refactorLabelsRecursive(state, services, applicationPosInOccurrence, - applicationTerm, rule, goal, hint, tacletTerm, (JTerm) sfa.formula(), - activeRefactorings); - if (!sfa.formula().equals(updatedTerm)) { - goal.changeFormula(new SequentFormula(updatedTerm), + refactorLabelsRecursive(context, (JTerm) sfa.formula(), activeRefactorings); + if (!((JTerm) sfa.formula()).equalsIncludingLabels(updatedTerm)) { + context.goal().changeFormula(new SequentFormula(updatedTerm), new PosInOccurrence(sfa, PosInTerm.getTopLevel(), inAntec)); } } @@ -1763,41 +1248,25 @@ protected void refactorSemisequent(TermLabelState state, Services services, /** * Performs a {@link TermLabel} refactoring recursively on the given {@link JTerm}. * - * @param state The {@link TermLabelState} of the current rule application. - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param rule The {@link Rule} which is applied. - * @param goal The optional {@link Goal} on which the {@link JTerm} to create will be used. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional taclet {@link JTerm}. + * @param context the {@link TermLabelContext} of the current rule application * @param term The {@link JTerm} to refactor. * @param activeRefactorings The active {@link TermLabelRefactoring}s to execute. * @return The refactored {@link JTerm} in which the {@link TermLabel}s may have changed. */ - protected JTerm refactorLabelsRecursive(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm, JTerm term, + protected JTerm refactorLabelsRecursive(TermLabelContext context, JTerm term, Set activeRefactorings) { boolean subsChanged = false; JTerm[] newSubs = new JTerm[term.arity()]; for (int i = 0; i < newSubs.length; i++) { JTerm oldSub = term.sub(i); - newSubs[i] = refactorLabelsRecursive(state, services, applicationPosInOccurrence, - applicationTerm, rule, goal, hint, tacletTerm, oldSub, activeRefactorings); - if (!newSubs[i].equals(oldSub)) { + newSubs[i] = refactorLabelsRecursive(context, oldSub, activeRefactorings); + if (!newSubs[i].equalsIncludingLabels(oldSub)) { subsChanged = true; } } ImmutableArray newLabels = - performRefactoring(state, services, applicationPosInOccurrence, applicationTerm, rule, - goal, hint, tacletTerm, term, activeRefactorings); - return subsChanged || newLabels != term.getLabels() ? services.getTermFactory() + performRefactoring(context, term, activeRefactorings); + return subsChanged || newLabels != term.getLabels() ? context.services().getTermFactory() .createTerm(term.op(), newSubs, term.boundVars(), newLabels) : term; } @@ -1805,34 +1274,19 @@ protected JTerm refactorLabelsRecursive(TermLabelState state, Services services, /** * Computes the new labels as part of the refactoring for the given {@link JTerm}. * - * @param state The {@link TermLabelState} of the current rule application. - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param rule The {@link Rule} which is applied. - * @param goal The optional {@link Goal} on which the {@link JTerm} to create will be used. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional taclet {@link JTerm}. + * @param context the {@link TermLabelContext} of the current rule application * @param term The {@link JTerm} to refactor. * @param activeRefactorings The active {@link TermLabelRefactoring}s to execute. * @return The new {@link TermLabel} which should be used for the given {@link JTerm}. */ - protected ImmutableArray performRefactoring(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm, JTerm term, + protected ImmutableArray performRefactoring(TermLabelContext context, JTerm term, Set activeRefactorings) { // Create list with all old labels LabelCollection newLabels = new LabelCollection(term.getLabels()); // Give all TermLabelInstantiator instances the chance to remove or to // add labels from/to the list for (TermLabelRefactoring refactoring : activeRefactorings) { - refactoring.refactorLabels(state, services, applicationPosInOccurrence, applicationTerm, - rule, goal, hint, tacletTerm, term, newLabels); + refactoring.refactorLabels(context, term, newLabels); } if (newLabels.isModified()) { return new ImmutableArray<>(newLabels.getLabels()); @@ -1868,16 +1322,6 @@ public static final class TermLabelConfiguration { */ private final ImmutableList modalityTermPolicies; - /** - * The direct {@link ChildTermLabelPolicy} instances to use. - */ - private final ImmutableList directChildTermLabelPolicies; - - /** - * The child and grandchild {@link ChildTermLabelPolicy} instances to use. - */ - private final ImmutableList childAndGrandchildTermLabelPolicies; - /** * The {@link TermLabelUpdate} instances. */ @@ -1900,7 +1344,7 @@ public static final class TermLabelConfiguration { * @param factory The {@link TermLabelFactory} to use. */ public TermLabelConfiguration(Name termLabelName, TermLabelFactory factory) { - this(termLabelName, factory, null, null, null, null, null, null, null); + this(termLabelName, factory, null, null, null, null, null); } /** @@ -1912,10 +1356,6 @@ public TermLabelConfiguration(Name termLabelName, TermLabelFactory factory) { * application term. * @param modalityTermPolicies The {@link TermLabelPolicy} instances applied on the modality * term. - * @param directChildTermLabelPolicies The direct {@link ChildTermLabelPolicy} instances to - * use. - * @param childAndGrandchildTermLabelPolicies The child and grandchild - * {@link ChildTermLabelPolicy} instances to use. * @param termLabelUpdates The {@link TermLabelUpdate} instances. * @param termLabelRefactorings The {@link TermLabelRefactoring} instances. * @param termLabelMerger The {@link TermLabelMerger} instance. @@ -1923,8 +1363,6 @@ public TermLabelConfiguration(Name termLabelName, TermLabelFactory factory) { public TermLabelConfiguration(Name termLabelName, TermLabelFactory factory, ImmutableList applicationTermPolicies, ImmutableList modalityTermPolicies, - ImmutableList directChildTermLabelPolicies, - ImmutableList childAndGrandchildTermLabelPolicies, ImmutableList termLabelUpdates, ImmutableList termLabelRefactorings, TermLabelMerger termLabelMerger) { @@ -1934,8 +1372,6 @@ public TermLabelConfiguration(Name termLabelName, TermLabelFactory factory, this.factory = factory; this.applicationTermPolicies = applicationTermPolicies; this.modalityTermPolicies = modalityTermPolicies; - this.directChildTermLabelPolicies = directChildTermLabelPolicies; - this.childAndGrandchildTermLabelPolicies = childAndGrandchildTermLabelPolicies; this.termLabelUpdates = termLabelUpdates; this.termLabelRefactorings = termLabelRefactorings; this.termLabelMerger = termLabelMerger; @@ -1977,24 +1413,6 @@ public ImmutableList getModalityTermPolicies() { return modalityTermPolicies; } - /** - * Returns the direct {@link ChildTermLabelPolicy} instances to use. - * - * @return The direct {@link ChildTermLabelPolicy} instances to use. - */ - public ImmutableList getDirectChildTermLabelPolicies() { - return directChildTermLabelPolicies; - } - - /** - * Returns the child and grandchild {@link ChildTermLabelPolicy} instances to use. - * - * @return The child and grandchild {@link ChildTermLabelPolicy} instances to use. - */ - public ImmutableList getChildAndGrandchildTermLabelPolicies() { - return childAndGrandchildTermLabelPolicies; - } - /** * Returns the {@link TermLabelUpdate} instances. * diff --git a/key.core/src/main/java/de/uka/ilkd/key/proof/OpReplacer.java b/key.core/src/main/java/de/uka/ilkd/key/proof/OpReplacer.java index 4b6338c6e8d..3fceea0cf6e 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/proof/OpReplacer.java +++ b/key.core/src/main/java/de/uka/ilkd/key/proof/OpReplacer.java @@ -18,7 +18,6 @@ import org.key_project.util.collection.ImmutableList; import org.key_project.util.collection.ImmutableSet; -import static de.uka.ilkd.key.logic.equality.TermLabelsProperty.TERM_LABELS_PROPERTY; /** @@ -235,7 +234,7 @@ public JTerm replace(JTerm term) { } for (SyntaxElement svs : map.keySet()) { - if (svs instanceof JTerm t && TERM_LABELS_PROPERTY.equalsModThisProperty(term, t)) { + if (svs instanceof JTerm t && term.equals(t)) { return (JTerm) map.get(svs); } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/proof/PrefixTermTacletAppIndexCacheImpl.java b/key.core/src/main/java/de/uka/ilkd/key/proof/PrefixTermTacletAppIndexCacheImpl.java index c563566ddec..de5e5280200 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/proof/PrefixTermTacletAppIndexCacheImpl.java +++ b/key.core/src/main/java/de/uka/ilkd/key/proof/PrefixTermTacletAppIndexCacheImpl.java @@ -5,6 +5,8 @@ import java.util.Map; +import de.uka.ilkd.key.logic.JTerm; + import org.key_project.logic.Term; import org.key_project.logic.op.QuantifiableVariable; import org.key_project.util.collection.ImmutableList; @@ -98,7 +100,10 @@ public boolean equals(Object obj) { return false; } - return parent == objKey.parent && analysedTerm.equals(objKey.analysedTerm); + // label-sensitive comparison: taclet applicability can depend on term labels + // (e.g. TermLabelCondition), so label variants must not share index entries + return parent == objKey.parent + && ((JTerm) analysedTerm).equalsIncludingLabels(objKey.analysedTerm); } public int hashCode() { diff --git a/key.core/src/main/java/de/uka/ilkd/key/proof/init/JavaProfile.java b/key.core/src/main/java/de/uka/ilkd/key/proof/init/JavaProfile.java index 27fc080a30b..ff03d18fac4 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/proof/init/JavaProfile.java +++ b/key.core/src/main/java/de/uka/ilkd/key/proof/init/JavaProfile.java @@ -147,7 +147,6 @@ protected ImmutableList computeTermLabelConfiguration() result = result.prepend(new TermLabelConfiguration(OriginTermLabel.NAME, new OriginTermLabelFactory(), originTermLabelPolicyList, null, null, - null, null, originTermLabelRefactorings, null)); result = diff --git a/key.core/src/main/java/de/uka/ilkd/key/rule/SyntacticalReplaceVisitor.java b/key.core/src/main/java/de/uka/ilkd/key/rule/SyntacticalReplaceVisitor.java index 71b977428d6..773691f5673 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/rule/SyntacticalReplaceVisitor.java +++ b/key.core/src/main/java/de/uka/ilkd/key/rule/SyntacticalReplaceVisitor.java @@ -367,8 +367,10 @@ public void visit(final Term p_visited) { pushNew(resolveSubst(newTerm)); } else { JTerm t; - final ImmutableArray labels = instantiateLabels(visited, visitedOp, - visited.subs(), visited.boundVars(), visited.getLabels()); + // The new term is structurally identical to visited (same op, subs, bound + // variables and labels), so pass visited itself to the label manager instead + // of rebuilding an equal throwaway term for every unchanged node. + final ImmutableArray labels = instantiateLabels(visited, visited); if (!visited.hasLabels() && labels != null && labels.isEmpty()) { t = visited; } else { @@ -389,11 +391,15 @@ private ImmutableArray instantiateLabels(JTerm tacletTerm, Operator n ImmutableArray newTermSubs, ImmutableArray newTermBoundVars, ImmutableArray newTermOriginalLabels) { - return TermLabelManager.instantiateLabels(termLabelState, services, - applicationPosInOccurrence, rule, ruleApp, goal, labelHint, tacletTerm, + return instantiateLabels(tacletTerm, tb.tf().createTerm(newTermOp, newTermSubs, newTermBoundVars, newTermOriginalLabels)); } + private ImmutableArray instantiateLabels(JTerm tacletTerm, JTerm newTerm) { + return TermLabelManager.instantiateLabels(termLabelState, services, + applicationPosInOccurrence, rule, ruleApp, goal, labelHint, tacletTerm, newTerm); + } + private Operator handleParametricFunction(ParametricFunctionInstance pfi) { ImmutableList args = ImmutableList.nil(); diff --git a/key.core/src/main/java/de/uka/ilkd/key/rule/UseDependencyContractRule.java b/key.core/src/main/java/de/uka/ilkd/key/rule/UseDependencyContractRule.java index 0f1b89bc6a0..53b8e1b7b1d 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/rule/UseDependencyContractRule.java +++ b/key.core/src/main/java/de/uka/ilkd/key/rule/UseDependencyContractRule.java @@ -40,7 +40,6 @@ import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; -import static de.uka.ilkd.key.logic.equality.TermLabelsProperty.TERM_LABELS_PROPERTY; public final class UseDependencyContractRule implements BuiltInRule, ComplexJustificationable { @@ -225,7 +224,7 @@ public static boolean isBaseOcc(JTerm focus, JTerm candidate) { return false; } for (int i = 1, n = candidate.arity(); i < n; i++) { - if (!(candidate.sub(i).equalsModProperty(focus.sub(i), TERM_LABELS_PROPERTY) + if (!(candidate.sub(i).equals(focus.sub(i)) || candidate.sub(i).op() instanceof LogicVariable)) { return false; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/rule/label/ChildTermLabelPolicy.java b/key.core/src/main/java/de/uka/ilkd/key/rule/label/ChildTermLabelPolicy.java deleted file mode 100644 index 4c4a58e6e84..00000000000 --- a/key.core/src/main/java/de/uka/ilkd/key/rule/label/ChildTermLabelPolicy.java +++ /dev/null @@ -1,105 +0,0 @@ -/* This file is part of KeY - https://key-project.org - * KeY is licensed under the GNU General Public License Version 2 - * SPDX-License-Identifier: GPL-2.0-only */ -package de.uka.ilkd.key.rule.label; - -import de.uka.ilkd.key.java.Services; -import de.uka.ilkd.key.logic.JTerm; -import de.uka.ilkd.key.logic.TermServices; -import de.uka.ilkd.key.logic.label.TermLabel; -import de.uka.ilkd.key.logic.label.TermLabelManager; -import de.uka.ilkd.key.logic.label.TermLabelState; -import de.uka.ilkd.key.proof.Goal; -import de.uka.ilkd.key.proof.Proof; - -import org.key_project.prover.rules.Rule; -import org.key_project.prover.rules.RuleApp; -import org.key_project.prover.sequent.PosInOccurrence; -import org.key_project.prover.sequent.Sequent; - -/** - *

    - * A {@link ChildTermLabelPolicy} is used by - * {@link TermLabelManager#instantiateLabels(TermLabelState, Services, PosInOccurrence, Rule, RuleApp, Goal, Object, JTerm, JTerm)} - * to decide for each {@link TermLabel} on a child or grandchild of the application {@link JTerm} if - * it should be re-added to the new {@link JTerm} or not. - *

    - *

    - * For more information about {@link TermLabel}s and how they are maintained during prove read the - * documentation of interface {@link TermLabel}. - *

    - * - * @author Martin Hentschel - * @see TermLabel - * @see TermLabelManager - */ -public interface ChildTermLabelPolicy extends RuleSpecificTask { - /** - * Decides if the currently active {@link Rule} application is supported or not. If it is not - * supported no iteration over children will be executed. Only if it returns {@code true} - * {@link #addLabel( TermServices, PosInOccurrence, JTerm, Rule, Goal, Object, JTerm, JTerm, JTerm, TermLabel)} - * will - * be called if a child {@link JTerm} contains a managed label. - * - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param rule The {@link Rule} which is applied. - * @param goal The optional {@link Goal} on which the {@link JTerm} to create will be used. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional {@link JTerm} in the taclet which is responsible to - * instantiate - * the new {@link JTerm} for the new proof node or {@code null} in case of built in - * rules. - * @param newTerm the template for the new {@link JTerm} to create - * @return {@code true} keep {@link TermLabel} and add it to the new {@link JTerm}. - * {@code false} - * drop {@link TermLabel} and do not need it to the new {@link JTerm}. - */ - boolean isRuleApplicationSupported(TermServices services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm, JTerm newTerm); - - /** - *

    - * Decides to add or not to add the given {@link TermLabel} on a child or grandchild of the - * application {@link JTerm} to the new {@link JTerm} which will be created. - *

    - *

    - * If the child {@link JTerm} is still a child of the new {@link JTerm} the label will still - * exist - * independent from the result of this method on the child. To remove it from the child a - * refacotring has to be used instead. - *

    - * - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param rule The {@link Rule} which is applied. - * @param goal The optional {@link Goal} on which the {@link JTerm} to create will be used. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional {@link JTerm} in the taclet which is responsible to - * instantiate - * the new {@link JTerm} for the new proof node or {@code null} in case of built in - * rules. - * @param newTerm the template for the new {@link JTerm} to create - * @param childTerm The {@link JTerm} which is a child or grandchild of the application - * {@link JTerm} that provides the {@link TermLabel}. - * @param label The {@link TermLabel} to decide if it should be kept or dropped. - * @return {@code true} add {@link TermLabel} to new {@link JTerm}. {@code false} do not add - * {@link TermLabel} to new {@link JTerm}. - */ - boolean addLabel(TermServices services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, Goal goal, Object hint, JTerm tacletTerm, - JTerm newTerm, JTerm childTerm, TermLabel label); -} diff --git a/key.core/src/main/java/de/uka/ilkd/key/rule/label/OriginTermLabelPolicy.java b/key.core/src/main/java/de/uka/ilkd/key/rule/label/OriginTermLabelPolicy.java index 75e0afa259a..46697488d3b 100755 --- a/key.core/src/main/java/de/uka/ilkd/key/rule/label/OriginTermLabelPolicy.java +++ b/key.core/src/main/java/de/uka/ilkd/key/rule/label/OriginTermLabelPolicy.java @@ -3,16 +3,12 @@ * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.rule.label; -import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.label.OriginTermLabel; import de.uka.ilkd.key.logic.label.OriginTermLabel.SpecType; import de.uka.ilkd.key.logic.label.TermLabel; -import de.uka.ilkd.key.logic.label.TermLabelState; -import de.uka.ilkd.key.proof.Goal; +import de.uka.ilkd.key.logic.label.TermLabelContext; -import org.key_project.prover.rules.Rule; -import org.key_project.prover.sequent.PosInOccurrence; /** * Policy for {@link OriginTermLabel}s. @@ -24,19 +20,17 @@ public class OriginTermLabelPolicy implements TermLabelPolicy { @Override - public TermLabel keepLabel(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm, - JTerm newTerm, TermLabel label) { - if (services.getProof() == null) { + public TermLabel keepLabel(TermLabelContext context, JTerm sourceTerm, JTerm newTerm, + TermLabel label) { + if (context.services().getProof() == null) { return label; } - if (services.getTermBuilder().getOriginFactory() == null) { + if (context.services().getTermBuilder().getOriginFactory() == null) { return null; } - if (!OriginTermLabel.canAddLabel(newTerm.op(), services)) { + if (!OriginTermLabel.canAddLabel(newTerm.op(), context.services())) { return null; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/rule/label/OriginTermLabelRefactoring.java b/key.core/src/main/java/de/uka/ilkd/key/rule/label/OriginTermLabelRefactoring.java index b7a14eae08d..5d718a333ba 100755 --- a/key.core/src/main/java/de/uka/ilkd/key/rule/label/OriginTermLabelRefactoring.java +++ b/key.core/src/main/java/de/uka/ilkd/key/rule/label/OriginTermLabelRefactoring.java @@ -8,15 +8,15 @@ import java.util.Map; import java.util.Set; -import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.logic.JTerm; +import de.uka.ilkd.key.logic.StrictTermKey; import de.uka.ilkd.key.logic.label.LabelCollection; import de.uka.ilkd.key.logic.label.OriginTermLabel; import de.uka.ilkd.key.logic.label.OriginTermLabel.Origin; import de.uka.ilkd.key.logic.label.OriginTermLabel.SpecType; import de.uka.ilkd.key.logic.label.OriginTermLabelFactory; import de.uka.ilkd.key.logic.label.TermLabel; -import de.uka.ilkd.key.logic.label.TermLabelState; +import de.uka.ilkd.key.logic.label.TermLabelContext; import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.rule.BuiltInRule; import de.uka.ilkd.key.rule.Taclet; @@ -24,7 +24,6 @@ import org.key_project.logic.Name; import org.key_project.prover.indexing.FormulaTag; import org.key_project.prover.rules.Rule; -import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.util.collection.ImmutableArray; import org.key_project.util.collection.ImmutableList; @@ -43,14 +42,13 @@ public ImmutableList getSupportedRuleNames() { } @Override - public RefactoringScope defineRefactoringScope(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm) { - if (rule instanceof BuiltInRule - && !TermLabelRefactoring.shouldRefactorOnBuiltInRule(rule, goal, hint)) { + public RefactoringScope defineRefactoringScope(TermLabelContext context) { + if (context.rule() instanceof BuiltInRule + && !TermLabelRefactoring.shouldRefactorOnBuiltInRule(context.rule(), context.goal(), + context.hint())) { return RefactoringScope.NONE; - } else if (rule instanceof Taclet && !shouldRefactorOnTaclet((Taclet) rule)) { + } else if (context.rule() instanceof Taclet + && !shouldRefactorOnTaclet((Taclet) context.rule())) { return RefactoringScope.NONE; } else { return RefactoringScope.APPLICATION_CHILDREN_AND_GRANDCHILDREN_SUBTREE; @@ -58,25 +56,24 @@ public RefactoringScope defineRefactoringScope(TermLabelState state, Services se } @Override - public void refactorLabels(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm, JTerm term, LabelCollection labels) { - if (services.getProof() == null) { + public void refactorLabels(TermLabelContext context, JTerm term, LabelCollection labels) { + if (context.services().getProof() == null) { return; } - if (rule instanceof BuiltInRule - && !TermLabelRefactoring.shouldRefactorOnBuiltInRule(rule, goal, hint)) { + if (context.rule() instanceof BuiltInRule + && !TermLabelRefactoring.shouldRefactorOnBuiltInRule(context.rule(), context.goal(), + context.hint())) { return; } - if (rule instanceof Taclet && !shouldRefactorOnTaclet((Taclet) rule)) { + if (context.rule() instanceof Taclet && !shouldRefactorOnTaclet((Taclet) context.rule())) { return; } final OriginTermLabel oldLabel = labels.getFirst(OriginTermLabel.class); - if (services.getTermBuilder().getOriginFactory() == null) { + if (context.services().getTermBuilder().getOriginFactory() == null) { if (oldLabel != null) { labels.remove(oldLabel); } @@ -85,13 +82,13 @@ public void refactorLabels(TermLabelState state, Services services, // cache origins per term to avoid a quadratic recursive collection per rule // application - final Map> originsCache = - services.getCaches().getSubtermOriginsCache(); + final Map> originsCache = + context.services().getCaches().getSubtermOriginsCache(); Set subtermOrigins = new LinkedHashSet<>(); for (JTerm sub : term.subs()) { subtermOrigins.addAll(collectSubtermOrigins(sub, originsCache)); } - OriginTermLabelFactory factory = services.getTermBuilder().getOriginFactory(); + OriginTermLabelFactory factory = context.services().getTermBuilder().getOriginFactory(); OriginTermLabel newLabel = null; if (oldLabel != null) { labels.remove(oldLabel); @@ -105,7 +102,7 @@ public void refactorLabels(TermLabelState state, Services services, if (newLabel != null) { final Origin origin = newLabel.getOrigin(); - if (OriginTermLabel.canAddLabel(term, services) + if (OriginTermLabel.canAddLabel(term, context.services()) && (!subtermOrigins.isEmpty() || origin.specType != SpecType.NONE)) { labels.add(newLabel); } @@ -135,8 +132,16 @@ private boolean shouldRefactorOnTaclet(Taclet taclet) { * subterms; the returned set is unmodifiable and shared, do not mutate */ @SuppressWarnings("unchecked") - private Set collectSubtermOrigins(JTerm term, Map> originsCache) { - Set cached = originsCache.get(term); + private Set collectSubtermOrigins(JTerm term, + Map> originsCache) { + // origins live in term labels: a label-free subtree cannot contribute any + if (!term.containsLabelsRecursive()) { + return Collections.emptySet(); + } + + // the cache key must be label-sensitive as the value is derived from the labels + final StrictTermKey key = new StrictTermKey(term); + Set cached = originsCache.get(key); if (cached != null) { return cached; } @@ -156,7 +161,7 @@ private Set collectSubtermOrigins(JTerm term, Map> or Set stored = result.isEmpty() ? Collections.emptySet() : Collections.unmodifiableSet(result); - originsCache.put(term, stored); + originsCache.put(key, stored); return stored; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/rule/label/PerpetualTermLabelPolicy.java b/key.core/src/main/java/de/uka/ilkd/key/rule/label/PerpetualTermLabelPolicy.java index 6b552737bad..07ba2dd9552 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/rule/label/PerpetualTermLabelPolicy.java +++ b/key.core/src/main/java/de/uka/ilkd/key/rule/label/PerpetualTermLabelPolicy.java @@ -3,14 +3,10 @@ * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.rule.label; -import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.label.TermLabel; -import de.uka.ilkd.key.logic.label.TermLabelState; -import de.uka.ilkd.key.proof.Goal; +import de.uka.ilkd.key.logic.label.TermLabelContext; -import org.key_project.prover.rules.Rule; -import org.key_project.prover.sequent.PosInOccurrence; /** * This policy always maintains a label. @@ -20,10 +16,8 @@ public class PerpetualTermLabelPolicy implements TermLabelPolicy { @Override - public TermLabel keepLabel(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm, - JTerm newTerm, TermLabel label) { + public TermLabel keepLabel(TermLabelContext context, JTerm sourceTerm, JTerm newTerm, + TermLabel label) { return label; } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/rule/label/RuleSpecificTask.java b/key.core/src/main/java/de/uka/ilkd/key/rule/label/RuleSpecificTask.java index cc61fb2e985..4193a27460e 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/rule/label/RuleSpecificTask.java +++ b/key.core/src/main/java/de/uka/ilkd/key/rule/label/RuleSpecificTask.java @@ -10,7 +10,6 @@ * Instances of this class provides functionality only if a supported rule is active. * * @author Martin Hentschel - * @see ChildTermLabelPolicy * @see TermLabelUpdate * @see TermLabelRefactoring */ diff --git a/key.core/src/main/java/de/uka/ilkd/key/rule/label/StayOnOperatorTermLabelPolicy.java b/key.core/src/main/java/de/uka/ilkd/key/rule/label/StayOnOperatorTermLabelPolicy.java index d6d0d26fe82..96230850fdd 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/rule/label/StayOnOperatorTermLabelPolicy.java +++ b/key.core/src/main/java/de/uka/ilkd/key/rule/label/StayOnOperatorTermLabelPolicy.java @@ -3,16 +3,12 @@ * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.rule.label; -import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.label.TermLabel; -import de.uka.ilkd.key.logic.label.TermLabelState; +import de.uka.ilkd.key.logic.label.TermLabelContext; import de.uka.ilkd.key.logic.op.JavaDLOperatorUtil; -import de.uka.ilkd.key.proof.Goal; import org.key_project.logic.op.Operator; -import org.key_project.prover.rules.Rule; -import org.key_project.prover.sequent.PosInOccurrence; /** * This {@link TermLabelPolicy} maintains a {@link TermLabel} as long the new {@link JTerm} has the @@ -25,12 +21,10 @@ public class StayOnOperatorTermLabelPolicy implements TermLabelPolicy { * {@inheritDoc} */ @Override - public TermLabel keepLabel(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm, - JTerm newTerm, TermLabel label) { - return applicationTerm != null - && JavaDLOperatorUtil.opEquals(newTerm.op(), applicationTerm.op()) + public TermLabel keepLabel(TermLabelContext context, JTerm sourceTerm, JTerm newTerm, + TermLabel label) { + return sourceTerm != null + && JavaDLOperatorUtil.opEquals(newTerm.op(), sourceTerm.op()) ? label : null; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/rule/label/TermLabelPolicy.java b/key.core/src/main/java/de/uka/ilkd/key/rule/label/TermLabelPolicy.java index 8117fd24dbc..4e93495124e 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/rule/label/TermLabelPolicy.java +++ b/key.core/src/main/java/de/uka/ilkd/key/rule/label/TermLabelPolicy.java @@ -3,17 +3,10 @@ * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.rule.label; -import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.label.TermLabel; +import de.uka.ilkd.key.logic.label.TermLabelContext; import de.uka.ilkd.key.logic.label.TermLabelManager; -import de.uka.ilkd.key.logic.label.TermLabelState; -import de.uka.ilkd.key.proof.Goal; -import de.uka.ilkd.key.proof.Proof; - -import org.key_project.prover.rules.Rule; -import org.key_project.prover.sequent.PosInOccurrence; -import org.key_project.prover.sequent.Sequent; /** @@ -21,11 +14,18 @@ * A {@link TermLabelPolicy} is used by * {@link TermLabelManager#instantiateLabels} * to decide for each {@link TermLabel} of an old {@link JTerm} if it should be re-added to the new - * {@link JTerm} or not. + * {@link JTerm} or not. Policies are registered per label {@link org.key_project.logic.Name} and + * are only asked for labels of that name. + *

    + *

    + * Execution order (part of the contract): within one term creation, policies run + * before all {@link TermLabelUpdate}s — first the application-term policies, then the + * modality-term policies. The label set produced by the policies is what the updates subsequently + * see and may transform. Do not design a label whose update must run before a policy. *

    *

    - * For more information about {@link TermLabel}s and how they are maintained during prove read the - * documentation of interface {@link TermLabel}. + * For more information about {@link TermLabel}s and how they are maintained during proof + * construction read the documentation of interface {@link TermLabel}. *

    * * @author Martin Hentschel @@ -35,31 +35,16 @@ public interface TermLabelPolicy { /** * Decides to keep (add to term which will be created) or to drop (do not add label to new term) - * the given {@link TermLabel} provided by the application {@link JTerm}. + * the given {@link TermLabel} provided by the source {@link JTerm}. * - * @param state The {@link TermLabelState} of the current rule application. - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param rule The {@link Rule} which is applied. - * @param goal The optional {@link Goal} on which the {@link JTerm} to create will be used. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional {@link JTerm} in the taclet which is responsible to - * instantiate - * the new {@link JTerm} for the new proof node or {@code null} in case of built in - * rules. + * @param context the {@link TermLabelContext} of the current rule application + * @param sourceTerm the {@link JTerm} whose labels are under consideration: the application + * term for application-term policies, the modality term for modality-term policies * @param newTerm the template for the new {@link JTerm} to create * @param label The {@link TermLabel} to decide if it should be kept or dropped. * @return The {@link TermLabel} to keep which might be a different one (e.g. with changed * parameters) or {@code null} if the {@link TermLabel} should be dropped. */ - TermLabel keepLabel(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm, - JTerm newTerm, TermLabel label); + TermLabel keepLabel(TermLabelContext context, JTerm sourceTerm, JTerm newTerm, + TermLabel label); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/rule/label/TermLabelRefactoring.java b/key.core/src/main/java/de/uka/ilkd/key/rule/label/TermLabelRefactoring.java index e530532abee..08a73aedf91 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/rule/label/TermLabelRefactoring.java +++ b/key.core/src/main/java/de/uka/ilkd/key/rule/label/TermLabelRefactoring.java @@ -3,13 +3,12 @@ * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.rule.label; -import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.TermBuilder; import de.uka.ilkd.key.logic.label.LabelCollection; import de.uka.ilkd.key.logic.label.TermLabel; +import de.uka.ilkd.key.logic.label.TermLabelContext; import de.uka.ilkd.key.logic.label.TermLabelManager; -import de.uka.ilkd.key.logic.label.TermLabelState; import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.proof.Proof; import de.uka.ilkd.key.proof.init.AbstractOperationPO; @@ -21,19 +20,16 @@ import de.uka.ilkd.key.rule.merge.CloseAfterMerge; import org.key_project.prover.rules.Rule; -import org.key_project.prover.sequent.PosInOccurrence; -import org.key_project.prover.sequent.Sequent; import org.key_project.prover.sequent.SequentFormula; /** *

    - * A {@link TermLabelRefactoring} is used by - * {@link TermLabelManager#refactorGoal(TermLabelState, Services, PosInOccurrence, Rule, Goal, Object, JTerm)} - * to refactor the labels of each visited {@link JTerm}. + * A {@link TermLabelRefactoring} is used by {@link TermLabelManager#refactorGoal} to refactor the + * labels of each visited {@link JTerm}. *

    *

    - * For more information about {@link TermLabel}s and how they are maintained during prove read the - * documentation of interface {@link TermLabel}. + * For more information about {@link TermLabel}s and how they are maintained during proof + * construction read the documentation of interface {@link TermLabel}. *

    * * @author Martin Hentschel @@ -94,47 +90,19 @@ static boolean shouldRefactorOnBuiltInRule(Rule rule, Goal goal, Object hint) { /** * Defines if a refactoring is required and if so in which {@link RefactoringScope}. * - * @param state The {@link TermLabelState} of the current rule application. - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param rule The {@link Rule} which is applied. - * @param goal The optional {@link Goal} on which the {@link JTerm} to create will be used. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional taclet {@link JTerm}. + * @param context the {@link TermLabelContext} of the current rule application * @return The required {@link RefactoringScope}. */ - RefactoringScope defineRefactoringScope(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm); + RefactoringScope defineRefactoringScope(TermLabelContext context); /** * This method is used to refactor the labels of the given {@link JTerm}. * - * @param state The {@link TermLabelState} of the current rule application. - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param rule The {@link Rule} which is applied. - * @param goal The optional {@link Goal} on which the {@link JTerm} to create will be used. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional taclet {@link JTerm}. + * @param context the {@link TermLabelContext} of the current rule application * @param term The {@link JTerm} which is now refactored. * @param labels The new labels the {@link JTerm} will have after the refactoring. */ - void refactorLabels(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, Goal goal, - Object hint, JTerm tacletTerm, JTerm term, LabelCollection labels); + void refactorLabels(TermLabelContext context, JTerm term, LabelCollection labels); /** * Possible refactoring scopes. @@ -153,11 +121,6 @@ enum RefactoringScope { */ APPLICATION_BELOW_UPDATES, - /** - * Refactor direct children of the application term. - */ - APPLICATION_DIRECT_CHILDREN, - /** * Refactor children and grandchildren of the application term. */ diff --git a/key.core/src/main/java/de/uka/ilkd/key/rule/label/TermLabelUpdate.java b/key.core/src/main/java/de/uka/ilkd/key/rule/label/TermLabelUpdate.java index 1dda30f1522..39812f1ea8e 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/rule/label/TermLabelUpdate.java +++ b/key.core/src/main/java/de/uka/ilkd/key/rule/label/TermLabelUpdate.java @@ -5,29 +5,26 @@ import java.util.Set; -import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.label.TermLabel; +import de.uka.ilkd.key.logic.label.TermLabelContext; import de.uka.ilkd.key.logic.label.TermLabelManager; -import de.uka.ilkd.key.logic.label.TermLabelState; -import de.uka.ilkd.key.proof.Goal; -import de.uka.ilkd.key.proof.Proof; - -import org.key_project.prover.rules.Rule; -import org.key_project.prover.rules.RuleApp; -import org.key_project.prover.sequent.PosInOccurrence; -import org.key_project.prover.sequent.Sequent; /** *

    - * A {@link TermLabelUpdate} is used by - * {@link TermLabelManager#instantiateLabels(TermLabelState, Services, PosInOccurrence, JTerm, Rule, RuleApp, Goal, Object, JTerm, JTerm)} - * to add or remove maintained {@link TermLabel}s which will be added to the new {@link JTerm}. + * A {@link TermLabelUpdate} is used by {@link TermLabelManager#instantiateLabels} to add or + * remove maintained {@link TermLabel}s which will be added to the new {@link JTerm}. + *

    + *

    + * Execution order (part of the contract): updates run after all + * {@link TermLabelPolicy} instances; the label set passed to {@code updateLabels} already + * contains the taclet-provided labels and the labels kept by the policies. Rule-specific updates + * (see {@link #getSupportedRuleNames()}) run before rule-independent ones. *

    *

    - * For more information about {@link TermLabel}s and how they are maintained during prove read the - * documentation of interface {@link TermLabel}. + * For more information about {@link TermLabel}s and how they are maintained during proof + * construction read the documentation of interface {@link TermLabel}. *

    * * @author Martin Hentschel @@ -36,34 +33,12 @@ */ public interface TermLabelUpdate extends RuleSpecificTask { /** - * This method can freely add, remove or sort the given {@link TermLabel} which will be added to - * the new {@link JTerm}. + * This method can freely add, remove or sort the given {@link TermLabel}s which will be added + * to the new {@link JTerm}. * - * @param state The {@link TermLabelState} of the current rule application. return {@code true} - * keep {@link TermLabel} and add it to the new {@link JTerm}. {@code false} drop - * {@link TermLabel} and do not need it to the new {@link JTerm}. - * @param services The {@link Services} used by the {@link Proof} on which a {@link Rule} is - * applied right now. - * @param applicationPosInOccurrence The {@link PosInOccurrence} in the previous {@link Sequent} - * which defines the {@link JTerm} that is rewritten. - * @param applicationTerm The {@link JTerm} defined by the {@link PosInOccurrence} in the - * previous {@link Sequent}. - * @param modalityTerm The optional modality {@link JTerm}. - * @param rule The {@link Rule} which is applied. - * @param ruleApp The {@link RuleApp} which is currently performed. - * @param hint An optional hint passed from the active rule to describe the term which should be - * created. - * @param tacletTerm The optional {@link JTerm} in the taclet which is responsible to - * instantiate - * the new {@link JTerm} for the new proof node or {@code null} in case of built in - * rules. + * @param context the {@link TermLabelContext} of the current rule application * @param newTerm the template for the new {@link JTerm} to create * @param labels The {@link Set} of {@link TermLabel}s to modify. */ - void updateLabels(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, JTerm modalityTerm, - Rule rule, RuleApp ruleApp, Object hint, JTerm tacletTerm, - JTerm newTerm, - Set labels); + void updateLabels(TermLabelContext context, JTerm newTerm, Set labels); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/HideCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/HideCommand.java index a4aa880fb0f..b1e88c367dd 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/HideCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/HideCommand.java @@ -23,7 +23,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -import static de.uka.ilkd.key.logic.equality.TermLabelsProperty.TERM_LABELS_PROPERTY; /** * Proof script command to hide formulas from the sequent. @@ -88,7 +87,7 @@ private SequentFormula find( for (SequentFormula s : semiseq) { Term term = s.formula(); Term formula = sf.formula(); - if ((TERM_LABELS_PROPERTY).equalsModThisProperty(term, formula)) { + if (term.equals(formula)) { return s; } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/speclang/ContractFactory.java b/key.core/src/main/java/de/uka/ilkd/key/speclang/ContractFactory.java index 1b96b9a1b12..a3989ca2545 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/speclang/ContractFactory.java +++ b/key.core/src/main/java/de/uka/ilkd/key/speclang/ContractFactory.java @@ -34,7 +34,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static de.uka.ilkd.key.logic.equality.TermLabelsProperty.TERM_LABELS_PROPERTY; import static de.uka.ilkd.key.logic.label.OriginTermLabel.Origin; /** @@ -511,9 +510,9 @@ private static void combineModifiable(FunctionalOperationContractImpl t, // check if the other modifiable is the same as the one in the uniform store. // To obtain meaningful results, check for equality ignoring all term labels! + // (plain term equality now ignores term labels) if (uniformModifiable.containsKey(h)) { - if (!TERM_LABELS_PROPERTY.equalsModThisProperty(uniformModifiable.get(h), - modifiable2)) { + if (!uniformModifiable.get(h).equals(modifiable2)) { uniformModifiable.remove(h); } else { // merge term labels (in particular origin labels) of both modifiable diff --git a/key.core/src/test/java/de/uka/ilkd/key/logic/LabeledTermImplTest.java b/key.core/src/test/java/de/uka/ilkd/key/logic/LabeledTermImplTest.java index b63dd7f6108..d6932e28cba 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/logic/LabeledTermImplTest.java +++ b/key.core/src/test/java/de/uka/ilkd/key/logic/LabeledTermImplTest.java @@ -15,7 +15,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertNotEquals; public class LabeledTermImplTest { @@ -40,10 +39,69 @@ public void testEqualsLabelOnTop() { JTerm labeledTerm = tf.createTerm(Junctor.AND, tf.createTerm(Junctor.TRUE), tf.createTerm(Junctor.FALSE), labels); - assertNotEquals(labeledTerm, unlabeledTerm, - "Labeled and unlabeled terms must not be equal"); - assertNotEquals(unlabeledTerm, labeledTerm, - "Labeled and unlabeled terms must not be equal"); + // equals ignores term labels ... + Assertions.assertEquals(labeledTerm, unlabeledTerm, + "equals must ignore term labels"); + Assertions.assertEquals(unlabeledTerm, labeledTerm, + "equals must ignore term labels"); + Assertions.assertEquals(labeledTerm.hashCode(), unlabeledTerm.hashCode(), + "hashCode must ignore term labels"); + // ... while equalsIncludingLabels does not + Assertions.assertFalse(labeledTerm.equalsIncludingLabels(unlabeledTerm), + "equalsIncludingLabels must distinguish labeled and unlabeled terms"); + Assertions.assertFalse(unlabeledTerm.equalsIncludingLabels(labeledTerm), + "equalsIncludingLabels must distinguish labeled and unlabeled terms"); + Assertions.assertTrue(labeledTerm.equalsIncludingLabels(labeledTerm)); + } + + /** + * Labels on subterms must be distinguished by + * {@link JTerm#equalsIncludingLabels(Object)}, too. + */ + @Test + public void testEqualsLabelOnSubterm() { + JTerm labeledSub = tf.createTerm(Junctor.TRUE, + new ImmutableArray<>(), null, + new ImmutableArray<>(ParameterlessTermLabel.ANON_HEAP_LABEL)); + JTerm labeledBelow = + tf.createTerm(Junctor.AND, labeledSub, tf.createTerm(Junctor.FALSE)); + JTerm unlabeled = + tf.createTerm(Junctor.AND, tf.createTerm(Junctor.TRUE), tf.createTerm(Junctor.FALSE)); + + Assertions.assertTrue(labeledBelow.containsLabelsRecursive()); + Assertions.assertFalse(unlabeled.containsLabelsRecursive()); + Assertions.assertEquals(labeledBelow, unlabeled); + Assertions.assertFalse(labeledBelow.equalsIncludingLabels(unlabeled)); + Assertions.assertFalse(unlabeled.equalsIncludingLabels(labeledBelow)); + } + + /** + * A caching {@link TermFactory} must intern labeled terms label-sensitively: two + * independently built, label-identical terms are the same object ({@code ==}), while a label + * variant and the unlabeled term are kept distinct. This guards the {@code ==} fast paths and + * memory sharing for the (very common) labeled terms. + */ + @Test + public void testCachedFactoryInternsLabeledTerms() { + TermFactory ctf = + new TermFactory(new java.util.HashMap<>()); + ImmutableArray lbl = + new ImmutableArray<>(ParameterlessTermLabel.ANON_HEAP_LABEL); + + JTerm labeled1 = ctf.createTerm(Junctor.AND, + new ImmutableArray<>(ctf.createTerm(Junctor.TRUE), ctf.createTerm(Junctor.FALSE)), + null, lbl); + JTerm labeled2 = ctf.createTerm(Junctor.AND, + new ImmutableArray<>(ctf.createTerm(Junctor.TRUE), ctf.createTerm(Junctor.FALSE)), + null, lbl); + JTerm unlabeled = ctf.createTerm(Junctor.AND, + new ImmutableArray<>(ctf.createTerm(Junctor.TRUE), ctf.createTerm(Junctor.FALSE)), + null, null); + + Assertions.assertSame(labeled1, labeled2, "identical labeled terms must be interned"); + Assertions.assertNotSame(labeled1, unlabeled, + "labeled and unlabeled variants must not be interned together"); + Assertions.assertEquals(labeled1.labeledHashCode(), labeled2.labeledHashCode()); } /** diff --git a/key.core/src/test/java/de/uka/ilkd/key/logic/TestTermLabelManager.java b/key.core/src/test/java/de/uka/ilkd/key/logic/TestTermLabelManager.java index 37648085ca4..cbb2fe39d6f 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/logic/TestTermLabelManager.java +++ b/key.core/src/test/java/de/uka/ilkd/key/logic/TestTermLabelManager.java @@ -10,6 +10,7 @@ import de.uka.ilkd.key.java.ast.expression.literal.IntLiteral; import de.uka.ilkd.key.ldt.IntegerLDT; import de.uka.ilkd.key.logic.label.*; +import de.uka.ilkd.key.logic.label.TermLabelContext; import de.uka.ilkd.key.logic.label.TermLabelManager.TermLabelConfiguration; import de.uka.ilkd.key.logic.op.LocationVariable; import de.uka.ilkd.key.proof.*; @@ -18,7 +19,6 @@ import de.uka.ilkd.key.proof.init.JavaProfile; import de.uka.ilkd.key.proof.init.Profile; import de.uka.ilkd.key.proof.io.ProblemLoaderException; -import de.uka.ilkd.key.rule.label.ChildTermLabelPolicy; import de.uka.ilkd.key.rule.label.TermLabelPolicy; import de.uka.ilkd.key.rule.label.TermLabelRefactoring; import de.uka.ilkd.key.rule.label.TermLabelRefactoring.RefactoringScope; @@ -29,7 +29,6 @@ import org.key_project.logic.PosInTerm; import org.key_project.prover.rules.Rule; import org.key_project.prover.rules.RuleAbortException; -import org.key_project.prover.rules.RuleApp; import org.key_project.prover.rules.RuleExecutor; import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.prover.sequent.Sequent; @@ -65,19 +64,6 @@ public void testrefactorGoal_childrenAndGrandchildren_ruleSpecific() } - @Test - public void testrefactorGoal_directChildren_allRules() throws ProblemLoaderException { - doRefactoringTestLogging(true, true, RefactoringScope.APPLICATION_DIRECT_CHILDREN); - } - - /** - * - */ - @Test - public void testrefactorGoal_directChildren_ruleSpecific() throws ProblemLoaderException { - doRefactoringTestLogging(true, false, RefactoringScope.APPLICATION_DIRECT_CHILDREN, "rule"); - } - /** * */ @@ -114,7 +100,7 @@ protected void doRefactoringTestLogging(boolean ruleChanged, boolean notSupporte RefactoringScope scope, String... supportedRules) throws ProblemLoaderException { LoggingTermLabelRefactoring refactoring = new LoggingTermLabelRefactoring(scope, supportedRules); - InitConfig initConfig = createTestServices(null, null, null, null, null, refactoring); + InitConfig initConfig = createTestServices(null, null, null, refactoring); Services services = initConfig.getServices(); TermBuilder TB = services.getTermBuilder(); // Create sequent @@ -192,14 +178,6 @@ protected void compareTerms(JTerm expected, JTerm current, boolean changed, } else { assertEquals(expectedName, current.getLabels().get(i).name().toString()); } - } else if (RefactoringScope.APPLICATION_DIRECT_CHILDREN.equals(scope)) { - String expectedName = expected.getLabels().get(i).name().toString(); - if ("ONE".equals(expectedName) || "ADD".equals(expectedName)) { - assertEquals(expectedName + "-CHANGED", - current.getLabels().get(i).name().toString()); - } else { - assertEquals(expectedName, current.getLabels().get(i).name().toString()); - } } else { fail("Unsupported scope \"" + scope + "\"."); } @@ -217,7 +195,7 @@ public void testInstantiateLabels_updates_allRules() { LoggingTermLabelUpdate update = new LoggingTermLabelUpdate(new ParameterlessTermLabel(new Name("UPDATED"))); Services services = Assertions.assertDoesNotThrow( - () -> createTestServices(null, null, null, null, update, null).getServices()); + () -> createTestServices(null, null, update, null).getServices()); PosInOccurrence pos = createTestPosInOccurrence(services); Rule rule = new DummyRule("rule"); JTerm taclet = services.getTermBuilder().tt(); @@ -244,7 +222,7 @@ public void testInstantiateLabels_updates_ruleSpecific() { LoggingTermLabelUpdate update = new LoggingTermLabelUpdate(new ParameterlessTermLabel(new Name("UPDATED")), "rule"); Services services = Assertions.assertDoesNotThrow( - () -> createTestServices(null, null, null, null, update, null).getServices()); + () -> createTestServices(null, null, update, null).getServices()); PosInOccurrence pos = createTestPosInOccurrence(services); Rule rule = new DummyRule("rule"); JTerm taclet = services.getTermBuilder().tt(); @@ -262,161 +240,6 @@ public void testInstantiateLabels_updates_ruleSpecific() { assertEquals(0, labels.size()); } - /** - * - */ - @Test - public void testInstantiateLabels_childAndGrandchildPolicies_allRules() { - LoggingChildTermLabelPolicy policy = new LoggingChildTermLabelPolicy(); - Services services = Assertions.assertDoesNotThrow( - () -> createTestServices(null, null, null, policy, null, null).getServices()); - PosInOccurrence pos = createTestPosInOccurrence(services); - Rule rule = new DummyRule("rule"); - JTerm taclet = services.getTermBuilder().tt(); - // Create labels - ImmutableArray labels = TermLabelManager.instantiateLabels(new TermLabelState(), - services, pos, rule, null, null, null, taclet, null); - assertNotNull(labels); - assertEquals(4, labels.size()); - assertEquals("ONE", labels.get(0).name().toString()); - assertEquals("ADD", labels.get(1).name().toString()); - assertEquals("TWO", labels.get(2).name().toString()); - assertEquals("THREE", labels.get(3).name().toString()); - // Test log - assertEquals(4, policy.getLog().size()); - assertEquals("ONE", policy.getLog().get(0).name().toString()); - assertEquals("ADD", policy.getLog().get(1).name().toString()); - assertEquals("TWO", policy.getLog().get(2).name().toString()); - assertEquals("THREE", policy.getLog().get(3).name().toString()); - // Test other not supported rule - Rule otherRule = new DummyRule("notSupportedRule"); - labels = TermLabelManager.instantiateLabels(new TermLabelState(), services, pos, otherRule, - null, null, null, taclet, null); - assertNotNull(labels); - assertEquals(4, labels.size()); - assertEquals("ONE", labels.get(0).name().toString()); - assertEquals("ADD", labels.get(1).name().toString()); - assertEquals("TWO", labels.get(2).name().toString()); - assertEquals("THREE", labels.get(3).name().toString()); - // Test log - assertEquals(8, policy.getLog().size()); - assertEquals("ONE", policy.getLog().get(0).name().toString()); - assertEquals("ADD", policy.getLog().get(1).name().toString()); - assertEquals("TWO", policy.getLog().get(2).name().toString()); - assertEquals("THREE", policy.getLog().get(3).name().toString()); - assertEquals("ONE", policy.getLog().get(4).name().toString()); - assertEquals("ADD", policy.getLog().get(5).name().toString()); - assertEquals("TWO", policy.getLog().get(6).name().toString()); - assertEquals("THREE", policy.getLog().get(7).name().toString()); - } - - /** - * - */ - @Test - public void testInstantiateLabels_childAndGrandchildPolicies_ruleSpecific() { - LoggingChildTermLabelPolicy policy = new LoggingChildTermLabelPolicy("rule"); - Services services = Assertions.assertDoesNotThrow( - () -> createTestServices(null, null, null, policy, null, null).getServices()); - PosInOccurrence pos = createTestPosInOccurrence(services); - Rule rule = new DummyRule("rule"); - JTerm taclet = services.getTermBuilder().tt(); - // Create labels - ImmutableArray labels = TermLabelManager.instantiateLabels(new TermLabelState(), - services, pos, rule, null, null, null, taclet, null); - assertNotNull(labels); - assertEquals(4, labels.size()); - assertEquals("ONE", labels.get(0).name().toString()); - assertEquals("ADD", labels.get(1).name().toString()); - assertEquals("TWO", labels.get(2).name().toString()); - assertEquals("THREE", labels.get(3).name().toString()); - // Test log - assertEquals(4, policy.getLog().size()); - assertEquals("ONE", policy.getLog().get(0).name().toString()); - assertEquals("ADD", policy.getLog().get(1).name().toString()); - assertEquals("TWO", policy.getLog().get(2).name().toString()); - assertEquals("THREE", policy.getLog().get(3).name().toString()); - // Test other not supported rule - Rule otherRule = new DummyRule("notSupportedRule"); - labels = TermLabelManager.instantiateLabels(new TermLabelState(), services, pos, otherRule, - null, null, null, taclet, null); - assertNotNull(labels); - assertEquals(0, labels.size()); - // Test log - assertEquals(4, policy.getLog().size()); - } - - /** - * - * @throws ProblemLoaderException - * Occurred Exception - */ - @Test - public void testInstantiateLabels_directChildPolicies_allRules() throws ProblemLoaderException { - LoggingChildTermLabelPolicy policy = new LoggingChildTermLabelPolicy(); - Services services = createTestServices(null, null, policy, null, null, null).getServices(); - PosInOccurrence pos = createTestPosInOccurrence(services); - Rule rule = new DummyRule("rule"); - JTerm taclet = services.getTermBuilder().tt(); - // Create labels - ImmutableArray labels = TermLabelManager.instantiateLabels(new TermLabelState(), - services, pos, rule, null, null, null, taclet, null); - assertNotNull(labels); - assertEquals(2, labels.size()); - assertEquals("ONE", labels.get(0).name().toString()); - assertEquals("ADD", labels.get(1).name().toString()); - // Test log - assertEquals(2, policy.getLog().size()); - assertEquals("ONE", policy.getLog().get(0).name().toString()); - assertEquals("ADD", policy.getLog().get(1).name().toString()); - // Test other not supported rule - Rule otherRule = new DummyRule("notSupportedRule"); - labels = TermLabelManager.instantiateLabels(new TermLabelState(), services, pos, otherRule, - null, null, null, taclet, null); - assertNotNull(labels); - assertEquals(2, labels.size()); - assertEquals("ONE", labels.get(0).name().toString()); - assertEquals("ADD", labels.get(1).name().toString()); - // Test log - assertEquals(4, policy.getLog().size()); - assertEquals("ONE", policy.getLog().get(0).name().toString()); - assertEquals("ADD", policy.getLog().get(1).name().toString()); - assertEquals("ONE", policy.getLog().get(2).name().toString()); - assertEquals("ADD", policy.getLog().get(3).name().toString()); - } - - /** - * - */ - @Test - public void testInstantiateLabels_directChildPolicies_ruleSpecific() { - LoggingChildTermLabelPolicy policy = new LoggingChildTermLabelPolicy("rule"); - Services services = Assertions.assertDoesNotThrow( - () -> createTestServices(null, null, policy, null, null, null).getServices()); - PosInOccurrence pos = createTestPosInOccurrence(services); - Rule rule = new DummyRule("rule"); - JTerm taclet = services.getTermBuilder().tt(); - // Create labels - ImmutableArray labels = TermLabelManager.instantiateLabels(new TermLabelState(), - services, pos, rule, null, null, null, taclet, null); - assertNotNull(labels); - assertEquals(2, labels.size()); - assertEquals("ONE", labels.get(0).name().toString()); - assertEquals("ADD", labels.get(1).name().toString()); - // Test log - assertEquals(2, policy.getLog().size()); - assertEquals("ONE", policy.getLog().get(0).name().toString()); - assertEquals("ADD", policy.getLog().get(1).name().toString()); - // Test other not supported rule - Rule otherRule = new DummyRule("notSupportedRule"); - labels = TermLabelManager.instantiateLabels(new TermLabelState(), services, pos, otherRule, - null, null, null, taclet, null); - assertNotNull(labels); - assertEquals(0, labels.size()); - // Test log - assertEquals(2, policy.getLog().size()); - } - /** * */ @@ -424,7 +247,7 @@ public void testInstantiateLabels_directChildPolicies_ruleSpecific() { public void testInstantiateLabels_modalityTermPolicies() { LoggingTermLabelPolicy policy = new LoggingTermLabelPolicy(); Services services = Assertions.assertDoesNotThrow( - () -> createTestServices(null, policy, null, null, null, null).getServices()); + () -> createTestServices(null, policy, null, null).getServices()); TermBuilder TB = services.getTermBuilder(); JTerm modality = TB.label( TB.box(JavaBlock.EMPTY_JAVABLOCK, @@ -457,7 +280,7 @@ public void testInstantiateLabels_modalityTermPolicies() { public void testInstantiateLabels_applicationTermPolicies() { LoggingTermLabelPolicy policy = new LoggingTermLabelPolicy(); Services services = Assertions.assertDoesNotThrow( - () -> createTestServices(policy, null, null, null, null, null).getServices()); + () -> createTestServices(policy, null, null, null).getServices()); PosInOccurrence pos = createTestPosInOccurrence(services); JTerm taclet = services.getTermBuilder().tt(); Rule rule = new DummyRule("rule"); @@ -478,7 +301,7 @@ public void testInstantiateLabels_applicationTermPolicies() { @Test public void testInstantiateLabels_taclet() { Services services = Assertions.assertDoesNotThrow( - () -> createTestServices(null, null, null, null, null, null).getServices()); + () -> createTestServices(null, null, null, null).getServices()); PosInOccurrence pos = createTestPosInOccurrence(services); Rule rule = new DummyRule("rule"); JTerm taclet = services.getTermBuilder().label(services.getTermBuilder().tt(), @@ -526,7 +349,7 @@ protected JTerm createTestTerm(Services services) { @Test public void testParseLabel() throws TermLabelException { Services services = Assertions.assertDoesNotThrow( - () -> createTestServices(null, null, null, null, null, null).getServices()); + () -> createTestServices(null, null, null, null).getServices()); TermLabelManager manager = TermLabelManager.getTermLabelManager(services); // Test null parameter TermLabel label = manager.parseLabel("ONE", null, services); @@ -564,7 +387,7 @@ public void testGetSupportedTermLabelNames() { assertTrue(names.isEmpty()); // Test services Services services = Assertions.assertDoesNotThrow( - () -> createTestServices(null, null, null, null, null, null).getServices()); + () -> createTestServices(null, null, null, null).getServices()); names = TermLabelManager.getSupportedTermLabelNames(services); assertNotNull(names); assertEquals(5, names.size()); @@ -590,8 +413,7 @@ public void testGetTermLabelManager() { } protected InitConfig createTestServices(final TermLabelPolicy applicationTermPolicy, - final TermLabelPolicy modalityTermPolicy, final ChildTermLabelPolicy directChildPolicy, - final ChildTermLabelPolicy childAndGrandchildPolicy, final TermLabelUpdate update, + final TermLabelPolicy modalityTermPolicy, final TermLabelUpdate update, final TermLabelRefactoring refactoring) throws ProblemLoaderException { if (CONFIG == null) { KeYEnvironment env = null; @@ -620,18 +442,6 @@ protected ImmutableList computeTermLabelConfiguration() if (modalityTermPolicy != null) { modalityTermPolicies = modalityTermPolicies.prepend(modalityTermPolicy); } - ImmutableList directChildTermLabelPolicies = - ImmutableList.nil(); - if (directChildPolicy != null) { - directChildTermLabelPolicies = - directChildTermLabelPolicies.prepend(directChildPolicy); - } - ImmutableList childAndGrandchildTermLabelPolicies = - ImmutableList.nil(); - if (childAndGrandchildPolicy != null) { - childAndGrandchildTermLabelPolicies = - childAndGrandchildTermLabelPolicies.prepend(childAndGrandchildPolicy); - } ImmutableList termLabelUpdates = ImmutableList.nil(); if (update != null) { termLabelUpdates = termLabelUpdates.prepend(update); @@ -645,28 +455,23 @@ protected ImmutableList computeTermLabelConfiguration() ImmutableList result = ImmutableList.nil(); result = result.prepend(new TermLabelConfiguration(new Name("ONE"), new LoggingFactory(new Name("ONE")), applicationTermPolicies, - modalityTermPolicies, directChildTermLabelPolicies, - childAndGrandchildTermLabelPolicies, termLabelUpdates, + modalityTermPolicies, termLabelUpdates, termLabelRefactorings, null)); result = result.prepend(new TermLabelConfiguration(new Name("TWO"), new LoggingFactory(new Name("TWO")), applicationTermPolicies, - modalityTermPolicies, directChildTermLabelPolicies, - childAndGrandchildTermLabelPolicies, termLabelUpdates, + modalityTermPolicies, termLabelUpdates, termLabelRefactorings, null)); result = result.prepend(new TermLabelConfiguration(new Name("THREE"), new LoggingFactory(new Name("THREE")), applicationTermPolicies, - modalityTermPolicies, directChildTermLabelPolicies, - childAndGrandchildTermLabelPolicies, termLabelUpdates, + modalityTermPolicies, termLabelUpdates, termLabelRefactorings, null)); result = result.prepend(new TermLabelConfiguration(new Name("ADD"), new LoggingFactory(new Name("ADD")), applicationTermPolicies, - modalityTermPolicies, directChildTermLabelPolicies, - childAndGrandchildTermLabelPolicies, termLabelUpdates, + modalityTermPolicies, termLabelUpdates, termLabelRefactorings, null)); result = result.prepend(new TermLabelConfiguration(new Name("APPLICATION"), new LoggingFactory(new Name("APPLICATION")), applicationTermPolicies, - modalityTermPolicies, directChildTermLabelPolicies, - childAndGrandchildTermLabelPolicies, termLabelUpdates, + modalityTermPolicies, termLabelUpdates, termLabelRefactorings, null)); return result; } @@ -692,18 +497,12 @@ public ImmutableList getSupportedRuleNames() { } @Override - public RefactoringScope defineRefactoringScope(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, - Goal goal, Object hint, JTerm tacletTerm) { + public RefactoringScope defineRefactoringScope(TermLabelContext context) { return scope; } @Override - public void refactorLabels(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, - Goal goal, Object hint, JTerm tacletTerm, JTerm term, LabelCollection labels) { + public void refactorLabels(TermLabelContext context, JTerm term, LabelCollection labels) { List changedLabels = new LinkedList<>(); boolean changed = labels.isModified(); for (TermLabel label : labels.getLabels()) { @@ -737,63 +536,18 @@ public ImmutableList getSupportedRuleNames() { } @Override - public void updateLabels(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, JTerm modalityTerm, - org.key_project.prover.rules.Rule rule, - RuleApp ruleApp, Object hint, JTerm tacletTerm, JTerm newTerm, + public void updateLabels(TermLabelContext context, JTerm newTerm, Set labels) { labels.add(toAdd); } } - private static class LoggingChildTermLabelPolicy implements ChildTermLabelPolicy { - private ImmutableList supportedRuleNames = ImmutableList.nil(); - - private final List log = new LinkedList<>(); - - public LoggingChildTermLabelPolicy(String... supportedRules) { - for (String rule : supportedRules) { - supportedRuleNames = supportedRuleNames.prepend(new Name(rule)); - } - } - - @Override - public ImmutableList getSupportedRuleNames() { - return supportedRuleNames; - } - - @Override - public boolean isRuleApplicationSupported(TermServices services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, - Goal goal, Object hint, JTerm tacletTerm, JTerm newTerm) { - return true; - } - - @Override - public boolean addLabel(TermServices services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, Goal goal, Object hint, JTerm tacletTerm, - JTerm newTerm, JTerm childTerm, TermLabel label) { - log.add(label); - return true; - } - - public List getLog() { - return log; - } - } - private static class LoggingTermLabelPolicy implements TermLabelPolicy { private final List log = new LinkedList<>(); @Override - public TermLabel keepLabel(TermLabelState state, Services services, - PosInOccurrence applicationPosInOccurrence, - JTerm applicationTerm, Rule rule, - Goal goal, Object hint, JTerm tacletTerm, - JTerm newTerm, TermLabel label) { + public TermLabel keepLabel(TermLabelContext context, JTerm sourceTerm, JTerm newTerm, + TermLabel label) { log.add(label); return label; } diff --git a/key.core/src/test/java/de/uka/ilkd/key/logic/label/TermLabelEquivalenceDumpTest.java b/key.core/src/test/java/de/uka/ilkd/key/logic/label/TermLabelEquivalenceDumpTest.java new file mode 100644 index 00000000000..8ea228ef487 --- /dev/null +++ b/key.core/src/test/java/de/uka/ilkd/key/logic/label/TermLabelEquivalenceDumpTest.java @@ -0,0 +1,196 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.logic.label; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Iterator; +import java.util.Set; +import java.util.TreeSet; + +import de.uka.ilkd.key.control.KeYEnvironment; +import de.uka.ilkd.key.logic.JTerm; +import de.uka.ilkd.key.proof.Node; +import de.uka.ilkd.key.proof.Proof; +import de.uka.ilkd.key.settings.ProofSettings; + +import org.key_project.prover.sequent.Sequent; +import org.key_project.prover.sequent.SequentFormula; +import org.key_project.util.collection.ImmutableArray; + +import org.junit.jupiter.api.Test; + +/** + * Regression harness for the term-label reworking (equality flip + LabeledTermImpl/TermImpl + * merge). It replays a curated set of console-style KeY proofs and writes, for every proof node, + * a canonical serialization of the sequent including all term labels at every subterm. + * + *

    + * The dump is written to the directory given by the {@code labeldump.dir} system property (default + * a temp dir). Running this test on two revisions and diffing the two output directories proves + * that the change did not alter any sequent or any term label anywhere in these proofs. The test + * also records which term labels were actually observed ({@code _labels_seen.txt}) so label + * coverage is established from real data rather than assumed. + *

    + * + *

    + * This is intentionally driven by an explicit, hand-written term serializer rather than + * {@link Object#toString()} so that label capture does not depend on any pretty-printer. + *

    + */ +public class TermLabelEquivalenceDumpTest { + + /** + * Proof files relative to the repository root. Chosen to be replayable headlessly and to + * collectively exercise the term labels registered by the plain {@code JavaProfile} (origin, + * anonHeap, selectSK, ...). Labels used only by the symbolic_execution / proof_references + * modules are deliberately out of scope. + */ + private static final String[] PROOFS = { + // heap / JML operation & dependency contracts (anonHeap, selectSK, impl, origin, ...) + "key.ui/examples/heap/observer/ExampleObserver_ExampleObserver.key.proof", + "key.ui/examples/heap/observer/ExampleSubject_ExampleSubject.key.proof", + "key.ui/examples/heap/observer/ExampleSubject_notifyObservers.key.proof", + "key.ui/examples/heap/observer/ExampleSubject_inv.key.proof", + "key.ui/examples/heap/observer/ExampleSubject_change.key.proof", + "key.ui/examples/heap/observer/ExampleObserver_value.key.proof", + "key.ui/examples/heap/observer/ExampleSubject_addObserver.key.proof", + "key.ui/examples/heap/observer/ExampleSubject_value_dep.key.proof", + "key.ui/examples/heap/observer/ExampleObserver_update.key.proof", + "key.ui/examples/heap/verifyThis11_1_Maximum/project.key.proof", + "key.ui/examples/heap/coincidence_count/project.key.proof", + "key.ui/examples/heap/fm12_01_LRS/lcp.key.proof", + "key.ui/examples/heap/permutedSum/perm.proof", + "key.ui/examples/heap/BoyerMoore/BM(BM__monoLemma((I,int,int)).JML normal_behavior operation contract.0.proof", + // java_dl / arithmetic / bit operations (loopScopeIndex, SC, ...) + "key.ui/examples/standard_key/java_dl/recursion/triangular.proof", + "key.ui/examples/standard_key/arith/jdivevenodd.key.proof", + "key.ui/examples/standard_key/bitoperations/exBitwiseOr1.key.proof", + }; + + /** + * Term labels that are actually attached during standard (console) proof search and therefore + * must be observed while replaying {@link #PROOFS}. Note: {@code postCondition}, {@code undef} + * and {@code selfComposedExecution} are attached only by the information-flow module + * (key.core.infflow) and {@code Trigger} only by the SMT translation, so none of them is + * reachable by replaying standard Java profile proofs; the infflow labels are covered by the + * separate testRunAllInfProofs regression instead. + */ + private static final Set EXPECTED_LABELS = Set.of( + "origin", "anonHeapFunction", "selectSK", "impl", "SC", "loopScopeIndex"); + + @Test + public void dumpAllProofSequents() throws Exception { + final Path dumpDir = Paths.get(System.getProperty("key.labeldump.dir", + System.getProperty("java.io.tmpdir") + "/labeldump")); + Files.createDirectories(dumpDir); + final Path root = repoRoot(); + + // origin labels are user-facing and only attached when this setting is on + ProofSettings.DEFAULT_SETTINGS.getTermLabelSettings().setUseOriginLabels(true); + + final Set labelsSeen = new TreeSet<>(); + final StringBuilder index = new StringBuilder(); + int loaded = 0; + for (String rel : PROOFS) { + final Path pf = root.resolve(rel); + if (!Files.exists(pf)) { + index.append("MISSING ").append(rel).append('\n'); + continue; + } + KeYEnvironment env = null; + try { + env = KeYEnvironment.load(pf); + final Proof proof = env.getLoadedProof(); + final StringBuilder sb = new StringBuilder(); + int nodes = 0; + final Iterator it = proof.root().subtreeIterator(); + while (it.hasNext()) { + final Node n = it.next(); + sb.append("=== node ").append(n.serialNr()).append(" ===\n"); + dumpSequent(n.sequent(), sb, labelsSeen); + nodes++; + } + Files.writeString(dumpDir.resolve(dumpName(rel)), sb.toString()); + index.append("OK ").append(nodes).append(" nodes ").append(rel).append('\n'); + loaded++; + } catch (Exception e) { + index.append("FAILED ").append(rel).append(" : ").append(e).append('\n'); + } finally { + if (env != null) { + env.dispose(); + } + } + } + Files.writeString(dumpDir.resolve("_index.txt"), index.toString()); + Files.writeString(dumpDir.resolve("_labels_seen.txt"), String.join("\n", labelsSeen)); + System.out.println("[labeldump] wrote " + loaded + "/" + PROOFS.length + + " proof dumps to " + dumpDir); + System.out.println("[labeldump] labels observed: " + labelsSeen); + + org.junit.jupiter.api.Assertions.assertTrue(loaded >= 15, + "expected at least 15 of the curated proofs to replay, got " + loaded); + org.junit.jupiter.api.Assertions.assertTrue(labelsSeen.containsAll(EXPECTED_LABELS), + "term-label coverage regressed; expected " + EXPECTED_LABELS + " but saw " + + labelsSeen); + } + + private static void dumpSequent(Sequent seq, StringBuilder sb, Set labelsSeen) { + for (SequentFormula sf : seq.antecedent()) { + dumpTerm((JTerm) sf.formula(), sb, labelsSeen); + sb.append('\n'); + } + sb.append("==>\n"); + for (SequentFormula sf : seq.succedent()) { + dumpTerm((JTerm) sf.formula(), sb, labelsSeen); + sb.append('\n'); + } + } + + /** Recursively serialize a term, emitting the (sorted) label set at every node. */ + private static void dumpTerm(JTerm t, StringBuilder sb, Set labelsSeen) { + sb.append(t.op().name()); + if (t.hasLabels()) { + final ImmutableArray labels = t.getLabels(); + final TreeSet here = new TreeSet<>(); + for (int i = 0, sz = labels.size(); i < sz; i++) { + final String s = labels.get(i).toString(); + here.add(s); + labelsSeen.add(labels.get(i).name().toString()); + } + sb.append("<<").append(String.join(",", here)).append(">>"); + } + if (!t.boundVars().isEmpty()) { + sb.append('{').append(t.boundVars()).append('}'); + } + if (t.arity() > 0) { + sb.append('('); + for (int i = 0; i < t.arity(); i++) { + if (i > 0) { + sb.append(','); + } + dumpTerm(t.sub(i), sb, labelsSeen); + } + sb.append(')'); + } + } + + private static String dumpName(String rel) { + return rel.replaceAll("[^A-Za-z0-9._-]", "_") + ".dump"; + } + + /** Walk up from the working directory until the KeY example tree is found. */ + private static Path repoRoot() { + Path p = Paths.get("").toAbsolutePath(); + while (p != null) { + if (Files.exists(p.resolve("key.ui/examples/heap"))) { + return p; + } + p = p.getParent(); + } + throw new IllegalStateException("could not locate repository root from " + + Paths.get("").toAbsolutePath()); + } +} diff --git a/key.core/src/test/java/de/uka/ilkd/key/util/TestLinkedHashMapWrapper.java b/key.core/src/test/java/de/uka/ilkd/key/util/TestLinkedHashMapWrapper.java index a52478d9a61..411adda347e 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/util/TestLinkedHashMapWrapper.java +++ b/key.core/src/test/java/de/uka/ilkd/key/util/TestLinkedHashMapWrapper.java @@ -128,9 +128,10 @@ public void testTermLabelProperties() { assertEquals(1, irrelevantTermLabelsMap.size()); // add mappings with irrelevant labels to all maps - assertNull(basicMap.put(irrelevantLabelTT, 2), - "Nothing should be returned as basicMap should not contain the key"); - assertEquals(2, basicMap.size()); + assertEquals(1, basicMap.put(irrelevantLabelTT, 2), + "Old value should be returned as term equality (basicMap) now ignores term labels"); + assertEquals(1, basicMap.size(), + "Size should not increase as term equality ignores labels"); assertEquals(1, termLabelsMap.put(irrelevantLabelTT, 2), "Old value should be returned as termLabelsMap should already contain the key"); @@ -148,9 +149,10 @@ public void testTermLabelProperties() { // add mappings with relevant labels to all maps - assertNull(basicMap.put(relevantLabelTT, 3), - "Nothing should be returned as basicMap should not contain the key"); - assertEquals(3, basicMap.size()); + assertEquals(2, basicMap.put(relevantLabelTT, 3), + "Old value should be returned as term equality (basicMap) now ignores term labels"); + assertEquals(1, basicMap.size(), + "Size should not increase as term equality ignores labels"); assertEquals(2, termLabelsMap.put(relevantLabelTT, 3), "Value 3 should be returned as termLabelsMap was previously updated with irrelevantLabelTT");