GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/rewriter.cpp Lines: 171 195 87.7 %
Date: 2021-08-01 Branches: 331 766 43.2 %

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