GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/rewriter.cpp Lines: 166 187 88.8 %
Date: 2021-09-17 Branches: 306 736 41.6 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Andres Noetzli, Dejan Jovanovic
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
 * The Rewriter class.
14
 */
15
16
#include "theory/rewriter.h"
17
18
#include "options/theory_options.h"
19
#include "proof/conv_proof_generator.h"
20
#include "smt/smt_engine.h"
21
#include "smt/smt_engine_scope.h"
22
#include "smt/smt_statistics_registry.h"
23
#include "theory/builtin/proof_checker.h"
24
#include "theory/evaluator.h"
25
#include "theory/quantifiers/extended_rewrite.h"
26
#include "theory/rewriter_tables.h"
27
#include "theory/theory.h"
28
#include "util/resource_manager.h"
29
30
using namespace std;
31
32
namespace cvc5 {
33
namespace theory {
34
35
/** Attribute true for nodes that have been rewritten with proofs enabled */
36
struct RewriteWithProofsAttributeId
37
{
38
};
39
typedef expr::Attribute<RewriteWithProofsAttributeId, bool>
40
    RewriteWithProofsAttribute;
41
42
// Note that this function is a simplified version of Theory::theoryOf for
43
// (type-based) theoryOfMode. We expand and simplify it here for the sake of
44
// efficiency.
45
160284525
static TheoryId theoryOf(TNode node) {
46
160284525
  if (node.getKind() == kind::EQUAL)
47
  {
48
    // Equality is owned by the theory that owns the domain
49
24884939
    return Theory::theoryOf(node[0].getType());
50
  }
51
  // Regular nodes are owned by the kind
52
135399586
  return kindToTheoryId(node.getKind());
53
}
54
55
/**
56
 * TheoryEngine::rewrite() keeps a stack of things that are being pre-
57
 * and post-rewritten.  Each element of the stack is a
58
 * RewriteStackElement.
59
 */
60
134086918
struct RewriteStackElement {
61
  /**
62
   * Construct a fresh stack element.
63
   */
64
39907464
  RewriteStackElement(TNode node, TheoryId theoryId)
65
39907464
      : d_node(node),
66
        d_original(node),
67
        d_theoryId(theoryId),
68
        d_originalTheoryId(theoryId),
69
39907464
        d_nextChild(0)
70
  {
71
39907464
  }
72
73
236648049
  TheoryId getTheoryId() { return static_cast<TheoryId>(d_theoryId); }
74
75
23611184
  TheoryId getOriginalTheoryId()
76
  {
77
23611184
    return static_cast<TheoryId>(d_originalTheoryId);
78
  }
79
80
  /** The node we're currently rewriting */
81
  Node d_node;
82
  /** Original node (either the unrewritten node or the node after prerewriting)
83
   */
84
  Node d_original;
85
  /** Id of the theory that's currently rewriting this node */
86
  unsigned d_theoryId : 8;
87
  /** Id of the original theory that started the rewrite */
88
  unsigned d_originalTheoryId : 8;
89
  /** Index of the child this node is done rewriting */
90
  unsigned d_nextChild : 32;
91
  /** Builder for this node */
92
  NodeBuilder d_builder;
93
};
94
95
96
43989356
Node Rewriter::rewrite(TNode node) {
97
43989356
  if (node.getNumChildren() == 0)
98
  {
99
    // Nodes with zero children should never change via rewriting. We return
100
    // eagerly for the sake of efficiency here.
101
9155026
    return node;
102
  }
103
34834330
  return getInstance()->rewriteTo(theoryOf(node), node);
104
}
105
106
63386
Node Rewriter::callExtendedRewrite(TNode node, bool aggr)
107
{
108
63386
  return getInstance()->extendedRewrite(node, aggr);
109
}
110
111
244142
Node Rewriter::extendedRewrite(TNode node, bool aggr)
112
{
113
488284
  quantifiers::ExtendedRewriter er(*this, aggr);
114
488284
  return er.extendedRewrite(node);
115
}
116
117
62642
TrustNode Rewriter::rewriteWithProof(TNode node,
118
                                     bool isExtEq)
119
{
120
  // must set the proof checker before calling this
121
62642
  Assert(d_tpg != nullptr);
122
62642
  if (isExtEq)
123
  {
124
    // theory rewriter is responsible for rewriting the equality
125
37
    TheoryRewriter* tr = getInstance()->d_theoryRewriters[theoryOf(node)];
126
37
    Assert(tr != nullptr);
127
37
    return tr->rewriteEqualityExtWithProof(node);
128
  }
129
125210
  Node ret = getInstance()->rewriteTo(theoryOf(node), node, d_tpg.get());
130
62605
  return TrustNode::mkTrustRewrite(node, ret, d_tpg.get());
131
}
132
133
3796
void Rewriter::setProofNodeManager(ProofNodeManager* pnm)
134
{
135
  // if not already initialized with proof support
136
3796
  if (d_tpg == nullptr)
137
  {
138
3796
    Trace("rewriter") << "Rewriter::setProofNodeManager" << std::endl;
139
    // the rewriter is staticly determinstic, thus use static cache policy
140
    // for the term conversion proof generator
141
7592
    d_tpg.reset(new TConvProofGenerator(pnm,
142
                                        nullptr,
143
                                        TConvPolicy::FIXPOINT,
144
                                        TConvCachePolicy::STATIC,
145
3796
                                        "Rewriter::TConvProofGenerator"));
146
  }
147
3796
}
148
149
140
Node Rewriter::rewriteEqualityExt(TNode node)
150
{
151
140
  Assert(node.getKind() == kind::EQUAL);
152
  // note we don't force caching of this method currently
153
140
  return d_theoryRewriters[theoryOf(node)]->rewriteEqualityExt(node);
154
}
155
156
129282
void Rewriter::registerTheoryRewriter(theory::TheoryId tid,
157
                                      TheoryRewriter* trew)
158
{
159
129282
  d_theoryRewriters[tid] = trew;
160
129282
}
161
162
14336
TheoryRewriter* Rewriter::getTheoryRewriter(theory::TheoryId theoryId)
163
{
164
14336
  return d_theoryRewriters[theoryId];
165
}
166
167
34960358
Rewriter* Rewriter::getInstance()
168
{
169
34960358
  return smt::currentSmtEngine()->getRewriter();
170
}
171
172
36144218
Node Rewriter::rewriteTo(theory::TheoryId theoryId,
173
                         Node node,
174
                         TConvProofGenerator* tcpg)
175
{
176
  RewriteWithProofsAttribute rpfa;
177
#ifdef CVC5_ASSERTIONS
178
36144218
  bool isEquality = node.getKind() == kind::EQUAL && (!node[0].getType().isBoolean());
179
180
36144218
  if (d_rewriteStack == nullptr)
181
  {
182
9541
    d_rewriteStack.reset(new std::unordered_set<Node>());
183
  }
184
#endif
185
186
36144218
  Trace("rewriter") << "Rewriter::rewriteTo(" << theoryId << "," << node << ")"<< std::endl;
187
188
  // Check if it's been cached already
189
72288436
  Node cached = getPostRewriteCache(theoryId, node);
190
36144218
  if (!cached.isNull() && (tcpg == nullptr || node.getAttribute(rpfa)))
191
  {
192
29565164
    return cached;
193
  }
194
195
  // Put the node on the stack in order to start the "recursive" rewrite
196
13158108
  vector<RewriteStackElement> rewriteStack;
197
6579054
  rewriteStack.push_back(RewriteStackElement(node, theoryId));
198
199
6579054
  ResourceManager* rm = NULL;
200
6579054
  bool hasSmtEngine = smt::smtEngineInScope();
201
6579054
  if (hasSmtEngine) {
202
6579054
    rm = smt::currentResourceManager();
203
  }
204
  // Rewrite until the stack is empty
205
  for (;;){
206
73235872
    if (hasSmtEngine)
207
    {
208
73235872
      rm->spendResource(Resource::RewriteStep);
209
    }
210
211
    // Get the top of the recursion stack
212
73235872
    RewriteStackElement& rewriteStackTop = rewriteStack.back();
213
214
146471744
    Trace("rewriter") << "Rewriter::rewriting: "
215
146471744
                      << rewriteStackTop.getTheoryId() << ","
216
73235872
                      << rewriteStackTop.d_node << std::endl;
217
218
    // Before rewriting children we need to do a pre-rewrite of the node
219
73235872
    if (rewriteStackTop.d_nextChild == 0)
220
    {
221
      // Check if the pre-rewrite has already been done (it's in the cache)
222
39907464
      cached = getPreRewriteCache(rewriteStackTop.getTheoryId(),
223
                                  rewriteStackTop.d_node);
224
79814928
      if (cached.isNull()
225
39907464
          || (tcpg != nullptr && !rewriteStackTop.d_node.getAttribute(rpfa)))
226
      {
227
        // Rewrite until fix-point is reached
228
        for(;;) {
229
          // Perform the pre-rewrite
230
          RewriteResponse response = preRewrite(
231
14433873
              rewriteStackTop.getTheoryId(), rewriteStackTop.d_node, tcpg);
232
233
          // Put the rewritten node to the top of the stack
234
13284382
          rewriteStackTop.d_node = response.d_node;
235
13284382
          TheoryId newTheory = theoryOf(rewriteStackTop.d_node);
236
          // In the pre-rewrite, if changing theories, we just call the other theories pre-rewrite
237
26568764
          if (newTheory == rewriteStackTop.getTheoryId()
238
13284382
              && response.d_status == REWRITE_DONE)
239
          {
240
12134891
            break;
241
          }
242
1149491
          rewriteStackTop.d_theoryId = newTheory;
243
1149491
        }
244
245
        // Cache the rewrite
246
12134891
        setPreRewriteCache(rewriteStackTop.getOriginalTheoryId(),
247
                           rewriteStackTop.d_original,
248
                           rewriteStackTop.d_node);
249
      }
250
      // Otherwise we're have already been pre-rewritten (in pre-rewrite cache)
251
      else {
252
        // Continue with the cached version
253
27772573
        rewriteStackTop.d_node = cached;
254
27772573
        rewriteStackTop.d_theoryId = theoryOf(cached);
255
      }
256
    }
257
258
73235872
    rewriteStackTop.d_original = rewriteStackTop.d_node;
259
    // Now it's time to rewrite the children, check if this has already been done
260
73235872
    cached = getPostRewriteCache(rewriteStackTop.getTheoryId(),
261
                                 rewriteStackTop.d_node);
262
    // If not, go through the children
263
146471744
    if (cached.isNull()
264
73235872
        || (tcpg != nullptr && !rewriteStackTop.d_node.getAttribute(rpfa)))
265
    {
266
      // The child we need to rewrite
267
44804705
      unsigned child = rewriteStackTop.d_nextChild++;
268
269
      // To build the rewritten expression we set up the builder
270
44804705
      if(child == 0) {
271
11476297
        if (rewriteStackTop.d_node.getNumChildren() > 0)
272
        {
273
          // The children will add themselves to the builder once they're done
274
10845427
          rewriteStackTop.d_builder << rewriteStackTop.d_node.getKind();
275
10845427
          kind::MetaKind metaKind = rewriteStackTop.d_node.getMetaKind();
276
10845427
          if (metaKind == kind::metakind::PARAMETERIZED) {
277
691418
            rewriteStackTop.d_builder << rewriteStackTop.d_node.getOperator();
278
          }
279
        }
280
      }
281
282
      // Process the next child
283
44804705
      if (child < rewriteStackTop.d_node.getNumChildren())
284
      {
285
        // The child node
286
66656820
        Node childNode = rewriteStackTop.d_node[child];
287
        // Push the rewrite request to the stack (NOTE: rewriteStackTop might be a bad reference now)
288
33328410
        rewriteStack.push_back(RewriteStackElement(childNode, theoryOf(childNode)));
289
        // Go on with the rewriting
290
33328410
        continue;
291
      }
292
293
      // Incorporate the children if necessary
294
11476295
      if (rewriteStackTop.d_node.getNumChildren() > 0)
295
      {
296
21690850
        Node rewritten = rewriteStackTop.d_builder;
297
10845425
        rewriteStackTop.d_node = rewritten;
298
10845425
        rewriteStackTop.d_theoryId = theoryOf(rewriteStackTop.d_node);
299
      }
300
301
      // Done with all pre-rewriting, so let's do the post rewrite
302
      for(;;) {
303
        // Do the post-rewrite
304
        RewriteResponse response = postRewrite(
305
11974623
            rewriteStackTop.getTheoryId(), rewriteStackTop.d_node, tcpg);
306
307
        // We continue with the response we got
308
11725456
        TheoryId newTheoryId = theoryOf(response.d_node);
309
23450912
        if (newTheoryId != rewriteStackTop.getTheoryId()
310
11725456
            || response.d_status == REWRITE_AGAIN_FULL)
311
        {
312
          // In the post rewrite if we've changed theories, we must do a full rewrite
313
1247283
          Assert(response.d_node != rewriteStackTop.d_node);
314
          //TODO: this is not thread-safe - should make this assertion dependent on sequential build
315
#ifdef CVC5_ASSERTIONS
316
1247283
          Assert(d_rewriteStack->find(response.d_node)
317
                 == d_rewriteStack->end());
318
1247283
          d_rewriteStack->insert(response.d_node);
319
#endif
320
2494566
          Node rewritten = rewriteTo(newTheoryId, response.d_node, tcpg);
321
1247283
          rewriteStackTop.d_node = rewritten;
322
#ifdef CVC5_ASSERTIONS
323
1247283
          d_rewriteStack->erase(response.d_node);
324
#endif
325
1247283
          break;
326
        }
327
10478173
        else if (response.d_status == REWRITE_DONE)
328
        {
329
#ifdef CVC5_ASSERTIONS
330
          RewriteResponse r2 =
331
20458020
              d_theoryRewriters[newTheoryId]->postRewrite(response.d_node);
332
20458020
          Assert(r2.d_node == response.d_node)
333
10229010
              << r2.d_node << " != " << response.d_node;
334
#endif
335
10229010
          rewriteStackTop.d_node = response.d_node;
336
10229010
          break;
337
        }
338
        // Check for trivial rewrite loops of size 1 or 2
339
249163
        Assert(response.d_node != rewriteStackTop.d_node);
340
249163
        Assert(d_theoryRewriters[rewriteStackTop.getTheoryId()]
341
                   ->postRewrite(response.d_node)
342
                   .d_node
343
               != rewriteStackTop.d_node);
344
249163
        rewriteStackTop.d_node = response.d_node;
345
249163
      }
346
347
      // We're done with the post rewrite, so we add to the cache
348
11476293
      if (tcpg != nullptr)
349
      {
350
        // if proofs are enabled, mark that we've rewritten with proofs
351
379029
        rewriteStackTop.d_original.setAttribute(rpfa, true);
352
379029
        if (!cached.isNull())
353
        {
354
          // We may have gotten a different node, due to non-determinism in
355
          // theory rewriters (e.g. quantifiers rewriter which introduces
356
          // fresh BOUND_VARIABLE). This can happen if we wrote once without
357
          // proofs and then rewrote again with proofs.
358
364994
          if (rewriteStackTop.d_node != cached)
359
          {
360
            Trace("rewriter-proof") << "WARNING: Rewritten forms with and "
361
                                       "without proofs were not equivalent"
362
                                    << std::endl;
363
            Trace("rewriter-proof")
364
                << "   original: " << rewriteStackTop.d_original << std::endl;
365
            Trace("rewriter-proof")
366
                << "with proofs: " << rewriteStackTop.d_node << std::endl;
367
            Trace("rewriter-proof") << " w/o proofs: " << cached << std::endl;
368
            Node eq = rewriteStackTop.d_node.eqNode(cached);
369
            // we make this a post-rewrite, since we are processing a node that
370
            // has finished post-rewriting above
371
            tcpg->addRewriteStep(rewriteStackTop.d_node,
372
                                 cached,
373
                                 PfRule::TRUST_REWRITE,
374
                                 {},
375
                                 {eq},
376
                                 false);
377
            // don't overwrite the cache, should be the same
378
            rewriteStackTop.d_node = cached;
379
          }
380
        }
381
      }
382
11476293
      setPostRewriteCache(rewriteStackTop.getOriginalTheoryId(),
383
                          rewriteStackTop.d_original,
384
                          rewriteStackTop.d_node);
385
    }
386
    else
387
    {
388
      // We were already in cache, so just remember it
389
28431167
      rewriteStackTop.d_node = cached;
390
28431167
      rewriteStackTop.d_theoryId = theoryOf(cached);
391
    }
392
393
    // If this is the last node, just return
394
39907460
    if (rewriteStack.size() == 1) {
395
6579052
      Assert(!isEquality || rewriteStackTop.d_node.getKind() == kind::EQUAL
396
             || rewriteStackTop.d_node.isConst());
397
6579052
      return rewriteStackTop.d_node;
398
    }
399
400
    // We're done with this node, append it to the parent
401
33328408
    rewriteStack[rewriteStack.size() - 2].d_builder << rewriteStackTop.d_node;
402
33328408
    rewriteStack.pop_back();
403
66656818
  }
404
405
  Unreachable();
406
} /* Rewriter::rewriteTo() */
407
408
13284382
RewriteResponse Rewriter::preRewrite(theory::TheoryId theoryId,
409
                                     TNode n,
410
                                     TConvProofGenerator* tcpg)
411
{
412
13284382
  if (tcpg != nullptr)
413
  {
414
    // call the trust rewrite response interface
415
    TrustRewriteResponse tresponse =
416
1094040
        d_theoryRewriters[theoryId]->preRewriteWithProof(n);
417
    // process the trust rewrite response: store the proof step into
418
    // tcpg if necessary and then convert to rewrite response.
419
547020
    return processTrustRewriteResponse(theoryId, tresponse, true, tcpg);
420
  }
421
12737362
  return d_theoryRewriters[theoryId]->preRewrite(n);
422
}
423
424
11725458
RewriteResponse Rewriter::postRewrite(theory::TheoryId theoryId,
425
                                      TNode n,
426
                                      TConvProofGenerator* tcpg)
427
{
428
11725458
  if (tcpg != nullptr)
429
  {
430
    // same as above, for post-rewrite
431
    TrustRewriteResponse tresponse =
432
773280
        d_theoryRewriters[theoryId]->postRewriteWithProof(n);
433
386640
    return processTrustRewriteResponse(theoryId, tresponse, false, tcpg);
434
  }
435
11338820
  return d_theoryRewriters[theoryId]->postRewrite(n);
436
}
437
438
933660
RewriteResponse Rewriter::processTrustRewriteResponse(
439
    theory::TheoryId theoryId,
440
    const TrustRewriteResponse& tresponse,
441
    bool isPre,
442
    TConvProofGenerator* tcpg)
443
{
444
933660
  Assert(tcpg != nullptr);
445
1867320
  TrustNode trn = tresponse.d_node;
446
933660
  Assert(trn.getKind() == TrustNodeKind::REWRITE);
447
1867320
  Node proven = trn.getProven();
448
933660
  if (proven[0] != proven[1])
449
  {
450
239895
    ProofGenerator* pg = trn.getGenerator();
451
239895
    if (pg == nullptr)
452
    {
453
479790
      Node tidn = builtin::BuiltinProofRuleChecker::mkTheoryIdNode(theoryId);
454
      // add small step trusted rewrite
455
      Node rid = mkMethodId(isPre ? MethodId::RW_REWRITE_THEORY_PRE
456
479790
                                  : MethodId::RW_REWRITE_THEORY_POST);
457
959580
      tcpg->addRewriteStep(proven[0],
458
                           proven[1],
459
                           PfRule::THEORY_REWRITE,
460
                           {},
461
                           {proven, tidn, rid},
462
719685
                           isPre);
463
    }
464
    else
465
    {
466
      // store proven rewrite step
467
      tcpg->addRewriteStep(proven[0], proven[1], pg, isPre);
468
    }
469
  }
470
1867320
  return RewriteResponse(tresponse.d_status, trn.getNode());
471
}
472
473
void Rewriter::clearCaches()
474
{
475
#ifdef CVC5_ASSERTIONS
476
  d_rewriteStack.reset(nullptr);
477
#endif
478
479
  clearCachesInternal();
480
}
481
482
1674864
Node Rewriter::rewriteViaMethod(TNode n, MethodId idr)
483
{
484
1674864
  if (idr == MethodId::RW_REWRITE)
485
  {
486
1646365
    return rewrite(n);
487
  }
488
28499
  if (idr == MethodId::RW_EXT_REWRITE)
489
  {
490
14
    return extendedRewrite(n);
491
  }
492
28485
  if (idr == MethodId::RW_REWRITE_EQ_EXT)
493
  {
494
140
    return rewriteEqualityExt(n);
495
  }
496
28345
  if (idr == MethodId::RW_EVALUATE)
497
  {
498
    Evaluator eval;
499
28345
    return eval.eval(n, {}, {}, false);
500
  }
501
  if (idr == MethodId::RW_IDENTITY)
502
  {
503
    // does nothing
504
    return n;
505
  }
506
  // unknown rewriter
507
  Unhandled() << "Rewriter::rewriteViaMethod: no rewriter for " << idr
508
              << std::endl;
509
  return n;
510
}
511
512
}  // namespace theory
513
29577
}  // namespace cvc5