GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/rewriter.cpp Lines: 171 195 87.7 %
Date: 2021-08-19 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
154787599
static TheoryId theoryOf(TNode node) {
44
154787599
  if (node.getKind() == kind::EQUAL)
45
  {
46
    // Equality is owned by the theory that owns the domain
47
22791870
    return Theory::theoryOf(node[0].getType());
48
  }
49
  // Regular nodes are owned by the kind
50
131995729
  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
130612792
struct RewriteStackElement {
59
  /**
60
   * Construct a fresh stack element.
61
   */
62
39050880
  RewriteStackElement(TNode node, TheoryId theoryId)
63
39050880
      : d_node(node),
64
        d_original(node),
65
        d_theoryId(theoryId),
66
        d_originalTheoryId(theoryId),
67
39050880
        d_nextChild(0)
68
  {
69
39050880
  }
70
71
232418133
  TheoryId getTheoryId() { return static_cast<TheoryId>(d_theoryId); }
72
73
23015124
  TheoryId getOriginalTheoryId()
74
  {
75
23015124
    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
40618950
Node Rewriter::rewrite(TNode node) {
99
40618950
  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
8999498
    return node;
104
  }
105
31619452
  return getInstance()->rewriteTo(theoryOf(node), node);
106
}
107
108
60462
TrustNode Rewriter::rewriteWithProof(TNode node,
109
                                     bool isExtEq)
110
{
111
  // must set the proof checker before calling this
112
60462
  Assert(d_tpg != nullptr);
113
60462
  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
120866
  Node ret = getInstance()->rewriteTo(theoryOf(node), node, d_tpg.get());
121
60433
  return TrustNode::mkTrustRewrite(node, ret, d_tpg.get());
122
}
123
124
3769
void Rewriter::setProofNodeManager(ProofNodeManager* pnm)
125
{
126
  // if not already initialized with proof support
127
3769
  if (d_tpg == nullptr)
128
  {
129
3769
    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
7538
    d_tpg.reset(new TConvProofGenerator(pnm,
133
                                        nullptr,
134
                                        TConvPolicy::FIXPOINT,
135
                                        TConvCachePolicy::STATIC,
136
3769
                                        "Rewriter::TConvProofGenerator"));
137
  }
138
3769
}
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
128138
void Rewriter::registerTheoryRewriter(theory::TheoryId tid,
149
                                      TheoryRewriter* trew)
150
{
151
128138
  getInstance()->d_theoryRewriters[tid] = trew;
152
128138
}
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
13622
TheoryRewriter* Rewriter::getTheoryRewriter(theory::TheoryId theoryId)
183
{
184
13622
  return d_theoryRewriters[theoryId];
185
}
186
187
31808346
Rewriter* Rewriter::getInstance()
188
{
189
31808346
  return smt::currentSmtEngine()->getRewriter();
190
}
191
192
32874651
Node Rewriter::rewriteTo(theory::TheoryId theoryId,
193
                         Node node,
194
                         TConvProofGenerator* tcpg)
195
{
196
  RewriteWithProofsAttribute rpfa;
197
#ifdef CVC5_ASSERTIONS
198
32874651
  bool isEquality = node.getKind() == kind::EQUAL && (!node[0].getType().isBoolean());
199
200
32874651
  if (d_rewriteStack == nullptr)
201
  {
202
9488
    d_rewriteStack.reset(new std::unordered_set<Node>());
203
  }
204
#endif
205
206
32874651
  Trace("rewriter") << "Rewriter::rewriteTo(" << theoryId << "," << node << ")"<< std::endl;
207
208
  // Check if it's been cached already
209
65749302
  Node cached = getPostRewriteCache(theoryId, node);
210
32874651
  if (!cached.isNull() && (tcpg == nullptr || node.getAttribute(rpfa)))
211
  {
212
26853073
    return cached;
213
  }
214
215
  // Put the node on the stack in order to start the "recursive" rewrite
216
12043156
  vector<RewriteStackElement> rewriteStack;
217
6021578
  rewriteStack.push_back(RewriteStackElement(node, theoryId));
218
219
6021578
  ResourceManager* rm = NULL;
220
6021578
  bool hasSmtEngine = smt::smtEngineInScope();
221
6021578
  if (hasSmtEngine) {
222
6021578
    rm = smt::currentResourceManager();
223
  }
224
  // Rewrite until the stack is empty
225
  for (;;){
226
72080180
    if (hasSmtEngine)
227
    {
228
72080180
      rm->spendResource(Resource::RewriteStep);
229
    }
230
231
    // Get the top of the recursion stack
232
72080180
    RewriteStackElement& rewriteStackTop = rewriteStack.back();
233
234
144160360
    Trace("rewriter") << "Rewriter::rewriting: "
235
144160360
                      << rewriteStackTop.getTheoryId() << ","
236
72080180
                      << rewriteStackTop.d_node << std::endl;
237
238
    // Before rewriting children we need to do a pre-rewrite of the node
239
72080180
    if (rewriteStackTop.d_nextChild == 0)
240
    {
241
      // Check if the pre-rewrite has already been done (it's in the cache)
242
39050880
      cached = getPreRewriteCache(rewriteStackTop.getTheoryId(),
243
                                  rewriteStackTop.d_node);
244
78101760
      if (cached.isNull()
245
39050880
          || (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
14289142
              rewriteStackTop.getTheoryId(), rewriteStackTop.d_node, tcpg);
252
253
          // Put the rewritten node to the top of the stack
254
13088959
          rewriteStackTop.d_node = response.d_node;
255
13088959
          TheoryId newTheory = theoryOf(rewriteStackTop.d_node);
256
          // In the pre-rewrite, if changing theories, we just call the other theories pre-rewrite
257
26177918
          if (newTheory == rewriteStackTop.getTheoryId()
258
13088959
              && response.d_status == REWRITE_DONE)
259
          {
260
11888776
            break;
261
          }
262
1200183
          rewriteStackTop.d_theoryId = newTheory;
263
1200183
        }
264
265
        // Cache the rewrite
266
11888776
        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
27162104
        rewriteStackTop.d_node = cached;
274
27162104
        rewriteStackTop.d_theoryId = theoryOf(cached);
275
      }
276
    }
277
278
72080180
    rewriteStackTop.d_original = rewriteStackTop.d_node;
279
    // Now it's time to rewrite the children, check if this has already been done
280
72080180
    cached = getPostRewriteCache(rewriteStackTop.getTheoryId(),
281
                                 rewriteStackTop.d_node);
282
    // If not, go through the children
283
144160360
    if (cached.isNull()
284
72080180
        || (tcpg != nullptr && !rewriteStackTop.d_node.getAttribute(rpfa)))
285
    {
286
      // The child we need to rewrite
287
44155652
      unsigned child = rewriteStackTop.d_nextChild++;
288
289
      // To build the rewritten expression we set up the builder
290
44155652
      if(child == 0) {
291
11126352
        if (rewriteStackTop.d_node.getNumChildren() > 0)
292
        {
293
          // The children will add themselves to the builder once they're done
294
10517393
          rewriteStackTop.d_builder << rewriteStackTop.d_node.getKind();
295
10517393
          kind::MetaKind metaKind = rewriteStackTop.d_node.getMetaKind();
296
10517393
          if (metaKind == kind::metakind::PARAMETERIZED) {
297
717591
            rewriteStackTop.d_builder << rewriteStackTop.d_node.getOperator();
298
          }
299
        }
300
      }
301
302
      // Process the next child
303
44155652
      if (child < rewriteStackTop.d_node.getNumChildren())
304
      {
305
        // The child node
306
66058604
        Node childNode = rewriteStackTop.d_node[child];
307
        // Push the rewrite request to the stack (NOTE: rewriteStackTop might be a bad reference now)
308
33029302
        rewriteStack.push_back(RewriteStackElement(childNode, theoryOf(childNode)));
309
        // Go on with the rewriting
310
33029302
        continue;
311
      }
312
313
      // Incorporate the children if necessary
314
11126350
      if (rewriteStackTop.d_node.getNumChildren() > 0)
315
      {
316
21034782
        Node rewritten = rewriteStackTop.d_builder;
317
10517391
        rewriteStackTop.d_node = rewritten;
318
10517391
        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
11643870
            rewriteStackTop.getTheoryId(), rewriteStackTop.d_node, tcpg);
326
327
        // We continue with the response we got
328
11385107
        TheoryId newTheoryId = theoryOf(response.d_node);
329
22770214
        if (newTheoryId != rewriteStackTop.getTheoryId()
330
11385107
            || response.d_status == REWRITE_AGAIN_FULL)
331
        {
332
          // In the post rewrite if we've changed theories, we must do a full rewrite
333
1194766
          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
1194766
          Assert(d_rewriteStack->find(response.d_node)
337
                 == d_rewriteStack->end());
338
1194766
          d_rewriteStack->insert(response.d_node);
339
#endif
340
2389532
          Node rewritten = rewriteTo(newTheoryId, response.d_node, tcpg);
341
1194766
          rewriteStackTop.d_node = rewritten;
342
#ifdef CVC5_ASSERTIONS
343
1194766
          d_rewriteStack->erase(response.d_node);
344
#endif
345
1194766
          break;
346
        }
347
10190341
        else if (response.d_status == REWRITE_DONE)
348
        {
349
#ifdef CVC5_ASSERTIONS
350
          RewriteResponse r2 =
351
19863164
              d_theoryRewriters[newTheoryId]->postRewrite(response.d_node);
352
19863164
          Assert(r2.d_node == response.d_node)
353
9931582
              << r2.d_node << " != " << response.d_node;
354
#endif
355
9931582
          rewriteStackTop.d_node = response.d_node;
356
9931582
          break;
357
        }
358
        // Check for trivial rewrite loops of size 1 or 2
359
258759
        Assert(response.d_node != rewriteStackTop.d_node);
360
258759
        Assert(d_theoryRewriters[rewriteStackTop.getTheoryId()]
361
                   ->postRewrite(response.d_node)
362
                   .d_node
363
               != rewriteStackTop.d_node);
364
258759
        rewriteStackTop.d_node = response.d_node;
365
258759
      }
366
367
      // We're done with the post rewrite, so we add to the cache
368
11126348
      if (tcpg != nullptr)
369
      {
370
        // if proofs are enabled, mark that we've rewritten with proofs
371
345403
        rewriteStackTop.d_original.setAttribute(rpfa, true);
372
345403
        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
331328
          if (rewriteStackTop.d_node != cached)
379
          {
380
8
            Trace("rewriter-proof") << "WARNING: Rewritten forms with and "
381
4
                                       "without proofs were not equivalent"
382
4
                                    << std::endl;
383
8
            Trace("rewriter-proof")
384
4
                << "   original: " << rewriteStackTop.d_original << std::endl;
385
8
            Trace("rewriter-proof")
386
4
                << "with proofs: " << rewriteStackTop.d_node << std::endl;
387
4
            Trace("rewriter-proof") << " w/o proofs: " << cached << std::endl;
388
8
            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
8
            tcpg->addRewriteStep(rewriteStackTop.d_node,
392
                                 cached,
393
                                 PfRule::TRUST_REWRITE,
394
                                 {},
395
                                 {eq},
396
4
                                 false);
397
            // don't overwrite the cache, should be the same
398
4
            rewriteStackTop.d_node = cached;
399
          }
400
        }
401
      }
402
11126348
      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
27924528
      rewriteStackTop.d_node = cached;
410
27924528
      rewriteStackTop.d_theoryId = theoryOf(cached);
411
    }
412
413
    // If this is the last node, just return
414
39050876
    if (rewriteStack.size() == 1) {
415
6021576
      Assert(!isEquality || rewriteStackTop.d_node.getKind() == kind::EQUAL
416
             || rewriteStackTop.d_node.isConst());
417
6021576
      return rewriteStackTop.d_node;
418
    }
419
420
    // We're done with this node, append it to the parent
421
33029300
    rewriteStack[rewriteStack.size() - 2].d_builder << rewriteStackTop.d_node;
422
33029300
    rewriteStack.pop_back();
423
66058602
  }
424
425
  Unreachable();
426
} /* Rewriter::rewriteTo() */
427
428
13088959
RewriteResponse Rewriter::preRewrite(theory::TheoryId theoryId,
429
                                     TNode n,
430
                                     TConvProofGenerator* tcpg)
431
{
432
13088959
  Kind k = n.getKind();
433
  std::function<RewriteResponse(RewriteEnvironment*, TNode)> fn =
434
26177918
      (k == kind::EQUAL) ? d_preRewritersEqual[theoryId] : d_preRewriters[k];
435
13088959
  if (fn == nullptr)
436
  {
437
13088959
    if (tcpg != nullptr)
438
    {
439
      // call the trust rewrite response interface
440
      TrustRewriteResponse tresponse =
441
949392
          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
474696
      return processTrustRewriteResponse(theoryId, tresponse, true, tcpg);
445
    }
446
12614263
    return d_theoryRewriters[theoryId]->preRewrite(n);
447
  }
448
  return fn(&d_re, n);
449
}
450
451
11385109
RewriteResponse Rewriter::postRewrite(theory::TheoryId theoryId,
452
                                      TNode n,
453
                                      TConvProofGenerator* tcpg)
454
{
455
11385109
  Kind k = n.getKind();
456
  std::function<RewriteResponse(RewriteEnvironment*, TNode)> fn =
457
22770218
      (k == kind::EQUAL) ? d_postRewritersEqual[theoryId] : d_postRewriters[k];
458
11385109
  if (fn == nullptr)
459
  {
460
11385109
    if (tcpg != nullptr)
461
    {
462
      // same as above, for post-rewrite
463
      TrustRewriteResponse tresponse =
464
695198
          d_theoryRewriters[theoryId]->postRewriteWithProof(n);
465
347599
      return processTrustRewriteResponse(theoryId, tresponse, false, tcpg);
466
    }
467
11037512
    return d_theoryRewriters[theoryId]->postRewrite(n);
468
  }
469
  return fn(&d_re, n);
470
}
471
472
822295
RewriteResponse Rewriter::processTrustRewriteResponse(
473
    theory::TheoryId theoryId,
474
    const TrustRewriteResponse& tresponse,
475
    bool isPre,
476
    TConvProofGenerator* tcpg)
477
{
478
822295
  Assert(tcpg != nullptr);
479
1644590
  TrustNode trn = tresponse.d_node;
480
822295
  Assert(trn.getKind() == TrustNodeKind::REWRITE);
481
1644590
  Node proven = trn.getProven();
482
822295
  if (proven[0] != proven[1])
483
  {
484
194733
    ProofGenerator* pg = trn.getGenerator();
485
194733
    if (pg == nullptr)
486
    {
487
389466
      Node tidn = builtin::BuiltinProofRuleChecker::mkTheoryIdNode(theoryId);
488
      // add small step trusted rewrite
489
      Node rid = mkMethodId(isPre ? MethodId::RW_REWRITE_THEORY_PRE
490
389466
                                  : MethodId::RW_REWRITE_THEORY_POST);
491
778932
      tcpg->addRewriteStep(proven[0],
492
                           proven[1],
493
                           PfRule::THEORY_REWRITE,
494
                           {},
495
                           {proven, tidn, rid},
496
584199
                           isPre);
497
    }
498
    else
499
    {
500
      // store proven rewrite step
501
      tcpg->addRewriteStep(proven[0], proven[1], pg, isPre);
502
    }
503
  }
504
1644590
  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
29349
}  // namespace cvc5