summaryrefslogtreecommitdiff
path: root/gcc/cp/logic.cc
blob: e4967bbfa299151fd02880340866d54b4923909b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
/* Derivation and subsumption rules for constraints.
   Copyright (C) 2013-2016 Free Software Foundation, Inc.
   Contributed by Andrew Sutton (andrew.n.sutton@gmail.com)

This file is part of GCC.

GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.

GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */

#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "hash-set.h"
#include "machmode.h"
#include "vec.h"
#include "double-int.h"
#include "input.h"
#include "alias.h"
#include "symtab.h"
#include "wide-int.h"
#include "inchash.h"
#include "tree.h"
#include "stringpool.h"
#include "attribs.h"
#include "intl.h"
#include "flags.h"
#include "cp-tree.h"
#include "c-family/c-common.h"
#include "c-family/c-objc.h"
#include "cp-objcp-common.h"
#include "tree-inline.h"
#include "decl.h"
#include "toplev.h"
#include "type-utils.h"

#include <list>

namespace {

// Helper algorithms

// Increment iter distance(first, last) times.
template<typename I1, typename I2, typename I3>
  I1 next_by_distance (I1 iter, I2 first, I3 last)
  {
    for ( ; first != last; ++first, ++iter)
      ;
    return iter;
  }

/*---------------------------------------------------------------------------
                           Proof state
---------------------------------------------------------------------------*/

/* A term list is a list of atomic constraints. It is used
   to maintain the lists of assumptions and conclusions in a
   proof goal.

   Each term list maintains an iterator that refers to the current
   term. This can be used by various tactics to support iteration
   and stateful manipulation of the list. */
struct term_list : std::list<tree>
{
  term_list ();
  term_list (const term_list &x);
  term_list& operator= (const term_list &x);

  tree       current_term ()       { return *current; }
  const_tree current_term () const { return *current; }


  void insert (tree t);
  tree erase ();

  void start ();
  void next ();
  bool done() const;

  iterator current;
};

inline
term_list::term_list ()
  : std::list<tree> (), current (end ())
{ }

inline
term_list::term_list (const term_list &x)
  : std::list<tree> (x)
  , current (next_by_distance (begin (), x.begin (), x.current))
{ }

inline term_list&
term_list::operator= (const term_list &x)
{
  std::list<tree>::operator=(x);
  current = next_by_distance (begin (), x.begin (), x.current);
  return *this;
}

/* Try saving the term T into the list of terms. If
   T is already in the list of terms, then no action is
   performed. Otherwise, insert T before the current
   position, making this term current.

   Note that not inserting terms is an optimization
   that corresponds to the structural rule of
   contraction.

   NOTE: With the contraction rule, this data structure
   would be more efficiently represented as an ordered set
   or hash set.  */
void
term_list::insert (tree t)
{
  /* Search the current term list. If there is already
     a matching term, do not add the new one.  */
  for (iterator i = begin(); i != end(); ++i)
    if (cp_tree_equal (*i, t))
      return;

  current = std::list<tree>::insert (current, t);
}

/* Remove the current term from the list, repositioning to
   the term following the removed term. Note that the new
   position could be past the end of the list.

   The removed term is returned. */
inline tree
term_list::erase ()
{
  tree t = *current;
  current = std::list<tree>::erase (current);
  return t;
}

/* Initialize the current term to the first in the list. */
inline void
term_list::start ()
{
  current = begin ();
}

/* Advance to the next term in the list. */
inline void
term_list::next ()
{
  ++current;
}

/* Returns true when the current position is past the end. */
inline bool
term_list::done () const
{
  return current == end ();
}


/* A goal (or subgoal) models a sequent of the form
   'A |- C' where A and C are lists of assumptions and
   conclusions written as propositions in the constraint
   language (i.e., lists of trees).
*/
struct proof_goal
{
  term_list assumptions;
  term_list conclusions;
};

/* A proof state owns a list of goals and tracks the
   current sub-goal. The class also provides facilities
   for managing subgoals and constructing term lists. */
struct proof_state : std::list<proof_goal>
{
  proof_state ();

  iterator branch (iterator i);
};

/* An alias for proof state iterators. */
typedef proof_state::iterator goal_iterator;

/* Initialize the state with a single empty goal,
   and set that goal as the current subgoal. */
inline
proof_state::proof_state ()
  : std::list<proof_goal> (1)
{ }


/* Branch the current goal by creating a new subgoal,
   returning a reference to // the new object. This does
   not update the current goal. */
inline proof_state::iterator
proof_state::branch (iterator i)
{
  gcc_assert (i != end());
  proof_goal& g = *i;
  return insert (++i, g);
}

/*---------------------------------------------------------------------------
                           Logical rules
---------------------------------------------------------------------------*/

/*These functions modify the current state and goal by decomposing
  logical expressions using the logical rules of sequent calculus for
  first order logic.

  Note that in each decomposition rule, the term T has been erased
  from term list before the specific rule is applied. */

/* The left logical rule for conjunction adds both operands
   to the current set of constraints. */
void
left_conjunction (proof_state &, goal_iterator i, tree t)
{
  gcc_assert (TREE_CODE (t) == CONJ_CONSTR);

  /* Insert the operands into the current branch. Note that the
     final order of insertion is left-to-right. */
  term_list &l = i->assumptions;
  l.insert (TREE_OPERAND (t, 1));
  l.insert (TREE_OPERAND (t, 0));
}

/* The left logical rule for disjunction creates a new goal,
   adding the first operand to the original set of
   constraints and the second operand to the new set
   of constraints. */
void
left_disjunction (proof_state &s, goal_iterator i, tree t)
{
  gcc_assert (TREE_CODE (t) == DISJ_CONSTR);

  /* Branch the current subgoal. */
  goal_iterator j = s.branch (i);
  term_list &l1 = i->assumptions;
  term_list &l2 = j->assumptions;

  /* Insert operands into the different branches. */
  l1.insert (TREE_OPERAND (t, 0));
  l2.insert (TREE_OPERAND (t, 1));
}

/* The left logical rules for parameterized constraints
   adds its operand to the current goal. The list of
   parameters are effectively discarded. */
void
left_parameterized_constraint (proof_state &, goal_iterator i, tree t)
{
  gcc_assert (TREE_CODE (t) == PARM_CONSTR);
  term_list &l = i->assumptions;
  l.insert (PARM_CONSTR_OPERAND (t));
}

/*---------------------------------------------------------------------------
                           Decomposition
---------------------------------------------------------------------------*/

/* The following algorithms decompose expressions into sets of
   atomic propositions. In terms of the sequent calculus, these
   functions exercise the logical rules only.

   This is equivalent, for the purpose of determining subsumption,
   to rewriting a constraint in disjunctive normal form. It also
   allows the resulting assumptions to be used as declarations
   for the purpose of separate checking. */

/* Apply the left logical rules to the proof state. */
void
decompose_left_term (proof_state &s, goal_iterator i)
{
  term_list &l = i->assumptions;
  tree t = l.current_term ();
  switch (TREE_CODE (t))
    {
    case CONJ_CONSTR:
      left_conjunction (s, i, l.erase ());
      break;
    case DISJ_CONSTR:
      left_disjunction (s, i, l.erase ());
      break;
    case PARM_CONSTR:
      left_parameterized_constraint (s, i, l.erase ());
      break;
    default:
      l.next ();
      break;
    }
}

/* Apply the left logical rules of the sequent calculus
   until the current goal is fully decomposed into atomic
   constraints. */
void
decompose_left_goal (proof_state &s, goal_iterator i)
{
  term_list& l = i->assumptions;
  l.start ();
  while (!l.done ())
    decompose_left_term (s, i);
}

/* Apply the left logical rules of the sequent calculus
   until the antecedents are fully decomposed into atomic
   constraints. */
void
decompose_left (proof_state& s)
{
  goal_iterator iter = s.begin ();
  goal_iterator end = s.end ();
  for ( ; iter != end; ++iter)
    decompose_left_goal (s, iter);
}

/* Returns a vector of terms from the term list L. */
tree
extract_terms (term_list& l)
{
  tree result = make_tree_vec (l.size());
  term_list::iterator iter = l.begin();
  term_list::iterator end = l.end();
  for (int n = 0; iter != end; ++iter, ++n)
    TREE_VEC_ELT (result, n) = *iter;
  return result;
}

/* Extract the assumptions from the proof state S
   as a vector of vectors of atomic constraints. */
inline tree
extract_assumptions (proof_state& s)
{
  tree result = make_tree_vec (s.size ());
  goal_iterator iter = s.begin ();
  goal_iterator end = s.end ();
  for (int n = 0; iter != end; ++iter, ++n)
    TREE_VEC_ELT (result, n) = extract_terms (iter->assumptions);
  return result;
}

} // namespace

/* Decompose the required expression T into a constraint set: a
   vector of vectors containing only atomic propositions. If T is
   invalid, return an error. */
tree
decompose_assumptions (tree t)
{
  if (!t || t == error_mark_node)
    return t;

  /* Create a proof state, and insert T as the sole assumption. */
  proof_state s;
  term_list &l = s.begin ()->assumptions;
  l.insert (t);

  /* Decompose the expression into a constraint set, and then
     extract the terms for the AST. */
  decompose_left (s);
  return extract_assumptions (s);
}


/*---------------------------------------------------------------------------
                           Subsumption Rules
---------------------------------------------------------------------------*/

namespace {

bool subsumes_constraint (tree, tree);
bool subsumes_conjunction (tree, tree);
bool subsumes_disjunction (tree, tree);
bool subsumes_parameterized_constraint (tree, tree);
bool subsumes_atomic_constraint (tree, tree);

/* Returns true if the assumption A matches the conclusion C. This
   is generally the case when A and C have the same syntax.

   NOTE: There will be specialized matching rules to accommodate
   type equivalence, conversion, inheritance, etc. But this is not
   in the current concepts draft. */
inline bool
match_terms (tree a, tree c)
{
  return cp_tree_equal (a, c);
}

/* Returns true if the list of assumptions AS subsumes the atomic
   proposition C. This is the case when we can find a proposition
  in AS that entails the conclusion C. */
bool
subsumes_atomic_constraint (tree as, tree c)
{
  for (int i = 0; i < TREE_VEC_LENGTH (as); ++i)
    if (match_terms (TREE_VEC_ELT (as, i), c))
      return true;
  return false;
}

/* Returns true when both operands of C are subsumed by the
   assumptions AS. */
inline bool
subsumes_conjunction (tree as, tree c)
{
  tree l = TREE_OPERAND (c, 0);
  tree r = TREE_OPERAND (c, 1);
  return subsumes_constraint (as, l) && subsumes_constraint (as, r);
}

/* Returns true when either operand of C is subsumed by the
   assumptions AS. */
inline bool
subsumes_disjunction (tree as, tree c)
{
  tree l = TREE_OPERAND (c, 0);
  tree r = TREE_OPERAND (c, 1);
  return subsumes_constraint (as, l) || subsumes_constraint (as, r);
}

/* Returns true when the operand of C is subsumed by the
   assumptions in AS. The parameters are not considered in
   the subsumption rules. */
bool
subsumes_parameterized_constraint (tree as, tree c)
{
  tree t = PARM_CONSTR_OPERAND (c);
  return subsumes_constraint (as, t);
}


/* Returns true when the list of assumptions AS subsumes the
   concluded proposition C. This is a simple recursive descent
   on C, matching against propositions in the assumption list AS. */
bool
subsumes_constraint (tree as, tree c)
{
  switch (TREE_CODE (c))
    {
    case CONJ_CONSTR:
      return subsumes_conjunction (as, c);
    case DISJ_CONSTR:
      return subsumes_disjunction (as, c);
    case PARM_CONSTR:
      return subsumes_parameterized_constraint (as, c);
    default:
      return subsumes_atomic_constraint (as, c);
    }
}

/* Returns true if the LEFT constraints subsume the RIGHT constraints.
   This is done by checking that the RIGHT requirements follow from
   each of the LEFT subgoals. */
bool
subsumes_constraints_nonnull (tree left, tree right)
{
  gcc_assert (check_constraint_info (left));
  gcc_assert (check_constraint_info (right));

  /* Check that the required expression in RIGHT is subsumed by each
     subgoal in the assumptions of LEFT. */
  tree as = CI_ASSUMPTIONS (left);
  tree c = CI_NORMALIZED_CONSTRAINTS (right);
  for (int i = 0; i < TREE_VEC_LENGTH (as); ++i)
    if (!subsumes_constraint (TREE_VEC_ELT (as, i), c))
      return false;
  return true;
}

} /* namespace */

/* Returns true if the LEFT constraints subsume the RIGHT
   constraints. */
bool
subsumes (tree left, tree right)
{
  if (left == right)
    return true;
  if (!left)
    return false;
  if (!right)
    return true;
  return subsumes_constraints_nonnull (left, right);
}