GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/strings/term_registry.cpp Lines: 279 308 90.6 %
Date: 2021-05-24 Branches: 670 1418 47.2 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Andres Noetzli, Morgan Deters
4
 *
5
 * This file is part of the cvc5 project.
6
 *
7
 * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS
8
 * in the top-level source directory and their institutional affiliations.
9
 * All rights reserved.  See the file COPYING in the top-level source
10
 * directory for licensing information.
11
 * ****************************************************************************
12
 *
13
 * Implementation of term registry for the theory of strings.
14
 */
15
16
#include "theory/strings/term_registry.h"
17
18
#include "expr/attribute.h"
19
#include "options/smt_options.h"
20
#include "options/strings_options.h"
21
#include "smt/logic_exception.h"
22
#include "theory/rewriter.h"
23
#include "theory/strings/inference_manager.h"
24
#include "theory/strings/theory_strings_utils.h"
25
#include "theory/strings/word.h"
26
27
using namespace std;
28
using namespace cvc5::context;
29
using namespace cvc5::kind;
30
31
namespace cvc5 {
32
namespace theory {
33
namespace strings {
34
35
struct StringsProxyVarAttributeId
36
{
37
};
38
typedef expr::Attribute<StringsProxyVarAttributeId, bool>
39
    StringsProxyVarAttribute;
40
41
9459
TermRegistry::TermRegistry(SolverState& s,
42
                           SequencesStatistics& statistics,
43
9459
                           ProofNodeManager* pnm)
44
    : d_state(s),
45
      d_im(nullptr),
46
      d_statistics(statistics),
47
      d_hasStrCode(false),
48
      d_functionsTerms(s.getSatContext()),
49
9459
      d_inputVars(s.getUserContext()),
50
      d_preregisteredTerms(s.getSatContext()),
51
9459
      d_registeredTerms(s.getUserContext()),
52
9459
      d_registeredTypes(s.getUserContext()),
53
9459
      d_proxyVar(s.getUserContext()),
54
9459
      d_lengthLemmaTermsCache(s.getUserContext()),
55
      d_epg(pnm ? new EagerProofGenerator(
56
                      pnm,
57
1192
                      s.getUserContext(),
58
1192
                      "strings::TermRegistry::EagerProofGenerator")
59
59138
                : nullptr)
60
{
61
9459
  NodeManager* nm = NodeManager::currentNM();
62
9459
  d_zero = nm->mkConst(Rational(0));
63
9459
  d_one = nm->mkConst(Rational(1));
64
9459
  d_negOne = NodeManager::currentNM()->mkConst(Rational(-1));
65
9459
  d_cardSize = utils::getAlphabetCardinality();
66
9459
}
67
68
9459
TermRegistry::~TermRegistry() {}
69
70
9459
void TermRegistry::finishInit(InferenceManager* im) { d_im = im; }
71
72
20302
Node TermRegistry::eagerReduce(Node t, SkolemCache* sc)
73
{
74
20302
  NodeManager* nm = NodeManager::currentNM();
75
20302
  Node lemma;
76
20302
  Kind tk = t.getKind();
77
20302
  if (tk == STRING_TO_CODE)
78
  {
79
    // ite( str.len(s)==1, 0 <= str.code(s) < |A|, str.code(s)=-1 )
80
2384
    Node code_len = utils::mkNLength(t[0]).eqNode(nm->mkConst(Rational(1)));
81
2384
    Node code_eq_neg1 = t.eqNode(nm->mkConst(Rational(-1)));
82
    Node code_range = nm->mkNode(
83
        AND,
84
2384
        nm->mkNode(GEQ, t, nm->mkConst(Rational(0))),
85
2384
        nm->mkNode(
86
7152
            LT, t, nm->mkConst(Rational(utils::getAlphabetCardinality()))));
87
1192
    lemma = nm->mkNode(ITE, code_len, code_range, code_eq_neg1);
88
  }
89
19110
  else if (tk == STRING_STRIDOF)
90
  {
91
    // (and (>= (str.indexof x y n) (- 1)) (<= (str.indexof x y n) (str.len
92
    // x)))
93
320
    Node l = nm->mkNode(STRING_LENGTH, t[0]);
94
480
    lemma = nm->mkNode(AND,
95
320
                       nm->mkNode(GEQ, t, nm->mkConst(Rational(-1))),
96
320
                       nm->mkNode(LEQ, t, l));
97
  }
98
18950
  else if (tk == STRING_STOI)
99
  {
100
    // (>= (str.to_int x) (- 1))
101
53
    lemma = nm->mkNode(GEQ, t, nm->mkConst(Rational(-1)));
102
  }
103
18897
  else if (tk == STRING_STRCTN)
104
  {
105
    // ite( (str.contains s r), (= s (str.++ sk1 r sk2)), (not (= s r)))
106
    Node sk1 =
107
3914
        sc->mkSkolemCached(t[0], t[1], SkolemCache::SK_FIRST_CTN_PRE, "sc1");
108
    Node sk2 =
109
3914
        sc->mkSkolemCached(t[0], t[1], SkolemCache::SK_FIRST_CTN_POST, "sc2");
110
1957
    lemma = t[0].eqNode(utils::mkNConcat(sk1, t[1], sk2));
111
1957
    lemma = nm->mkNode(ITE, t, lemma, t[0].eqNode(t[1]).notNode());
112
  }
113
20302
  return lemma;
114
}
115
116
8445
Node TermRegistry::lengthPositive(Node t)
117
{
118
8445
  NodeManager* nm = NodeManager::currentNM();
119
16890
  Node zero = nm->mkConst(Rational(0));
120
16890
  Node emp = Word::mkEmptyWord(t.getType());
121
16890
  Node tlen = nm->mkNode(STRING_LENGTH, t);
122
16890
  Node tlenEqZero = tlen.eqNode(zero);
123
16890
  Node tEqEmp = t.eqNode(emp);
124
16890
  Node caseEmpty = nm->mkNode(AND, tlenEqZero, tEqEmp);
125
16890
  Node caseNEmpty = nm->mkNode(GT, tlen, zero);
126
  // (or (and (= (str.len t) 0) (= t "")) (> (str.len t) 0))
127
16890
  return nm->mkNode(OR, caseEmpty, caseNEmpty);
128
}
129
130
105323
void TermRegistry::preRegisterTerm(TNode n)
131
{
132
105323
  if (d_preregisteredTerms.find(n) != d_preregisteredTerms.end())
133
  {
134
42780
    return;
135
  }
136
104554
  eq::EqualityEngine* ee = d_state.getEqualityEngine();
137
104554
  d_preregisteredTerms.insert(n);
138
209108
  Trace("strings-preregister")
139
104554
      << "TheoryString::preregister : " << n << std::endl;
140
  // check for logic exceptions
141
104554
  Kind k = n.getKind();
142
104554
  if (!options::stringExp())
143
  {
144
2818
    if (k == STRING_STRIDOF || k == STRING_ITOS || k == STRING_STOI
145
2818
        || k == STRING_STRREPL || k == STRING_SUBSTR || k == STRING_STRREPLALL
146
2818
        || k == SEQ_NTH || k == STRING_REPLACE_RE || k == STRING_REPLACE_RE_ALL
147
2818
        || k == STRING_STRCTN || k == STRING_LEQ || k == STRING_TOLOWER
148
2818
        || k == STRING_TOUPPER || k == STRING_REV || k == STRING_UPDATE)
149
    {
150
      std::stringstream ss;
151
      ss << "Term of kind " << k
152
         << " not supported in default mode, try --strings-exp";
153
      throw LogicException(ss.str());
154
    }
155
  }
156
104554
  if (k == EQUAL)
157
  {
158
39176
    if (n[0].getType().isRegExp())
159
    {
160
      std::stringstream ss;
161
      ss << "Equality between regular expressions is not supported";
162
      throw LogicException(ss.str());
163
    }
164
39176
    ee->addTriggerPredicate(n);
165
39176
    return;
166
  }
167
65378
  else if (k == STRING_IN_REGEXP)
168
  {
169
2066
    d_im->requirePhase(n, true);
170
2066
    ee->addTriggerPredicate(n);
171
2066
    ee->addTerm(n[0]);
172
2066
    ee->addTerm(n[1]);
173
2066
    return;
174
  }
175
63312
  else if (k == STRING_TO_CODE)
176
  {
177
1930
    d_hasStrCode = true;
178
  }
179
61382
  else if (k == REGEXP_RANGE)
180
  {
181
180
    for (const Node& nc : n)
182
    {
183
120
      if (!nc.isConst())
184
      {
185
        throw LogicException(
186
            "expecting a constant string term in regexp range");
187
      }
188
120
      if (nc.getConst<String>().size() != 1)
189
      {
190
        throw LogicException(
191
            "expecting a single constant string term in regexp range");
192
      }
193
    }
194
  }
195
63312
  registerTerm(n, 0);
196
126624
  TypeNode tn = n.getType();
197
63312
  if (tn.isRegExp() && n.isVar())
198
  {
199
    std::stringstream ss;
200
    ss << "Regular expression variables are not supported.";
201
    throw LogicException(ss.str());
202
  }
203
63312
  if (tn.isString())  // string-only
204
  {
205
    // all characters of constants should fall in the alphabet
206
31713
    if (n.isConst())
207
    {
208
6434
      std::vector<unsigned> vec = n.getConst<String>().getVec();
209
11359
      for (unsigned u : vec)
210
      {
211
8142
        if (u >= d_cardSize)
212
        {
213
          std::stringstream ss;
214
          ss << "Characters in string \"" << n
215
             << "\" are outside of the given alphabet.";
216
          throw LogicException(ss.str());
217
        }
218
      }
219
    }
220
31713
    ee->addTerm(n);
221
  }
222
31599
  else if (tn.isBoolean())
223
  {
224
    // All kinds that we do congruence over that may return a Boolean go here
225
1740
    if (k==STRING_STRCTN || k == STRING_LEQ || k == SEQ_NTH)
226
    {
227
      // Get triggered for both equal and dis-equal
228
1734
      ee->addTriggerPredicate(n);
229
    }
230
  }
231
  else
232
  {
233
    // Function applications/predicates
234
29859
    ee->addTerm(n);
235
  }
236
  // Set d_functionsTerms stores all function applications that are
237
  // relevant to theory combination. Notice that this is a subset of
238
  // the applications whose kinds are function kinds in the equality
239
  // engine. This means it does not include applications of operators
240
  // like re.++, which is not a function kind in the equality engine.
241
  // Concatenation terms do not need to be considered here because
242
  // their arguments have string type and do not introduce any shared
243
  // terms.
244
63312
  if (n.hasOperator() && ee->isFunctionKind(k) && k != STRING_CONCAT)
245
  {
246
29475
    d_functionsTerms.push_back(n);
247
  }
248
63312
  if (options::stringFMF())
249
  {
250
12382
    if (tn.isStringLike())
251
    {
252
      // Our decision strategy will minimize the length of this term if it is a
253
      // variable but not an internally generated Skolem, or a term that does
254
      // not belong to this theory.
255
9080
      if (n.isVar() ? !d_skCache.isSkolem(n)
256
2610
                    : kindToTheoryId(k) != THEORY_STRINGS)
257
      {
258
332
        d_inputVars.insert(n);
259
332
        Trace("strings-preregister") << "input variable: " << n << std::endl;
260
      }
261
    }
262
  }
263
}
264
265
111640
void TermRegistry::registerTerm(Node n, int effort)
266
{
267
223280
  Trace("strings-register") << "TheoryStrings::registerTerm() " << n
268
111640
                            << ", effort = " << effort << std::endl;
269
111640
  if (d_registeredTerms.find(n) != d_registeredTerms.end())
270
  {
271
71558
    Trace("strings-register") << "...already registered" << std::endl;
272
143116
    return;
273
  }
274
40082
  bool do_register = true;
275
80164
  TypeNode tn = n.getType();
276
40082
  if (!tn.isStringLike())
277
  {
278
19203
    if (options::stringEagerLen())
279
    {
280
19203
      do_register = effort == 0;
281
    }
282
    else
283
    {
284
      do_register = effort > 0 || n.getKind() != STRING_CONCAT;
285
    }
286
  }
287
40082
  if (!do_register)
288
  {
289
    Trace("strings-register") << "...do not register" << std::endl;
290
    return;
291
  }
292
40082
  Trace("strings-register") << "...register" << std::endl;
293
40082
  d_registeredTerms.insert(n);
294
  // ensure the type is registered
295
40082
  registerType(tn);
296
80164
  TrustNode regTermLem;
297
40082
  if (tn.isStringLike())
298
  {
299
    // register length information:
300
    //  for variables, split on empty vs positive length
301
    //  for concat/const/replace, introduce proxy var and state length relation
302
20879
    regTermLem = getRegisterTermLemma(n);
303
  }
304
19203
  else if (n.getKind() != STRING_STRCTN)
305
  {
306
    // we don't send out eager reduction lemma for str.contains currently
307
36454
    Node eagerRedLemma = eagerReduce(n, &d_skCache);
308
18227
    if (!eagerRedLemma.isNull())
309
    {
310
      // if there was an eager reduction, we make the trust node for it
311
1287
      if (d_epg != nullptr)
312
      {
313
236
        regTermLem = d_epg->mkTrustNode(
314
118
            eagerRedLemma, PfRule::STRING_EAGER_REDUCTION, {}, {n});
315
      }
316
      else
317
      {
318
1169
        regTermLem = TrustNode::mkTrustLemma(eagerRedLemma, nullptr);
319
      }
320
    }
321
  }
322
40082
  if (!regTermLem.isNull())
323
  {
324
15870
    Trace("strings-lemma") << "Strings::Lemma REG-TERM : " << regTermLem
325
7935
                           << std::endl;
326
15870
    Trace("strings-assert")
327
7935
        << "(assert " << regTermLem.getNode() << ")" << std::endl;
328
7935
    d_im->trustedLemma(regTermLem, InferenceId::STRINGS_REGISTER_TERM);
329
  }
330
}
331
332
40082
void TermRegistry::registerType(TypeNode tn)
333
{
334
40082
  if (d_registeredTypes.find(tn) != d_registeredTypes.end())
335
  {
336
38168
    return;
337
  }
338
1914
  d_registeredTypes.insert(tn);
339
1914
  if (tn.isStringLike())
340
  {
341
    // preregister the empty word for the type
342
1538
    Node emp = Word::mkEmptyWord(tn);
343
769
    if (!d_state.hasTerm(emp))
344
    {
345
769
      preRegisterTerm(emp);
346
    }
347
  }
348
}
349
350
20879
TrustNode TermRegistry::getRegisterTermLemma(Node n)
351
{
352
20879
  Assert(n.getType().isStringLike());
353
20879
  NodeManager* nm = NodeManager::currentNM();
354
  // register length information:
355
  //  for variables, split on empty vs positive length
356
  //  for concat/const/replace, introduce proxy var and state length relation
357
41758
  Node lsum;
358
20879
  if (n.getKind() != STRING_CONCAT && !n.isConst())
359
  {
360
14499
    Node lsumb = nm->mkNode(STRING_LENGTH, n);
361
14365
    lsum = Rewriter::rewrite(lsumb);
362
    // can register length term if it does not rewrite
363
14365
    if (lsum == lsumb)
364
    {
365
14231
      registerTermAtomic(n, LENGTH_SPLIT);
366
14231
      return TrustNode::null();
367
    }
368
  }
369
13296
  Node sk = d_skCache.mkSkolemCached(n, SkolemCache::SK_PURIFY, "lsym");
370
  StringsProxyVarAttribute spva;
371
6648
  sk.setAttribute(spva, true);
372
13296
  Node eq = Rewriter::rewrite(sk.eqNode(n));
373
6648
  d_proxyVar[n] = sk;
374
  // If we are introducing a proxy for a constant or concat term, we do not
375
  // need to send lemmas about its length, since its length is already
376
  // implied.
377
6648
  if (n.isConst() || n.getKind() == STRING_CONCAT)
378
  {
379
    // do not send length lemma for sk.
380
6514
    registerTermAtomic(sk, LENGTH_IGNORE);
381
  }
382
13296
  Node skl = nm->mkNode(STRING_LENGTH, sk);
383
6648
  if (n.getKind() == STRING_CONCAT)
384
  {
385
7470
    std::vector<Node> nodeVec;
386
12684
    for (const Node& nc : n)
387
    {
388
8949
      if (nc.getAttribute(StringsProxyVarAttribute()))
389
      {
390
        Assert(d_proxyVarToLength.find(nc) != d_proxyVarToLength.end());
391
        nodeVec.push_back(d_proxyVarToLength[nc]);
392
      }
393
      else
394
      {
395
17898
        Node lni = nm->mkNode(STRING_LENGTH, nc);
396
8949
        nodeVec.push_back(lni);
397
      }
398
    }
399
3735
    lsum = nm->mkNode(PLUS, nodeVec);
400
3735
    lsum = Rewriter::rewrite(lsum);
401
  }
402
2913
  else if (n.isConst())
403
  {
404
2779
    lsum = nm->mkConst(Rational(Word::getLength(n)));
405
  }
406
6648
  Assert(!lsum.isNull());
407
6648
  d_proxyVarToLength[sk] = lsum;
408
13296
  Node ceq = Rewriter::rewrite(skl.eqNode(lsum));
409
410
13296
  Node ret = nm->mkNode(AND, eq, ceq);
411
412
  // it is a simple rewrite to justify this
413
6648
  if (d_epg != nullptr)
414
  {
415
659
    return d_epg->mkTrustNode(ret, PfRule::MACRO_SR_PRED_INTRO, {}, {ret});
416
  }
417
5989
  return TrustNode::mkTrustLemma(ret, nullptr);
418
}
419
420
21575
void TermRegistry::registerTermAtomic(Node n, LengthStatus s)
421
{
422
21575
  if (d_lengthLemmaTermsCache.find(n) != d_lengthLemmaTermsCache.end())
423
  {
424
21192
    return;
425
  }
426
14235
  d_lengthLemmaTermsCache.insert(n);
427
428
14235
  if (s == LENGTH_IGNORE)
429
  {
430
    // ignore it
431
6512
    return;
432
  }
433
15446
  std::map<Node, bool> reqPhase;
434
15446
  TrustNode lenLem = getRegisterTermAtomicLemma(n, s, reqPhase);
435
7723
  if (!lenLem.isNull())
436
  {
437
15438
    Trace("strings-lemma") << "Strings::Lemma REGISTER-TERM-ATOMIC : " << lenLem
438
7719
                           << std::endl;
439
15438
    Trace("strings-assert")
440
7719
        << "(assert " << lenLem.getNode() << ")" << std::endl;
441
7719
    d_im->trustedLemma(lenLem, InferenceId::STRINGS_REGISTER_TERM_ATOMIC);
442
  }
443
22973
  for (const std::pair<const Node, bool>& rp : reqPhase)
444
  {
445
15250
    d_im->requirePhase(rp.first, rp.second);
446
  }
447
}
448
449
27656
SkolemCache* TermRegistry::getSkolemCache() { return &d_skCache; }
450
451
6484
const context::CDList<TNode>& TermRegistry::getFunctionTerms() const
452
{
453
6484
  return d_functionsTerms;
454
}
455
456
74
const context::CDHashSet<Node>& TermRegistry::getInputVars() const
457
{
458
74
  return d_inputVars;
459
}
460
461
10105
bool TermRegistry::hasStringCode() const { return d_hasStrCode; }
462
463
7723
TrustNode TermRegistry::getRegisterTermAtomicLemma(
464
    Node n, LengthStatus s, std::map<Node, bool>& reqPhase)
465
{
466
7723
  if (n.isConst())
467
  {
468
    // No need to send length for constant terms. This case may be triggered
469
    // for cases where the skolem cache automatically replaces a skolem by
470
    // a constant.
471
4
    return TrustNode::null();
472
  }
473
7719
  Assert(n.getType().isStringLike());
474
7719
  NodeManager* nm = NodeManager::currentNM();
475
15438
  Node n_len = nm->mkNode(kind::STRING_LENGTH, n);
476
15438
  Node emp = Word::mkEmptyWord(n.getType());
477
7719
  if (s == LENGTH_GEQ_ONE)
478
  {
479
16
    Node neq_empty = n.eqNode(emp).negate();
480
16
    Node len_n_gt_z = nm->mkNode(GT, n_len, d_zero);
481
16
    Node len_geq_one = nm->mkNode(AND, neq_empty, len_n_gt_z);
482
16
    Trace("strings-lemma") << "Strings::Lemma SK-GEQ-ONE : " << len_geq_one
483
8
                           << std::endl;
484
8
    Trace("strings-assert") << "(assert " << len_geq_one << ")" << std::endl;
485
8
    return TrustNode::mkTrustLemma(len_geq_one, nullptr);
486
  }
487
488
7711
  if (s == LENGTH_ONE)
489
  {
490
    Node len_one = n_len.eqNode(d_one);
491
    Trace("strings-lemma") << "Strings::Lemma SK-ONE : " << len_one
492
                           << std::endl;
493
    Trace("strings-assert") << "(assert " << len_one << ")" << std::endl;
494
    return TrustNode::mkTrustLemma(len_one, nullptr);
495
  }
496
7711
  Assert(s == LENGTH_SPLIT);
497
498
  // get the positive length lemma
499
15422
  Node lenLemma = lengthPositive(n);
500
  // split whether the string is empty
501
15422
  Node n_len_eq_z = n_len.eqNode(d_zero);
502
15422
  Node n_len_eq_z_2 = n.eqNode(emp);
503
15422
  Node case_empty = nm->mkNode(AND, n_len_eq_z, n_len_eq_z_2);
504
15422
  Node case_emptyr = Rewriter::rewrite(case_empty);
505
7711
  if (!case_emptyr.isConst())
506
  {
507
    // prefer trying the empty case first
508
    // notice that requirePhase must only be called on rewritten literals that
509
    // occur in the CNF stream.
510
7625
    n_len_eq_z = Rewriter::rewrite(n_len_eq_z);
511
7625
    Assert(!n_len_eq_z.isConst());
512
7625
    reqPhase[n_len_eq_z] = true;
513
7625
    n_len_eq_z_2 = Rewriter::rewrite(n_len_eq_z_2);
514
7625
    Assert(!n_len_eq_z_2.isConst());
515
7625
    reqPhase[n_len_eq_z_2] = true;
516
  }
517
  else
518
  {
519
    // If n = "" ---> true or len( n ) = 0 ----> true, then we expect that
520
    // n ---> "". Since this method is only called on non-constants n, it must
521
    // be that n = "" ^ len( n ) = 0 does not rewrite to true.
522
86
    Assert(!case_emptyr.getConst<bool>());
523
  }
524
525
7711
  if (d_epg != nullptr)
526
  {
527
734
    return d_epg->mkTrustNode(lenLemma, PfRule::STRING_LENGTH_POS, {}, {n});
528
  }
529
6977
  return TrustNode::mkTrustLemma(lenLemma, nullptr);
530
}
531
532
167754
Node TermRegistry::getSymbolicDefinition(Node n, std::vector<Node>& exp) const
533
{
534
167754
  if (n.getNumChildren() == 0)
535
  {
536
167044
    Node pn = getProxyVariableFor(n);
537
83522
    if (pn.isNull())
538
    {
539
10804
      return Node::null();
540
    }
541
145436
    Node eq = n.eqNode(pn);
542
72718
    eq = Rewriter::rewrite(eq);
543
72718
    if (std::find(exp.begin(), exp.end(), eq) == exp.end())
544
    {
545
70623
      exp.push_back(eq);
546
    }
547
72718
    return pn;
548
  }
549
168464
  std::vector<Node> children;
550
84232
  if (n.getMetaKind() == metakind::PARAMETERIZED)
551
  {
552
36
    children.push_back(n.getOperator());
553
  }
554
202562
  for (const Node& nc : n)
555
  {
556
132175
    if (n.getType().isRegExp())
557
    {
558
27475
      children.push_back(nc);
559
    }
560
    else
561
    {
562
195555
      Node ns = getSymbolicDefinition(nc, exp);
563
104700
      if (ns.isNull())
564
      {
565
13845
        return Node::null();
566
      }
567
      else
568
      {
569
90855
        children.push_back(ns);
570
      }
571
    }
572
  }
573
70387
  return NodeManager::currentNM()->mkNode(n.getKind(), children);
574
}
575
576
88040
Node TermRegistry::getProxyVariableFor(Node n) const
577
{
578
88040
  NodeNodeMap::const_iterator it = d_proxyVar.find(n);
579
88040
  if (it != d_proxyVar.end())
580
  {
581
77062
    return (*it).second;
582
  }
583
10978
  return Node::null();
584
}
585
586
4342
Node TermRegistry::ensureProxyVariableFor(Node n)
587
{
588
4342
  Node proxy = getProxyVariableFor(n);
589
4342
  if (proxy.isNull())
590
  {
591
174
    registerTerm(n, 0);
592
174
    proxy = getProxyVariableFor(n);
593
  }
594
4342
  Assert(!proxy.isNull());
595
4342
  return proxy;
596
}
597
598
39393
void TermRegistry::removeProxyEqs(Node n, std::vector<Node>& unproc) const
599
{
600
39393
  if (n.getKind() == AND)
601
  {
602
650
    for (const Node& nc : n)
603
    {
604
484
      removeProxyEqs(nc, unproc);
605
    }
606
332
    return;
607
  }
608
39227
  Trace("strings-subs-proxy") << "Input : " << n << std::endl;
609
78454
  Node ns = Rewriter::rewrite(n);
610
39227
  if (ns.getKind() == EQUAL)
611
  {
612
117507
    for (size_t i = 0; i < 2; i++)
613
    {
614
      // determine whether this side has a proxy variable
615
78338
      if (ns[i].getAttribute(StringsProxyVarAttribute()))
616
      {
617
2
        if (getProxyVariableFor(ns[1 - i]) == ns[i])
618
        {
619
          Trace("strings-subs-proxy")
620
              << "...trivial definition via " << ns[i] << std::endl;
621
          // it is a trivial equality, e.g. between a proxy variable
622
          // and its definition
623
          return;
624
        }
625
      }
626
    }
627
  }
628
39227
  if (!n.isConst() || !n.getConst<bool>())
629
  {
630
39213
    Trace("strings-subs-proxy") << "...unprocessed" << std::endl;
631
39213
    unproc.push_back(n);
632
  }
633
}
634
635
}  // namespace strings
636
}  // namespace theory
637
28191
}  // namespace cvc5