|
15 | 15 | import org.key_project.logic.op.Function; |
16 | 16 | import org.key_project.logic.op.QuantifiableVariable; |
17 | 17 | import org.key_project.logic.sort.Sort; |
| 18 | +import org.key_project.util.collection.ImmutableList; |
18 | 19 | import org.key_project.util.collection.ImmutableMap; |
19 | 20 | import org.key_project.util.collection.ImmutableSet; |
20 | 21 |
|
@@ -94,10 +95,79 @@ private Term applySubst(QuantifiableVariable var, Term instance, Term t, TermBui |
94 | 95 |
|
95 | 96 | /** |
96 | 97 | * Try to apply the substitution to a term, introducing casts if necessary (may never be the |
97 | | - * case any more, XXX) |
| 98 | + * case any more, XXX). |
| 99 | + * <p> |
| 100 | + * Fast path: all variables are substituted in a single traversal ({@link #applyGroundOnePass}). |
| 101 | + * This is sound because the substitution is ground (the instances contain no free variables, so |
| 102 | + * no capture can happen); shadowing of a substituted variable by a binder inside {@code t} is |
| 103 | + * respected. If a cast is required (the slow, possibly dead path), it falls back to the |
| 104 | + * per-variable substitution {@link #applyWithoutCastsPerVar}. |
98 | 105 | */ |
99 | 106 | public Term applyWithoutCasts(Term t, Services services) { |
100 | 107 | assert isGround() : "non-ground substitutions are not yet implemented: " + this; |
| 108 | + final TermBuilder tb = services.getTermBuilder(); |
| 109 | + try { |
| 110 | + return applyGroundOnePass((JTerm) t, ImmutableList.nil(), tb); |
| 111 | + } catch (TermCreationException e) { |
| 112 | + return applyWithoutCastsPerVar(t, services); |
| 113 | + } |
| 114 | + } |
| 115 | + |
| 116 | + /** |
| 117 | + * Substitute every variable of {@link #varMap} in a single traversal of {@code t}. A variable |
| 118 | + * occurrence is replaced unless it is shadowed, i.e. re-bound by a binder above it (tracked in |
| 119 | + * {@code boundAbove}). Subterms in which no substituted variable occurs free are returned |
| 120 | + * unchanged. |
| 121 | + */ |
| 122 | + private JTerm applyGroundOnePass(JTerm t, ImmutableList<QuantifiableVariable> boundAbove, |
| 123 | + TermBuilder tb) { |
| 124 | + // Variable leaves (the most common node) are handled directly, without the freeVars |
| 125 | + // bookkeeping of containsSubstVar. |
| 126 | + if (t.op() instanceof QuantifiableVariable qv) { |
| 127 | + final Term inst = varMap.get(qv); |
| 128 | + return inst != null && !boundAbove.contains(qv) ? (JTerm) inst : t; |
| 129 | + } |
| 130 | + if (!containsSubstVar(t)) { |
| 131 | + return t; |
| 132 | + } |
| 133 | + final int arity = t.arity(); |
| 134 | + final JTerm[] newSubs = new JTerm[arity]; |
| 135 | + boolean changed = false; |
| 136 | + for (int i = 0; i < arity; i++) { |
| 137 | + final JTerm sub = t.sub(i); |
| 138 | + ImmutableList<QuantifiableVariable> below = boundAbove; |
| 139 | + final var boundHere = t.varsBoundHere(i); |
| 140 | + for (int j = 0; j < boundHere.size(); j++) { |
| 141 | + below = below.prepend(boundHere.get(j)); |
| 142 | + } |
| 143 | + newSubs[i] = applyGroundOnePass(sub, below, tb); |
| 144 | + if (newSubs[i] != sub) { |
| 145 | + changed = true; |
| 146 | + } |
| 147 | + } |
| 148 | + if (!changed) { |
| 149 | + return t; |
| 150 | + } |
| 151 | + return tb.tf().createTerm(t.op(), newSubs, t.boundVars(), t.getLabels()); |
| 152 | + } |
| 153 | + |
| 154 | + /** Whether any variable of {@link #varMap} occurs free in {@code t}. */ |
| 155 | + private boolean containsSubstVar(JTerm t) { |
| 156 | + final ImmutableSet<QuantifiableVariable> free = t.freeVars(); |
| 157 | + if (free.isEmpty()) { |
| 158 | + return false; |
| 159 | + } |
| 160 | + final Iterator<QuantifiableVariable> it = varMap.keyIterator(); |
| 161 | + while (it.hasNext()) { |
| 162 | + if (free.contains(it.next())) { |
| 163 | + return true; |
| 164 | + } |
| 165 | + } |
| 166 | + return false; |
| 167 | + } |
| 168 | + |
| 169 | + /** Per-variable substitution (one {@link ClashFreeSubst} pass per variable), with casts. */ |
| 170 | + private Term applyWithoutCastsPerVar(Term t, Services services) { |
101 | 171 | final TermBuilder tb = services.getTermBuilder(); |
102 | 172 | final Iterator<QuantifiableVariable> it = varMap.keyIterator(); |
103 | 173 | while (it.hasNext()) { |
|
0 commit comments