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

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
155115789
static TheoryId theoryOf(TNode node) {
44
155115789
  if (node.getKind() == kind::EQUAL)
45
  {
46
    // Equality is owned by the theory that owns the domain
47
22833538
    return Theory::theoryOf(node[0].getType());
48
  }
49
  // Regular nodes are owned by the kind
50
132282251
  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
130866739
struct RewriteStackElement {
59
  /**
60
   * Construct a fresh stack element.
61
   */
62
39119077
  RewriteStackElement(TNode node, TheoryId theoryId)
63
39119077
      : d_node(node),
64
        d_original(node),
65
        d_theoryId(theoryId),
66
        d_originalTheoryId(theoryId),
67
39119077
        d_nextChild(0)
68
  {
69
39119077
  }
70
71
232811800
  TheoryId getTheoryId() { return static_cast<TheoryId>(d_theoryId); }
72
73
23062136
  TheoryId getOriginalTheoryId()
74
  {
75
23062136
    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
40744526
Node Rewriter::rewrite(TNode node) {
99
40744526
  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
9004172
    return node;
104
  }
105
31740354
  return getInstance()->rewriteTo(theoryOf(node), node);
106
}
107
108
60438
TrustNode Rewriter::rewriteWithProof(TNode node,
109
                                     bool isExtEq)
110
{
111
  // must set the proof checker before calling this
112
60438
  Assert(d_tpg != nullptr);
113
60438
  if (isExtEq)
114
  {
115
    // theory rewriter is responsible for rewriting the equality
116
29
    TheoryRewriter* tr = getInstance()->d_theoryRewriters[theoryOf(node)];
117
29
    Assert(tr != nullptr);
118
29
    return tr->rewriteEqualityExtWithProof(node);
119
  }
120
120818
  Node ret = getInstance()->rewriteTo(theoryOf(node), node, d_tpg.get());
121
60409
  return TrustNode::mkTrustRewrite(node, ret, d_tpg.get());
122
}
123
124
3766
void Rewriter::setProofNodeManager(ProofNodeManager* pnm)
125
{
126
  // if not already initialized with proof support
127
3766
  if (d_tpg == nullptr)
128
  {
129
3766
    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
7532
    d_tpg.reset(new TConvProofGenerator(pnm,
133
                                        nullptr,
134
                                        TConvPolicy::FIXPOINT,
135
                                        TConvCachePolicy::STATIC,
136
3766
                                        "Rewriter::TConvProofGenerator"));
137
  }
138
3766
}
139
140
294
Node Rewriter::rewriteEqualityExt(TNode node)
141
{
142
294
  Assert(node.getKind() == kind::EQUAL);
143
  // note we don't force caching of this method currently
144
588
  return getInstance()->d_theoryRewriters[theoryOf(node)]->rewriteEqualityExt(
145
588
      node);
146
}
147
148
128086
void Rewriter::registerTheoryRewriter(theory::TheoryId tid,
149
                                      TheoryRewriter* trew)
150
{
151
128086
  getInstance()->d_theoryRewriters[tid] = trew;
152
128086
}
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
13302
TheoryRewriter* Rewriter::getTheoryRewriter(theory::TheoryId theoryId)
183
{
184
13302
  return d_theoryRewriters[theoryId];
185
}
186
187
31929172
Rewriter* Rewriter::getInstance()
188
{
189
31929172
  return smt::currentSmtEngine()->getRewriter();
190
}
191
192
33000723
Node Rewriter::rewriteTo(theory::TheoryId theoryId,
193
                         Node node,
194
                         TConvProofGenerator* tcpg)
195
{
196
  RewriteWithProofsAttribute rpfa;
197
#ifdef CVC5_ASSERTIONS
198
33000723
  bool isEquality = node.getKind() == kind::EQUAL && (!node[0].getType().isBoolean());
199
200
33000723
  if (d_rewriteStack == nullptr)
201
  {
202
9484
    d_rewriteStack.reset(new std::unordered_set<Node>());
203
  }
204
#endif
205
206
33000723
  Trace("rewriter") << "Rewriter::rewriteTo(" << theoryId << "," << node << ")"<< std::endl;
207
208
  // Check if it's been cached already
209
66001446
  Node cached = getPostRewriteCache(theoryId, node);
210
33000723
  if (!cached.isNull() && (tcpg == nullptr || node.getAttribute(rpfa)))
211
  {
212
26955062
    return cached;
213
  }
214
215
  // Put the node on the stack in order to start the "recursive" rewrite
216
12091322
  vector<RewriteStackElement> rewriteStack;
217
6045661
  rewriteStack.push_back(RewriteStackElement(node, theoryId));
218
219
6045661
  ResourceManager* rm = NULL;
220
6045661
  bool hasSmtEngine = smt::smtEngineInScope();
221
6045661
  if (hasSmtEngine) {
222
6045661
    rm = smt::currentResourceManager();
223
  }
224
  // Rewrite until the stack is empty
225
  for (;;){
226
72192491
    if (hasSmtEngine)
227
    {
228
72192491
      rm->spendResource(Resource::RewriteStep);
229
    }
230
231
    // Get the top of the recursion stack
232
72192491
    RewriteStackElement& rewriteStackTop = rewriteStack.back();
233
234
144384982
    Trace("rewriter") << "Rewriter::rewriting: "
235
144384982
                      << rewriteStackTop.getTheoryId() << ","
236
72192491
                      << rewriteStackTop.d_node << std::endl;
237
238
    // Before rewriting children we need to do a pre-rewrite of the node
239
72192491
    if (rewriteStackTop.d_nextChild == 0)
240
    {
241
      // Check if the pre-rewrite has already been done (it's in the cache)
242
39119077
      cached = getPreRewriteCache(rewriteStackTop.getTheoryId(),
243
                                  rewriteStackTop.d_node);
244
78238154
      if (cached.isNull()
245
39119077
          || (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
14319734
              rewriteStackTop.getTheoryId(), rewriteStackTop.d_node, tcpg);
252
253
          // Put the rewritten node to the top of the stack
254
13116082
          rewriteStackTop.d_node = response.d_node;
255
13116082
          TheoryId newTheory = theoryOf(rewriteStackTop.d_node);
256
          // In the pre-rewrite, if changing theories, we just call the other theories pre-rewrite
257
26232164
          if (newTheory == rewriteStackTop.getTheoryId()
258
13116082
              && response.d_status == REWRITE_DONE)
259
          {
260
11912430
            break;
261
          }
262
1203652
          rewriteStackTop.d_theoryId = newTheory;
263
1203652
        }
264
265
        // Cache the rewrite
266
11912430
        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
27206647
        rewriteStackTop.d_node = cached;
274
27206647
        rewriteStackTop.d_theoryId = theoryOf(cached);
275
      }
276
    }
277
278
72192491
    rewriteStackTop.d_original = rewriteStackTop.d_node;
279
    // Now it's time to rewrite the children, check if this has already been done
280
72192491
    cached = getPostRewriteCache(rewriteStackTop.getTheoryId(),
281
                                 rewriteStackTop.d_node);
282
    // If not, go through the children
283
144384982
    if (cached.isNull()
284
72192491
        || (tcpg != nullptr && !rewriteStackTop.d_node.getAttribute(rpfa)))
285
    {
286
      // The child we need to rewrite
287
44223124
      unsigned child = rewriteStackTop.d_nextChild++;
288
289
      // To build the rewritten expression we set up the builder
290
44223124
      if(child == 0) {
291
11149710
        if (rewriteStackTop.d_node.getNumChildren() > 0)
292
        {
293
          // The children will add themselves to the builder once they're done
294
10540766
          rewriteStackTop.d_builder << rewriteStackTop.d_node.getKind();
295
10540766
          kind::MetaKind metaKind = rewriteStackTop.d_node.getMetaKind();
296
10540766
          if (metaKind == kind::metakind::PARAMETERIZED) {
297
721255
            rewriteStackTop.d_builder << rewriteStackTop.d_node.getOperator();
298
          }
299
        }
300
      }
301
302
      // Process the next child
303
44223124
      if (child < rewriteStackTop.d_node.getNumChildren())
304
      {
305
        // The child node
306
66146832
        Node childNode = rewriteStackTop.d_node[child];
307
        // Push the rewrite request to the stack (NOTE: rewriteStackTop might be a bad reference now)
308
33073416
        rewriteStack.push_back(RewriteStackElement(childNode, theoryOf(childNode)));
309
        // Go on with the rewriting
310
33073416
        continue;
311
      }
312
313
      // Incorporate the children if necessary
314
11149708
      if (rewriteStackTop.d_node.getNumChildren() > 0)
315
      {
316
21081528
        Node rewritten = rewriteStackTop.d_builder;
317
10540764
        rewriteStackTop.d_node = rewritten;
318
10540764
        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
11667152
            rewriteStackTop.getTheoryId(), rewriteStackTop.d_node, tcpg);
326
327
        // We continue with the response we got
328
11408427
        TheoryId newTheoryId = theoryOf(response.d_node);
329
22816854
        if (newTheoryId != rewriteStackTop.getTheoryId()
330
11408427
            || response.d_status == REWRITE_AGAIN_FULL)
331
        {
332
          // In the post rewrite if we've changed theories, we must do a full rewrite
333
1199960
          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
1199960
          Assert(d_rewriteStack->find(response.d_node)
337
                 == d_rewriteStack->end());
338
1199960
          d_rewriteStack->insert(response.d_node);
339
#endif
340
2399920
          Node rewritten = rewriteTo(newTheoryId, response.d_node, tcpg);
341
1199960
          rewriteStackTop.d_node = rewritten;
342
#ifdef CVC5_ASSERTIONS
343
1199960
          d_rewriteStack->erase(response.d_node);
344
#endif
345
1199960
          break;
346
        }
347
10208467
        else if (response.d_status == REWRITE_DONE)
348
        {
349
#ifdef CVC5_ASSERTIONS
350
          RewriteResponse r2 =
351
19899492
              d_theoryRewriters[newTheoryId]->postRewrite(response.d_node);
352
19899492
          Assert(r2.d_node == response.d_node)
353
9949746
              << r2.d_node << " != " << response.d_node;
354
#endif
355
9949746
          rewriteStackTop.d_node = response.d_node;
356
9949746
          break;
357
        }
358
        // Check for trivial rewrite loops of size 1 or 2
359
258721
        Assert(response.d_node != rewriteStackTop.d_node);
360
258721
        Assert(d_theoryRewriters[rewriteStackTop.getTheoryId()]
361
                   ->postRewrite(response.d_node)
362
                   .d_node
363
               != rewriteStackTop.d_node);
364
258721
        rewriteStackTop.d_node = response.d_node;
365
258721
      }
366
367
      // We're done with the post rewrite, so we add to the cache
368
11149706
      if (tcpg != nullptr)
369
      {
370
        // if proofs are enabled, mark that we've rewritten with proofs
371
344231
        rewriteStackTop.d_original.setAttribute(rpfa, true);
372
344231
        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
330306
          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
11149706
      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
27969367
      rewriteStackTop.d_node = cached;
410
27969367
      rewriteStackTop.d_theoryId = theoryOf(cached);
411
    }
412
413
    // If this is the last node, just return
414
39119073
    if (rewriteStack.size() == 1) {
415
6045659
      Assert(!isEquality || rewriteStackTop.d_node.getKind() == kind::EQUAL
416
             || rewriteStackTop.d_node.isConst());
417
6045659
      return rewriteStackTop.d_node;
418
    }
419
420
    // We're done with this node, append it to the parent
421
33073414
    rewriteStack[rewriteStack.size() - 2].d_builder << rewriteStackTop.d_node;
422
33073414
    rewriteStack.pop_back();
423
66146830
  }
424
425
  Unreachable();
426
} /* Rewriter::rewriteTo() */
427
428
13116082
RewriteResponse Rewriter::preRewrite(theory::TheoryId theoryId,
429
                                     TNode n,
430
                                     TConvProofGenerator* tcpg)
431
{
432
13116082
  Kind k = n.getKind();
433
  std::function<RewriteResponse(RewriteEnvironment*, TNode)> fn =
434
26232164
      (k == kind::EQUAL) ? d_preRewritersEqual[theoryId] : d_preRewriters[k];
435
13116082
  if (fn == nullptr)
436
  {
437
13116082
    if (tcpg != nullptr)
438
    {
439
      // call the trust rewrite response interface
440
      TrustRewriteResponse tresponse =
441
946912
          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
473456
      return processTrustRewriteResponse(theoryId, tresponse, true, tcpg);
445
    }
446
12642626
    return d_theoryRewriters[theoryId]->preRewrite(n);
447
  }
448
  return fn(&d_re, n);
449
}
450
451
11408429
RewriteResponse Rewriter::postRewrite(theory::TheoryId theoryId,
452
                                      TNode n,
453
                                      TConvProofGenerator* tcpg)
454
{
455
11408429
  Kind k = n.getKind();
456
  std::function<RewriteResponse(RewriteEnvironment*, TNode)> fn =
457
22816858
      (k == kind::EQUAL) ? d_postRewritersEqual[theoryId] : d_postRewriters[k];
458
11408429
  if (fn == nullptr)
459
  {
460
11408429
    if (tcpg != nullptr)
461
    {
462
      // same as above, for post-rewrite
463
      TrustRewriteResponse tresponse =
464
692840
          d_theoryRewriters[theoryId]->postRewriteWithProof(n);
465
346420
      return processTrustRewriteResponse(theoryId, tresponse, false, tcpg);
466
    }
467
11062011
    return d_theoryRewriters[theoryId]->postRewrite(n);
468
  }
469
  return fn(&d_re, n);
470
}
471
472
819876
RewriteResponse Rewriter::processTrustRewriteResponse(
473
    theory::TheoryId theoryId,
474
    const TrustRewriteResponse& tresponse,
475
    bool isPre,
476
    TConvProofGenerator* tcpg)
477
{
478
819876
  Assert(tcpg != nullptr);
479
1639752
  TrustNode trn = tresponse.d_node;
480
819876
  Assert(trn.getKind() == TrustNodeKind::REWRITE);
481
1639752
  Node proven = trn.getProven();
482
819876
  if (proven[0] != proven[1])
483
  {
484
194478
    ProofGenerator* pg = trn.getGenerator();
485
194478
    if (pg == nullptr)
486
    {
487
388956
      Node tidn = builtin::BuiltinProofRuleChecker::mkTheoryIdNode(theoryId);
488
      // add small step trusted rewrite
489
      Node rid = mkMethodId(isPre ? MethodId::RW_REWRITE_THEORY_PRE
490
388956
                                  : MethodId::RW_REWRITE_THEORY_POST);
491
777912
      tcpg->addRewriteStep(proven[0],
492
                           proven[1],
493
                           PfRule::THEORY_REWRITE,
494
                           {},
495
                           {proven, tidn, rid},
496
583434
                           isPre);
497
    }
498
    else
499
    {
500
      // store proven rewrite step
501
      tcpg->addRewriteStep(proven[0], proven[1], pg, isPre);
502
    }
503
  }
504
1639752
  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
29337
}  // namespace cvc5