souffle  2.0.2-371-g6315b36
Namespaces | Data Structures | Typedefs | Functions
souffle::ast::analysis Namespace Reference

Namespaces

 detail
 

Data Structures

class  AlgebraicDataType
 Aggregates types using sums and products. More...
 
struct  all_type_factory
 A factory for computing sets of types covering all potential types. More...
 
class  Analysis
 Abstract class for a AST Analysis. More...
 
class  Assignment
 An assignment maps a list of variables to values of their respective property space. More...
 
class  AuxiliaryArityAnalysis
 Determine the auxiliary arity for relations. More...
 
class  ClauseNormalisationAnalysis
 
class  ComponentLookupAnalysis
 
class  ConstantType
 Representing the type assigned to a constant. More...
 
class  Constraint
 A generic base class for constraints on variables. More...
 
class  ConstraintAnalysis
 A base class for ConstraintAnalysis collecting constraints for an analysis by visiting every node of a given AST. More...
 
struct  ConstraintAnalysisVar
 A variable type to be utilized by AST constraint analysis. More...
 
class  FunctorAnalysis
 
class  IOTypeAnalysis
 
class  NormalisedClause
 
class  PolymorphicObjectsAnalysis
 
class  PrecedenceGraphAnalysis
 Analysis pass computing the precedence graph of the relations of the datalog progam. More...
 
class  PrimitiveType
 PrimitiveType = Number/Unsigned/Float/Symbol The class representing pre-built, concrete types. More...
 
class  Problem
 A problem is a list of constraints for which a solution is desired. More...
 
class  ProfileUseAnalysis
 Analysis that loads profile data and has a profile query interface. More...
 
struct  property_space
 A MPL type for defining a property space. More...
 
struct  RecordType
 A record type combining a list of fields into a new, aggregated type. More...
 
class  RecursiveClausesAnalysis
 Analysis pass identifying clauses which are recursive. More...
 
class  RedundantRelationsAnalysis
 Analysis pass identifying relations which do not contribute to the computation of the output relations. More...
 
class  RelationDetailCacheAnalysis
 Analysis pass mapping identifiers with relations and clauses. More...
 
class  RelationScheduleAnalysis
 Analysis pass computing a schedule for computing relations. More...
 
class  RelationScheduleAnalysisStep
 A single step in a relation schedule, consisting of the relations computed in the step and the relations that are no longer required at that step. More...
 
class  SCCGraphAnalysis
 Analysis pass computing the strongly connected component (SCC) graph for the datalog program. More...
 
struct  set_property_space
 A property space for set-based properties based on sub-set lattices. More...
 
struct  sub_type
 An implementation of a meet operation between sets of types computing the set of pair-wise greatest common subtypes. More...
 
class  SubsetType
 A type being a subset of another type. More...
 
class  SumTypeBranchesAnalysis
 
class  TopologicallySortedSCCGraphAnalysis
 Analysis pass computing a topologically sorted strongly connected component (SCC) graph. More...
 
class  Type
 An abstract base class for types to be covered within a type environment. More...
 
struct  type_lattice
 The type lattice forming the property space for the Type analysis. More...
 
class  TypeAnalysis
 
class  TypeBinding
 Class that encapsulates std::map of types binding that comes from .init c = Comp<MyType> Type binding in this example would be T->MyType if the component code is .comp Comp<T> ... More...
 
class  TypeConstraintsAnalysis
 Constraint analysis framework for types. More...
 
class  TypeEnvironment
 A type environment is a set of types. More...
 
class  TypeEnvironmentAnalysis
 
struct  TypeSet
 A collection to represent sets of types. More...
 
struct  TypeVisitor
 A visitor for Types. More...
 
class  UnionType
 A union type combining a list of types into a new, aggregated type. More...
 
struct  Variable
 A variable to be utilized within constraints to be handled by the constraint solver. More...
 
class  VisitOnceTypeVisitor
 A visitor for types visiting each type only once (effectively breaking recursive cycles). More...
 

Typedefs

using TypeConstraint = std::shared_ptr< Constraint< TypeVar > >
 The definition of the type of constraint to be utilized in the type analysis. More...
 
using TypeVar = ConstraintAnalysisVar< type_lattice >
 The definition of the type of variable to be utilized in the type analysis. More...
 

Functions

bool areEquivalentTypes (const Type &a, const Type &b)
 Determine if two types are equivalent. More...
 
std::string findUniqueRelationName (const Program &program, std::string base)
 Find a new relation name. More...
 
std::string findUniqueVariableName (const Clause &clause, std::string base)
 Find a variable name using base to form a string like base1 Use this when you need to limit the scope of a variable to the inside of an aggregate. More...
 
static const TypegetBaseType (const Type *type)
 
TypeSet getGreatestCommonSubtypes (const Type &a, const Type &b)
 Computes the greatest common sub types of the two given types. More...
 
template<typename... Types>
TypeSet getGreatestCommonSubtypes (const Types &... types)
 Computes the greatest common sub types of the given types. More...
 
TypeSet getGreatestCommonSubtypes (const TypeSet &a, const TypeSet &b)
 The set of pair-wise greatest common sub types of the types in the two given sets. More...
 
TypeSet getGreatestCommonSubtypes (const TypeSet &set)
 Computes the greatest common sub types of all the types in the given set. More...
 
std::map< const Argument *, bool > getGroundedTerms (const TranslationUnit &tu, const Clause &clause)
 Analyse the given clause and computes for each contained argument whether it is a grounded value or not. More...
 
std::set< std::string > getInjectedVariables (const TranslationUnit &tu, const Clause &clause, const Aggregator &aggregate)
 Given an aggregate and a clause, we find all the variables that have been injected into the aggregate. More...
 
std::set< std::string > getLocalVariables (const TranslationUnit &tu, const Clause &clause, const Aggregator &aggregate)
 Computes the set of local variables in an aggregate expression. More...
 
TypeAttribute getTypeAttribute (const Type &type)
 
std::optional< TypeAttributegetTypeAttribute (const TypeSet &type)
 
std::string getTypeQualifier (const Type &type)
 Returns full type qualifier for a given type. More...
 
std::set< std::string > getVariablesOutsideAggregate (const Clause &clause, const Aggregator &aggregate)
 Computes the set of variables occurring outside the aggregate. More...
 
std::set< std::string > getWitnessVariables (const TranslationUnit &tu, const Clause &clause, const Aggregator &aggregate)
 Computes the set of witness variables that are used in the aggregate A variable is a witness if it occurs in the aggregate body (but not in an inner aggregate) and also occurs ungrounded in the outer scope. More...
 
static TypeConstraint hasSuperTypeInSet (const TypeVar &var, TypeSet values)
 A constraint factory ensuring that all the types associated to the variable are subtypes of some type in the provided set (values) More...
 
bool haveCommonSupertype (const Type &a, const Type &b)
 Determine if there exist a type t such that a <: t and b <: t. More...
 
bool isADTEnum (const AlgebraicDataType &type)
 Determine if ADT is enumerations (are all constructors empty) More...
 
bool isNumericType (const TypeSet &type)
 
bool isOfKind (const Type &type, TypeAttribute kind)
 Check if the type is of a kind corresponding to the TypeAttribute. More...
 
bool isOfKind (const TypeSet &typeSet, TypeAttribute kind)
 
bool isOfRootType (const Type &type, const Type &root)
 Determines whether the given type is a sub-type of the given root type. More...
 
bool isOrderableType (const TypeSet &type)
 
bool isSubtypeOf (const Type &a, const Type &b)
 Determines whether type a is a subtype of type b. More...
 
static TypeConstraint isSubtypeOf (const TypeVar &a, const TypeVar &b)
 A constraint factory ensuring that all the types associated to the variable a are subtypes of the variable b. More...
 
static TypeConstraint isSubtypeOf (const TypeVar &variable, const Type &type)
 A constraint factory ensuring that all the types associated to the variable a are subtypes of type b. More...
 
static TypeConstraint isSubtypeOfComponent (const TypeVar &elementVariable, const TypeVar &recordVariable, size_t index)
 Constraint on record type and its elements. More...
 
static TypeConstraint satisfiesOverload (const TypeEnvironment &typeEnv, IntrinsicFunctors overloads, TypeVar result, std::vector< TypeVar > args, bool subtypeResult)
 Given a set of overloads, wait the list of candidates to reduce to one and then apply its constraints. More...
 
template<typename Var , typename Val = typename Var::property_space::value_type>
std::shared_ptr< Constraint< Var > > sub (const Val &a, const Var &b, const std::string &symbol="⊑")
 A generic factory for constraints of the form. More...
 
template<typename Var >
std::shared_ptr< Constraint< Var > > sub (const Var &a, const Var &b, const std::string &symbol="⊑")
 A generic factory for constraints of the form. More...
 
static TypeConstraint subtypesOfTheSameBaseType (const TypeVar &left, const TypeVar &right)
 Ensure that types of left and right have the same base types. More...
 

Typedef Documentation

◆ TypeConstraint

using souffle::ast::analysis::TypeConstraint = typedef std::shared_ptr<Constraint<TypeVar> >

The definition of the type of constraint to be utilized in the type analysis.

Definition at line 75 of file TypeConstraints.h.

◆ TypeVar

The definition of the type of variable to be utilized in the type analysis.

Definition at line 72 of file TypeConstraints.h.

Function Documentation

◆ areEquivalentTypes()

bool souffle::ast::analysis::areEquivalentTypes ( const Type a,
const Type b 
)

Determine if two types are equivalent.

That is, check if a <: b and b <: a

Definition at line 373 of file TypeSystem.cpp.

◆ findUniqueRelationName()

std::string souffle::ast::analysis::findUniqueRelationName ( const Program program,
std::string  base 
)

Find a new relation name.

I use this when I create new relations either for aggregate bodies or singleton aggregates.

Definition at line 190 of file Aggregate.cpp.

◆ findUniqueVariableName()

std::string souffle::ast::analysis::findUniqueVariableName ( const Clause clause,
std::string  base 
)

Find a variable name using base to form a string like base1 Use this when you need to limit the scope of a variable to the inside of an aggregate.

Definition at line 179 of file Aggregate.cpp.

184  {
185  int counter = 0;
186  auto candidate = base;
187  while (getRelation(program, candidate) != nullptr) {
188  candidate = base + toString(counter++);

Referenced by souffle::ast::transform::SimplifyAggregateTargetExpressionTransformer::transform().

◆ getBaseType()

static const Type& souffle::ast::analysis::getBaseType ( const Type type)
static

Definition at line 118 of file TypeConstraints.cpp.

126  {

◆ getGreatestCommonSubtypes() [1/4]

TypeSet souffle::ast::analysis::getGreatestCommonSubtypes ( const Type a,
const Type b 
)

Computes the greatest common sub types of the two given types.

Definition at line 254 of file TypeSystem.cpp.

255  {
256  return TypeSet(b);
257  }
258 
259  TypeSet res;
260  if (isA<UnionType>(a) && isA<UnionType>(b)) {
261  // collect common sub-types of union types
262  struct collector : public TypeVisitor<void> {
263  const Type& b;
264  TypeSet& res;
265  collector(const Type& b, TypeSet& res) : b(b), res(res) {}
266 
267  void visit(const Type& type) const override {
268  if (isSubtypeOf(type, b)) {
269  res.insert(type);
270  } else {
271  TypeVisitor<void>::visit(type);
272  }
273  }
274  void visitUnionType(const UnionType& type) const override {
275  for (const auto& cur : type.getElementTypes()) {
276  visit(*cur);
277  }
278  }
279  };
280 
281  // collect all common sub-types
282  collector(b, res).visit(a);
283  }
284 
285  // otherwise there is no common super type
286  return res;
287 }
288 
289 TypeSet getGreatestCommonSubtypes(const TypeSet& set) {
290  // Edge cases.
291  if (set.empty() || set.isAll()) {
292  return TypeSet();
293  }

Referenced by getGreatestCommonSubtypes().

◆ getGreatestCommonSubtypes() [2/4]

template<typename... Types>
TypeSet souffle::ast::analysis::getGreatestCommonSubtypes ( const Types &...  types)

Computes the greatest common sub types of the given types.

Definition at line 542 of file TypeSystem.h.

◆ getGreatestCommonSubtypes() [3/4]

TypeSet souffle::ast::analysis::getGreatestCommonSubtypes ( const TypeSet a,
const TypeSet b 
)

The set of pair-wise greatest common sub types of the types in the two given sets.

Definition at line 311 of file TypeSystem.cpp.

314  {
315  return b;
316  }
317  if (b.isAll()) {
318  return a;
319  }
320 
321  // compute pairwise greatest common sub types
322  TypeSet res;
323  for (const Type& x : a) {
324  for (const Type& y : b) {
325  res.insert(getGreatestCommonSubtypes(x, y));
326  }
327  }
328  return res;
329 }
330 
331 bool haveCommonSupertype(const Type& a, const Type& b) {
332  assert(&a.getTypeEnvironment() == &b.getTypeEnvironment() &&
333  "Types must be in the same type environment");
334 
335  if (a == b) {

◆ getGreatestCommonSubtypes() [4/4]

TypeSet souffle::ast::analysis::getGreatestCommonSubtypes ( const TypeSet set)

Computes the greatest common sub types of all the types in the given set.

Definition at line 295 of file TypeSystem.cpp.

298  : set) {
299  greatestCommonSubtypes = getGreatestCommonSubtypes(TypeSet(type), greatestCommonSubtypes);
300  }
301 
302  return greatestCommonSubtypes;
303 }
304 
305 TypeSet getGreatestCommonSubtypes(const TypeSet& a, const TypeSet& b) {
306  // special cases
307  if (a.empty()) {
308  return a;
309  }

References getGreatestCommonSubtypes().

Here is the call graph for this function:

◆ getGroundedTerms()

std::map< const Argument *, bool > souffle::ast::analysis::getGroundedTerms ( const TranslationUnit tu,
const Clause clause 
)

Analyse the given clause and computes for each contained argument whether it is a grounded value or not.

Parameters
tuthe translation unit containing the clause
clausethe clause to be analyzed
Returns
a map mapping each contained argument to a boolean indicating whether the argument represents a grounded value or not

Definition at line 278 of file Ground.cpp.

◆ getInjectedVariables()

std::set< std::string > souffle::ast::analysis::getInjectedVariables ( const TranslationUnit tu,
const Clause clause,
const Aggregator aggregate 
)

Given an aggregate and a clause, we find all the variables that have been injected into the aggregate.

This means that the variable occurs grounded in an outer scope. BUT does not occur in the target expression.

This means that the variable occurs grounded in an outer scope.

Imagine we have this:

A(letter, x) :- a(letter, _), x = min y : { a(l, y), y < max y : { a(letter, y) } }, z = max y : {B(y) }.

We need to figure out the base literal in which this aggregate occurs, so that we don't delete them Other aggregates occurring in the rule are irrelevant, so we'll replace the aggregate with a dummy aggregate variable. Then after that,

  1. A(letter, x) :- a(letter, _), x = min y : { a(l, y), y < max y : { a(letter, y) } }, z = aggr_var_0.

We should also ground all the aggr_vars so that this grounding transfers to whatever variable they might be assigned to.

A(letter, x) :- a(letter, _), x = min y : { a(l, y), y < max y : { a(letter, y) } }, z = aggr_var_0, grounding_atom(aggr_var_0).

Remember to negate the head atom and add it to the body. The clause we deal with will have a dummy head (*())

*() :- !A(letter, x), a(letter, _), x = min y : { a(l, y), y < max y : { a(letter, y) } }, z = aggr_var_0, grounding_atom(aggr_var_0).

Replace the original aggregate with the same shit because we can't be counting local variables n shit

*() :- !A(letter, x), a(letter, _), x = min y : { a(l, y), y < aggr_var_1 } }, z = aggr_var_0, grounding_atom(aggr_var_0).

Now, as long as we recorded all of the variables that occurred inside the aggregate, the intersection of these two sets (variables in an outer scope) and (variables occurring in t

A variable is considered injected into an aggregate if it occurs both within the aggregate AS WELL AS grounded in the outer scope. An outer scope could be an outer aggregate, or also just the base/rule scope. (i.e. not inside of another aggregate that is not an ancestor of this one).

So then in order to calculate the set of variables that have been injected into an aggregate, we perform the following steps:

  1. Find the set of variables occurring within the aggregate "aggregate"
  2. Find the set of variables that occur grounded in the outside scope 2a. Replace all non-ancestral aggregates with variables so that we can ignore their variable contents (it is not relevant). 2b. Replace the target aggregate with a variable as well 2c. Ground all occurrences of the non-ancestral aggregate variable replacements (i.e. grounding_atom(+aggr_var_0)) 2d. Visit all variables occurring in this tweaked clause that we made from steps 2a-2c, and check if they are grounded, and also occur in the set of target aggregate variables. Then it is an injected variable.

Definition at line 205 of file Aggregate.cpp.

206  : { a(l, y),
207  * y < max y : { a(letter, y) } },
208  * z = max y : {B(y) }.
209  *
210  * We need to figure out the base literal in which this aggregate occurs,
211  * so that we don't delete them
212  * Other aggregates occurring in the rule are irrelevant, so we'll replace
213  * the aggregate with a dummy aggregate variable. Then after that,
214  *
215  * 2. A(letter, x) :-
216  * a(letter, _),
217  * x = min y : { a(l, y),
218  * y < max y : { a(letter, y) } },
219  * z = aggr_var_0.
220  *
221  * We should also ground all the aggr_vars so that this grounding
222  * transfers to whatever variable they might be assigned to.
223  *
224  * A(letter, x) :-
225  * a(letter, _),
226  * x = min y : { a(l, y),
227  * y < max y : { a(letter, y) } },
228  * z = aggr_var_0,
229  * grounding_atom(aggr_var_0).
230  *
231  * Remember to negate the head atom and add it to the body. The clause we deal with will have a
232  * dummy head (*())
233  *
234  * *() :- !A(letter, x), a(letter, _), x = min y : { a(l, y), y < max y : { a(letter, y) } },
235  * z = aggr_var_0, grounding_atom(aggr_var_0).
236  *
237  * Replace the original aggregate with the same shit because we can't be counting local variables n shit
238  *
239  * *() :- !A(letter, x), a(letter, _), x = min y : { a(l, y), y < aggr_var_1 } }, z = aggr_var_0,
240  * grounding_atom(aggr_var_0).
241  *
242  * Now, as long as we recorded all of the variables that occurred inside the aggregate, the intersection
243  * of these two sets (variables in an outer scope) and (variables occurring in t
244  **/
245 
246  /**
247  * A variable is considered *injected* into an aggregate if it occurs both within the aggregate AS WELL
248  * AS *grounded* in the outer scope. An outer scope could be an outer aggregate, or also just the
249  *base/rule scope. (i.e. not inside of another aggregate that is not an ancestor of this one).
250  *
251  * So then in order to calculate the set of variables that have been injected into an aggregate, we
252  * perform the following steps:
253  *
254  * 1. Find the set of variables occurring within the aggregate "aggregate"
255  * 2. Find the set of variables that occur grounded in the outside scope
256  * 2a. Replace all non-ancestral aggregates with variables so that we can ignore their variable
257  * contents (it is not relevant). 2b. Replace the target aggregate with a variable as well 2c. Ground all
258  * occurrences of the non-ancestral aggregate variable replacements (i.e. grounding_atom(+aggr_var_0)) 2d.
259  * Visit all variables occurring in this tweaked clause that we made from steps 2a-2c, and check if they
260  * are grounded, and also occur in the set of target aggregate variables. Then it is an injected variable.
261  **/
262  // Step 1
263  std::set<std::string> variablesInTargetAggregate;
264  visitDepthFirst(aggregate,
265  [&](const Variable& variable) { variablesInTargetAggregate.insert(variable.getName()); });
266 
267  std::set<Own<Aggregator>> ancestorAggregates;
268 
269  visitDepthFirst(clause, [&](const Aggregator& ancestor) {
270  visitDepthFirst(ancestor, [&](const Aggregator& agg) {
271  if (agg == aggregate) {
272  ancestorAggregates.insert(souffle::clone(&ancestor));
273  }
274  });
275  });
276 
277  // 1. Replace non-ancestral aggregates with variables
278  struct ReplaceAggregatesWithVariables : public NodeMapper {
279  // Variables introduced to replace aggregators
280  mutable std::set<std::string> aggregatorVariables;
281  // ancestors are never replaced so don't need to clone, but actually
282  // it will have a child that will become invalid at one point. This is a concern. Need to clone these
283  // bastards too.
284  std::set<Own<Aggregator>> ancestors;
285  // make sure you clone the agg first.
286  Own<Aggregator> targetAggregate;
287 
288  const std::set<std::string>& getAggregatorVariables() {
289  return aggregatorVariables;
290  }
291 
292  ReplaceAggregatesWithVariables(std::set<Own<Aggregator>> ancestors, Own<Aggregator> targetAggregate)
293  : ancestors(std::move(ancestors)), targetAggregate(std::move(targetAggregate)) {}
294 
295  std::unique_ptr<Node> operator()(std::unique_ptr<Node> node) const override {
296  static int numReplaced = 0;
297  if (auto* aggregate = dynamic_cast<Aggregator*>(node.get())) {
298  // If we come across an aggregate that is NOT an ancestor of
299  // the target aggregate, or that IS itself the target aggregate,
300  // we should replace it with a dummy variable.
301  bool isAncestor = false;
302  for (auto& ancestor : ancestors) {
303  if (*ancestor == *aggregate) {
304  isAncestor = true;
305  break;
306  }
307  }
308  if (!isAncestor || *aggregate == *targetAggregate) {
309  // Replace the aggregator with a variable
310  std::stringstream newVariableName;
311  newVariableName << "+aggr_var_" << numReplaced++;
312  // Keep track of which variables are bound to aggregators
313  // (only applicable to non-ancestral aggregates)
314  if (!isAncestor) {
315  // we don't want to ground the target aggregate
316  // (why?) because these variables could be injected
317  // and that is fine.
318  aggregatorVariables.insert(newVariableName.str());
319  // but we are not supposed to be judging the aggregate as grounded and injected into
320  // the aggregate, that wouldn't make sense
321  }
322  return mk<Variable>(newVariableName.str());
323  }
324  }
325  node->apply(*this);
326  return node;
327  }
328  };
329  // 2. make a clone of the clause and then apply that mapper onto it
330  auto clauseCopy = souffle::clone(&clause);
331  auto tweakedClause = mk<Clause>();
332  // put a fake head here
333  tweakedClause->setHead(mk<Atom>("*"));
334  // copy in all the old body literals
335  for (Literal* lit : clause.getBodyLiterals()) {
336  tweakedClause->addToBody(souffle::clone(lit));
337  }
338  // copy in the head as a negated atom
339  tweakedClause->addToBody(mk<Negation>(souffle::clone(clause.getHead())));
340  // copy in body literals and also add the old head as a negated atom
341  ReplaceAggregatesWithVariables update(std::move(ancestorAggregates), souffle::clone(&aggregate));
342  tweakedClause->apply(update);
343  // the update will now tell us which variables we need to ground!
344  auto groundingAtom = mk<Atom>("+grounding_atom");
345  for (std::string variableName : update.getAggregatorVariables()) {
346  groundingAtom->addArgument(mk<Variable>(variableName));
347  }
348  // add the newly created grounding atom to the body
349  tweakedClause->addToBody(std::move(groundingAtom));
350 
351  std::set<std::string> injectedVariables;
352  // Search through the tweakedClause to find groundings!
353  for (const auto& argPair : analysis::getGroundedTerms(tu, *tweakedClause)) {
354  if (const auto* variable = dynamic_cast<const Variable*>(argPair.first)) {
355  bool varIsGrounded = argPair.second;
356  if (varIsGrounded && variablesInTargetAggregate.find(variable->getName()) !=
357  variablesInTargetAggregate.end()) {
358  // then we have found an injected variable, lovely!!
359  injectedVariables.insert(variable->getName());
360  }
361  }
362  }
363  // Remove any variables that occur in the target expression of the aggregate
364  if (aggregate.getTargetExpression() != nullptr) {
365  visitDepthFirst(*aggregate.getTargetExpression(),
366  [&](const Variable& v) { injectedVariables.erase(v.getName()); });
367  }
368 
369  return injectedVariables;
370 }
371 
372 } // namespace souffle::ast::analysis

◆ getLocalVariables()

std::set< std::string > souffle::ast::analysis::getLocalVariables ( const TranslationUnit tu,
const Clause clause,
const Aggregator aggregate 
)

Computes the set of local variables in an aggregate expression.

This is just the set of all variables occurring in the aggregate MINUS the injected variables MINUS the witness variables. :)

Definition at line 55 of file Aggregate.cpp.

56  : allVariablesInAggregate) {
57  if (injectedVariables.find(name) == injectedVariables.end() &&
58  witnessVariables.find(name) == witnessVariables.end()) {
59  localVariables.insert(name);
60  }
61  }
62  return localVariables;
63 }
64 /**
65  * Computes the set of witness variables that are used in the aggregate
66  * A variable is a witness if it occurs in the aggregate body (but not in an inner aggregate)
67  * and also occurs ungrounded in the outer scope.
68  **/
69 std::set<std::string> getWitnessVariables(

◆ getTypeAttribute() [1/2]

TypeAttribute souffle::ast::analysis::getTypeAttribute ( const Type type)

Definition at line 353 of file TypeSystem.cpp.

357  {
358  for (auto typeAttribute : {TypeAttribute::Signed, TypeAttribute::Unsigned, TypeAttribute::Float,
359  TypeAttribute::Record, TypeAttribute::Symbol}) {
360  if (isOfKind(type, typeAttribute)) {
361  return typeAttribute;

◆ getTypeAttribute() [2/2]

std::optional< TypeAttribute > souffle::ast::analysis::getTypeAttribute ( const TypeSet type)

Definition at line 363 of file TypeSystem.cpp.

364  {};
365 }
366 
367 bool areEquivalentTypes(const Type& a, const Type& b) {
368  return isSubtypeOf(a, b) && isSubtypeOf(b, a);
369 }
370 
371 bool isADTEnum(const AlgebraicDataType& type) {

◆ getTypeQualifier()

std::string souffle::ast::analysis::getTypeQualifier ( const Type type)

Returns full type qualifier for a given type.

Definition at line 204 of file TypeSystem.cpp.

204  {
205  return "f";
206  } else if (isOfKind(type, TypeAttribute::Symbol)) {
207  return "s";
208  } else if (isOfKind(type, TypeAttribute::Record)) {
209  return "r";
210  } else if (isOfKind(type, TypeAttribute::ADT)) {
211  return "+";
212  } else {
213  fatal("Unsupported kind");
214  }
215  }();
216 
217  return tfm::format("%s:%s", kind, type.getName());
218 }
219 
220 bool isSubtypeOf(const Type& a, const Type& b) {
221  assert(&a.getTypeEnvironment() == &b.getTypeEnvironment() &&
222  "Types must be in the same type environment");
223 
224  if (isOfRootType(a, b)) {

Referenced by souffle::ast::transform::IOAttributesTransformer::getRecordsTypes().

◆ getVariablesOutsideAggregate()

std::set< std::string > souffle::ast::analysis::getVariablesOutsideAggregate ( const Clause clause,
const Aggregator aggregate 
)

Computes the set of variables occurring outside the aggregate.

Find the set of variable names occurring outside of the given aggregate.

This is equivalent to taking the set minus of the set of all variable names occurring in the clause minus the set of all variable names occurring in the aggregate.

Definition at line 164 of file Aggregate.cpp.

166  {
167  variablesOutsideAggregate.insert(v);
168  }
169  }
170  return variablesOutsideAggregate;
171 }
172 
173 std::string findUniqueVariableName(const Clause& clause, std::string base) {
174  std::set<std::string> variablesInClause;
175  visitDepthFirst(clause, [&](const Variable& v) { variablesInClause.insert(v.getName()); });
176  int varNum = 0;
177  std::string candidate = base;

Referenced by souffle::ast::transform::SimplifyAggregateTargetExpressionTransformer::transform().

◆ getWitnessVariables()

std::set< std::string > souffle::ast::analysis::getWitnessVariables ( const TranslationUnit tu,
const Clause clause,
const Aggregator aggregate 
)

Computes the set of witness variables that are used in the aggregate A variable is a witness if it occurs in the aggregate body (but not in an inner aggregate) and also occurs ungrounded in the outer scope.

Computes the set of witness variables that are used in the aggregate.

Definition at line 75 of file Aggregate.cpp.

78  {
79  return aggregatorVariables;
80  }
81 
82  std::unique_ptr<Node> operator()(std::unique_ptr<Node> node) const override {
83  static int numReplaced = 0;
84  if (dynamic_cast<Aggregator*>(node.get()) != nullptr) {
85  // Replace the aggregator with a variable
86  std::stringstream newVariableName;
87  newVariableName << "+aggr_var_" << numReplaced++;
88 
89  // Keep track of which variables are bound to aggregators
90  aggregatorVariables.insert(newVariableName.str());
91 
92  return std::make_unique<Variable>(newVariableName.str());
93  }
94  node->apply(*this);
95  return node;
96  }
97  };
98 
99  auto aggregatorlessClause = std::make_unique<Clause>();
100  aggregatorlessClause->setHead(std::make_unique<Atom>("*"));
101  for (Literal* lit : clause.getBodyLiterals()) {
102  aggregatorlessClause->addToBody(souffle::clone(lit));
103  }
104 
105  auto negatedHead = std::make_unique<Negation>(souffle::clone(clause.getHead()));
106  aggregatorlessClause->addToBody(std::move(negatedHead));
107 
108  // Replace all aggregates with variables
109  M update;
110  aggregatorlessClause->apply(update);
111  auto groundingAtom = std::make_unique<Atom>("+grounding_atom");
112  for (std::string variableName : update.getAggregatorVariables()) {
113  groundingAtom->addArgument(std::make_unique<Variable>(variableName));
114  }
115  aggregatorlessClause->addToBody(std::move(groundingAtom));
116  // 2. Create an aggregate clause so that we can check
117  // that it IS this aggregate giving a grounding to the candidate variable.
118  auto aggregateSubclause = std::make_unique<Clause>();
119  aggregateSubclause->setHead(mk<Atom>("*"));
120  for (const auto& lit : aggregate.getBodyLiterals()) {
121  aggregateSubclause->addToBody(souffle::clone(lit));
122  }
123 
124  std::set<std::string> witnessVariables;
125  auto isGroundedInAggregateSubclause = analysis::getGroundedTerms(tu, *aggregateSubclause);
126  // 3. Calculate all the witness variables
127  // A witness will occur ungrounded in the aggregatorlessClause
128  for (const auto& argPair : analysis::getGroundedTerms(tu, *aggregatorlessClause)) {
129  if (const auto* variable = dynamic_cast<const Variable*>(argPair.first)) {
130  bool variableIsGrounded = argPair.second;
131  if (!variableIsGrounded) {
132  // then we expect it to be grounded in the aggregate subclause
133  // if it's a witness!!
134  for (const auto& aggArgPair : isGroundedInAggregateSubclause) {
135  if (const auto* var = dynamic_cast<const Variable*>(aggArgPair.first)) {
136  bool aggVariableIsGrounded = aggArgPair.second;
137  if (var->getName() == variable->getName() && aggVariableIsGrounded) {
138  witnessVariables.insert(variable->getName());
139  }
140  }
141  }
142  }
143  }
144  }
145  // 4. A witness variable may actually "originate" from an outer scope and
146  // just have been injected into this inner aggregate. Just check the set of injected variables
147  // and quickly minus them out.
148  std::set<std::string> injectedVariables = analysis::getInjectedVariables(tu, clause, aggregate);
149  for (const std::string& injected : injectedVariables) {
150  witnessVariables.erase(injected);
151  }
152 
153  return witnessVariables;
154 }
155 /**
156  * Computes the set of variables occurring outside the aggregate
157  **/
158 std::set<std::string> getVariablesOutsideAggregate(const Clause& clause, const Aggregator& aggregate) {
159  std::map<std::string, int> variableOccurrences;
160  visitDepthFirst(clause, [&](const Variable& var) { variableOccurrences[var.getName()]++; });

Referenced by souffle::ast::transform::SimplifyAggregateTargetExpressionTransformer::transform().

◆ hasSuperTypeInSet()

static TypeConstraint souffle::ast::analysis::hasSuperTypeInSet ( const TypeVar var,
TypeSet  values 
)
static

A constraint factory ensuring that all the types associated to the variable are subtypes of some type in the provided set (values)

Values can't be all.

Definition at line 77 of file TypeConstraints.cpp.

78  : var(std::move(var)), values(std::move(values)) {}
79 
80  bool update(Assignment<TypeVar>& assigment) const override {
81  // get current value of variable a
82  TypeSet& assigments = assigment[var];
83 
84  // remove all types that are not sub-types of b
85  if (assigments.isAll()) {
86  assigments = values;
87  return true;
88  }
89 
90  TypeSet newAssigments;
91  for (const Type& type : assigments) {
92  bool existsSuperTypeInValues =
93  any_of(values, [&type](const Type& value) { return isSubtypeOf(type, value); });
94  if (existsSuperTypeInValues) {
95  newAssigments.insert(type);
96  }
97  }
98  // check whether there was a change
99  if (newAssigments == assigments) {
100  return false;
101  }
102  assigments = newAssigments;
103  return true;
104  }
105 
106  void print(std::ostream& out) const override {
107  out << "∃ t ∈ " << values << ": " << var << " <: t";
108  }
109  };
110 
111  return std::make_shared<C>(var, values);
112 }
113 
114 static const Type& getBaseType(const Type* type) {
115  while (auto subset = dynamic_cast<const SubsetType*>(type)) {
116  type = &subset->getBaseType();

◆ haveCommonSupertype()

bool souffle::ast::analysis::haveCommonSupertype ( const Type a,
const Type b 
)

Determine if there exist a type t such that a <: t and b <: t.

Definition at line 337 of file TypeSystem.cpp.

339  {
340  return true;
341  }
342 
343  return any_of(a.getTypeEnvironment().getTypes(),
344  [&](const Type& type) { return isSubtypeOf(a, type) && isSubtypeOf(b, type); });
345 }
346 
347 TypeAttribute getTypeAttribute(const Type& type) {
348  for (auto typeAttribute : {TypeAttribute::Signed, TypeAttribute::Unsigned, TypeAttribute::Float,
349  TypeAttribute::Record, TypeAttribute::Symbol}) {
350  if (isOfKind(type, typeAttribute)) {
351  return typeAttribute;

◆ isADTEnum()

bool souffle::ast::analysis::isADTEnum ( const AlgebraicDataType type)

Determine if ADT is enumerations (are all constructors empty)

Definition at line 377 of file TypeSystem.cpp.

◆ isNumericType()

bool souffle::ast::analysis::isNumericType ( const TypeSet type)
inline

Definition at line 512 of file TypeSystem.h.

536  {

◆ isOfKind() [1/2]

bool souffle::ast::analysis::isOfKind ( const Type type,
TypeAttribute  kind 
)

Check if the type is of a kind corresponding to the TypeAttribute.

Definition at line 189 of file TypeSystem.cpp.

193  {
194  return !typeSet.empty() && !typeSet.isAll() &&
195  all_of(typeSet, [&](const Type& type) { return isOfKind(type, kind); });
196 }
197 

Referenced by isOfKind(), and souffle::ast::transform::TypeCheckerImpl::visitVariable().

◆ isOfKind() [2/2]

bool souffle::ast::analysis::isOfKind ( const TypeSet typeSet,
TypeAttribute  kind 
)

Definition at line 199 of file TypeSystem.cpp.

199  {
200  if (isOfKind(type, TypeAttribute::Signed)) {
201  return "i";
202  } else if (isOfKind(type, TypeAttribute::Unsigned)) {

References isOfKind(), souffle::Signed, and souffle::Unsigned.

Here is the call graph for this function:

◆ isOfRootType()

bool souffle::ast::analysis::isOfRootType ( const Type type,
const Type root 
)

Determines whether the given type is a sub-type of the given root type.

Definition at line 155 of file TypeSystem.cpp.

155  {
156  return type == root;
157  }
158  bool visitSubsetType(const SubsetType& type) const override {
159  return type == root || isOfRootType(type.getBaseType(), root);
160  }
161 
162  bool visitAlgebraicDataType(const AlgebraicDataType& type) const override {
163  return type == root;
164  }
165 
166  bool visitUnionType(const UnionType& type) const override {
167  return type == root ||
168  all_of(type.getElementTypes(), [&](const Type* cur) { return this->visit(*cur); });
169  }
170 
171  bool visitRecordType(const RecordType& type) const override {
172  return type == root;
173  }
174 
175  bool visitType(const Type& /*unused*/) const override {
176  return false;
177  }
178  };
179 
180  return visitor(root).visit(type);
181 }
182 
183 bool isOfKind(const Type& type, TypeAttribute kind) {
184  if (kind == TypeAttribute::Record) {
185  return isA<RecordType>(type);
186  } else if (kind == TypeAttribute::ADT) {
187  return isA<AlgebraicDataType>(type);

◆ isOrderableType()

bool souffle::ast::analysis::isOrderableType ( const TypeSet type)
inline

Definition at line 517 of file TypeSystem.h.

536  {

◆ isSubtypeOf() [1/3]

bool souffle::ast::analysis::isSubtypeOf ( const Type a,
const Type b 
)

Determines whether type a is a subtype of type b.

Definition at line 226 of file TypeSystem.cpp.

228  {
229  return all_of(static_cast<const UnionType&>(a).getElementTypes(),
230  [&b](const Type* type) { return isSubtypeOf(*type, b); });
231  }
232 
233  if (isA<UnionType>(b)) {
234  return any_of(static_cast<const UnionType&>(b).getElementTypes(),
235  [&a](const Type* type) { return isSubtypeOf(a, *type); });
236  }
237 
238  return false;
239 }
240 
241 void TypeEnvironment::print(std::ostream& out) const {
242  out << "Types:\n";
243  for (const auto& cur : types) {
244  out << "\t" << *cur.second << "\n";
245  }

◆ isSubtypeOf() [2/3]

static TypeConstraint souffle::ast::analysis::isSubtypeOf ( const TypeVar a,
const TypeVar b 
)
static

A constraint factory ensuring that all the types associated to the variable a are subtypes of the variable b.

Definition at line 27 of file TypeConstraints.cpp.

31  {

Referenced by souffle::ast::analysis::TypeEnvironment::print(), and souffle::ast::analysis::TypeConstraintsAnalysis::visitNegation().

◆ isSubtypeOf() [3/3]

static TypeConstraint souffle::ast::analysis::isSubtypeOf ( const TypeVar variable,
const Type type 
)
static

A constraint factory ensuring that all the types associated to the variable a are subtypes of type b.

Definition at line 35 of file TypeConstraints.cpp.

36  : variable(std::move(variable)), type(type) {}
37 
38  bool update(Assignment<TypeVar>& assignments) const override {
39  TypeSet& assignment = assignments[variable];
40 
41  if (assignment.isAll()) {
42  assignment = TypeSet(type);
43  return true;
44  }
45 
46  TypeSet newAssignment;
47  for (const Type& t : assignment) {
48  newAssignment.insert(getGreatestCommonSubtypes(t, type));
49  }
50 
51  // check whether there was a change
52  if (assignment == newAssignment) {
53  return false;
54  }
55  assignment = newAssignment;
56  return true;
57  }
58 
59  void print(std::ostream& out) const override {
60  out << variable << " <: " << type.getName();
61  }
62  };
63 
64  return std::make_shared<C>(variable, type);
65 }
66 
67 /**
68  * A constraint factory ensuring that all the types associated to the variable
69  * are subtypes of some type in the provided set (values)

◆ isSubtypeOfComponent()

static TypeConstraint souffle::ast::analysis::isSubtypeOfComponent ( const TypeVar elementVariable,
const TypeVar recordVariable,
size_t  index 
)
static

Constraint on record type and its elements.

Definition at line 320 of file TypeConstraints.cpp.

324  : elementVariable(std::move(elementVariable)), recordVariable(std::move(recordVariable)),
325  index(index) {}
326 
327  bool update(Assignment<TypeVar>& assignment) const override {
328  // get list of types for b
329  const TypeSet& recordTypes = assignment[recordVariable];
330 
331  // if it is (not yet) constrainted => skip
332  if (recordTypes.isAll()) {
333  return false;
334  }
335 
336  // compute new types for element and record
337  TypeSet newElementTypes;
338  TypeSet newRecordTypes;
339 
340  for (const Type& type : recordTypes) {
341  // A type must be either a record type or a subset of a record type
342  if (!isOfKind(type, TypeAttribute::Record)) {
343  continue;
344  }
345 
346  const auto& typeAsRecord = *as<RecordType>(type);
347 
348  // Wrong size => skip.
349  if (typeAsRecord.getFields().size() <= index) {
350  continue;
351  }
352 
353  // Valid type for record.
354  newRecordTypes.insert(type);
355 
356  // and its corresponding field.
357  newElementTypes.insert(*typeAsRecord.getFields()[index]);
358  }
359 
360  // combine with current types assigned to element
361  newElementTypes = getGreatestCommonSubtypes(assignment[elementVariable], newElementTypes);
362 
363  // update values
364  bool changed = false;
365  if (newRecordTypes != recordTypes) {
366  assignment[recordVariable] = newRecordTypes;
367  changed = true;
368  }
369 
370  if (assignment[elementVariable] != newElementTypes) {
371  assignment[elementVariable] = newElementTypes;
372  changed = true;
373  }
374 
375  return changed;
376  }
377 
378  void print(std::ostream& out) const override {
379  out << elementVariable << " <: " << recordVariable << "::" << index;
380  }
381  };
382 
383  return std::make_shared<C>(elementVariable, recordVariable, index);
384 }
385 
386 void TypeConstraintsAnalysis::visitSink(const Atom& atom) {
387  iterateOverAtom(atom, [&](const Argument& argument, const Type& attributeType) {
388  if (isA<RecordType>(attributeType)) {

◆ satisfiesOverload()

static TypeConstraint souffle::ast::analysis::satisfiesOverload ( const TypeEnvironment typeEnv,
IntrinsicFunctors  overloads,
TypeVar  result,
std::vector< TypeVar args,
bool  subtypeResult 
)
static

Given a set of overloads, wait the list of candidates to reduce to one and then apply its constraints.

NOTE: subtypeResult implies that func <: overload-return-type, rather than func = overload-return-type. This is required for old type semantics. See #1296 and tests/semantic/type_system4

Definition at line 230 of file TypeConstraints.cpp.

240  : typeEnv(typeEnv), overloads(std::move(overloads)), result(std::move(result)),
241  args(std::move(args)), subtypeResult(subtypeResult) {}
242 
243  bool update(Assignment<TypeVar>& assigment) const override {
244  auto subtypesOf = [&](const TypeSet& src, TypeAttribute tyAttr) {
245  auto& ty = typeEnv.getConstantType(tyAttr);
246  return src.filter(TypeSet(true), [&](auto&& x) { return isSubtypeOf(x, ty); });
247  };
248 
249  auto possible = [&](TypeAttribute ty, const TypeVar& var) {
250  auto& curr = assigment[var];
251  return curr.isAll() || any_of(curr, [&](auto&& t) { return getTypeAttribute(t) == ty; });
252  };
253 
254  overloads = filterNot(std::move(overloads), [&](const IntrinsicFunctorInfo& x) -> bool {
255  if (!x.variadic && args.size() != x.params.size()) return true; // arity mismatch?
256 
257  for (size_t i = 0; i < args.size(); ++i) {
258  if (!possible(x.params[x.variadic ? 0 : i], args[i])) return true;
259  }
260 
261  return !possible(x.result, result);
262  });
263 
264  bool changed = false;
265  auto newResult = [&]() -> std::optional<TypeSet> {
266  if (0 == overloads.size()) return TypeSet();
267  if (1 < overloads.size()) return {};
268 
269  auto& overload = overloads.front().get();
270  // `ord` is freakin' magical: it has the signature `a -> Int`.
271  // As a consequence, we might be given non-primitive arguments (i.e. types for which
272  // `TypeEnv::getConstantType` is undefined).
273  // Handle this by not imposing constraints on the arguments.
274  if (overload.op != FunctorOp::ORD) {
275  for (size_t i = 0; i < args.size(); ++i) {
276  auto argTy = overload.params[overload.variadic ? 0 : i];
277  auto& currArg = assigment[args[i]];
278  auto newArg = subtypesOf(currArg, argTy);
279  changed |= currArg != newArg;
280  // 2020-05-09: CI linter says to remove `std::move`, but clang-tidy-10 is happy.
281  currArg = std::move(newArg); // NOLINT
282  }
283  }
284 
285  if (nonMonotonicUpdate || subtypeResult) {
286  return subtypesOf(assigment[result], overload.result);
287  } else {
288  nonMonotonicUpdate = true;
289  return TypeSet{typeEnv.getConstantType(overload.result)};
290  }
291  }();
292 
293  if (newResult) {
294  auto& curr = assigment[result];
295  changed |= curr != *newResult;
296  // 2020-05-09: CI linter says to remove `std::move`, but clang-tidy-10 is happy.
297  curr = std::move(*newResult); // NOLINT
298  }
299 
300  return changed;
301  }
302 
303  void print(std::ostream& out) const override {
304  // TODO (darth_tytus): is this description correct?
305  out << "∃ t : " << result << " <: t where t is a base type";
306  }
307  };
308 
309  return std::make_shared<C>(
310  typeEnv, std::move(overloads), std::move(result), std::move(args), subtypeResult);
311 }
312 
313 /**
314  * Constraint on record type and its elements.
315  */

Referenced by souffle::ast::analysis::TypeConstraintsAnalysis::visitFunctor().

◆ sub() [1/2]

template<typename Var , typename Val = typename Var::property_space::value_type>
std::shared_ptr<Constraint<Var> > souffle::ast::analysis::sub ( const Val &  a,
const Var &  b,
const std::string &  symbol = "⊑" 
)

A generic factory for constraints of the form.

                           a ⊑ b

where b is a variables, a is a value of b's property space, and ⊑ is the order relation induced by b's property space.

Definition at line 257 of file ConstraintSystem.h.

257  : a(std::move(a)), b(std::move(b)), symbol(std::move(symbol)) {}
258 
259  bool update(Assignment<Var>& ass) const override {
260  typename Var::property_space::meet_assign_op_type meet_assign;
261  return meet_assign(ass[b], a);
262  }
263 
264  void print(std::ostream& out) const override {
265  out << a << " " << symbol << " " << b;
266  }
267  };
268 
269  return std::make_shared<Sub>(a, b, symbol);
270 }
271 
272 //----------------------------------------------------------------------
273 // assignment
274 //----------------------------------------------------------------------
275 
276 /**

◆ sub() [2/2]

template<typename Var >
std::shared_ptr<Constraint<Var> > souffle::ast::analysis::sub ( const Var &  a,
const Var &  b,
const std::string &  symbol = "⊑" 
)

A generic factory for constraints of the form.

                           a ⊑ b

where a and b are variables and ⊑ is the order relation induced by their associated property space.

Definition at line 228 of file ConstraintSystem.h.

229  {
230  typename Var::property_space::meet_assign_op_type meet_assign;
231  return meet_assign(ass[b], ass[a]);
232  }
233 
234  void print(std::ostream& out) const override {
235  out << a << " " << symbol << " " << b;
236  }
237  };
238 
239  return std::make_shared<Sub>(a, b, symbol);
240 }
241 
242 /**
243  * A generic factory for constraints of the form
244  *
245  * a ⊑ b
246  *

References b.

Referenced by souffle::ram::Program::clone(), souffle::ram::Program::getChildNodes(), souffle::ram::Program::getRelations(), souffle::ram::Program::print(), souffle::ram::Program::Program(), and souffle::ast::transform::reduceSubstitution().

◆ subtypesOfTheSameBaseType()

static TypeConstraint souffle::ast::analysis::subtypesOfTheSameBaseType ( const TypeVar left,
const TypeVar right 
)
static

Ensure that types of left and right have the same base types.

Definition at line 130 of file TypeConstraints.cpp.

131  : left(std::move(left)), right(std::move(right)) {}
132 
133  bool update(Assignment<TypeVar>& assigment) const override {
134  // get current value of variable a
135  TypeSet& assigmentsLeft = assigment[left];
136  TypeSet& assigmentsRight = assigment[right];
137 
138  // Base types common to left and right variables.
139  TypeSet baseTypes;
140 
141  // Base types present in left/right variable.
142  TypeSet baseTypesLeft;
143  TypeSet baseTypesRight;
144 
145  // Iterate over possible types extracting base types.
146  // Left
147  if (!assigmentsLeft.isAll()) {
148  for (const Type& type : assigmentsLeft) {
149  if (isA<SubsetType>(type) || isA<ConstantType>(type)) {
150  baseTypesLeft.insert(getBaseType(&type));
151  }
152  }
153  }
154  // Right
155  if (!assigmentsRight.isAll()) {
156  for (const Type& type : assigmentsRight) {
157  if (isA<SubsetType>(type) || isA<ConstantType>(type)) {
158  baseTypesRight.insert(getBaseType(&type));
159  }
160  }
161  }
162 
163  TypeSet resultLeft;
164  TypeSet resultRight;
165 
166  // Handle all
167  if (assigmentsLeft.isAll() && assigmentsRight.isAll()) {
168  return false;
169  }
170 
171  // If left xor right is all, assign base types of the other side as possible values.
172  if (assigmentsLeft.isAll()) {
173  assigmentsLeft = baseTypesRight;
174  return true;
175  }
176  if (assigmentsRight.isAll()) {
177  assigmentsRight = baseTypesLeft;
178  return true;
179  }
180 
181  baseTypes = TypeSet::intersection(baseTypesLeft, baseTypesRight);
182 
183  // Allow types if they are subtypes of any of the common base types.
184  for (const Type& type : assigmentsLeft) {
185  bool isSubtypeOfCommonBaseType = any_of(baseTypes.begin(), baseTypes.end(),
186  [&type](const Type& baseType) { return isSubtypeOf(type, baseType); });
187  if (isSubtypeOfCommonBaseType) {
188  resultLeft.insert(type);
189  }
190  }
191 
192  for (const Type& type : assigmentsRight) {
193  bool isSubtypeOfCommonBaseType = any_of(baseTypes.begin(), baseTypes.end(),
194  [&type](const Type& baseType) { return isSubtypeOf(type, baseType); });
195  if (isSubtypeOfCommonBaseType) {
196  resultRight.insert(type);
197  }
198  }
199 
200  // check whether there was a change
201  if (resultLeft == assigmentsLeft && resultRight == assigmentsRight) {
202  return false;
203  }
204  assigmentsLeft = resultLeft;
205  assigmentsRight = resultRight;
206  return true;
207  }
208  //
209  void print(std::ostream& out) const override {
210  out << "∃ t : (" << left << " <: t)"
211  << " ∧ "
212  << "(" << right << " <: t)"
213  << " where t is a base type";
214  }
215  };
216 
217  return std::make_shared<C>(left, right);
218 }
219 
220 /**
221  * Given a set of overloads, wait the list of candidates to reduce to one and then apply its constraints.
222  * NOTE: `subtypeResult` implies that `func <: overload-return-type`, rather than

Referenced by souffle::ast::analysis::TypeConstraintsAnalysis::visitFunctor().

souffle::ast::analysis::isOfKind
bool isOfKind(const TypeSet &typeSet, TypeAttribute kind)
Definition: TypeSystem.cpp:199
souffle::ast::analysis::isOfKind
bool isOfKind(const Type &type, TypeAttribute kind)
Check if the type is of a kind corresponding to the TypeAttribute.
Definition: TypeSystem.cpp:189
TypeAttribute
Type attribute class.
souffle::filterNot
std::vector< A > filterNot(std::vector< A > xs, F &&f)
Filter a vector to exclude certain elements.
Definition: FunctionalUtil.h:146
souffle::ast::analysis::areEquivalentTypes
bool areEquivalentTypes(const Type &a, const Type &b)
Determine if two types are equivalent.
Definition: TypeSystem.cpp:373
tinyformat::format
void format(std::ostream &out, const char *fmt)
Definition: tinyformat.h:1089
souffle::ast::analysis::isADTEnum
bool isADTEnum(const AlgebraicDataType &type)
Determine if ADT is enumerations (are all constructors empty)
Definition: TypeSystem.cpp:377
types
std::vector< Own< ast::Type > > types
Definition: ComponentInstantiation.cpp:64
souffle::ast::analysis::getBaseType
static const Type & getBaseType(const Type *type)
Definition: TypeConstraints.cpp:118
base
T & base
Definition: Reader.h:60
souffle::ast::analysis::haveCommonSupertype
bool haveCommonSupertype(const Type &a, const Type &b)
Determine if there exist a type t such that a <: t and b <: t.
Definition: TypeSystem.cpp:337
souffle::ast::analysis::getGroundedTerms
std::map< const Argument *, bool > getGroundedTerms(const TranslationUnit &tu, const Clause &clause)
Analyse the given clause and computes for each contained argument whether it is a grounded value or n...
Definition: Ground.cpp:278
souffle::ast::analysis::getGreatestCommonSubtypes
TypeSet getGreatestCommonSubtypes(const Type &a, const Type &b)
Computes the greatest common sub types of the two given types.
Definition: TypeSystem.cpp:254
souffle::toString
const std::string & toString(const std::string &str)
A generic function converting strings into strings (trivial case).
Definition: StringUtil.h:234
l
var l
Definition: htmlJsChartistMin.h:15
souffle::clone
auto clone(const std::vector< A * > &xs)
Definition: ContainerUtil.h:172
i
size_t i
Definition: json11.h:663
souffle::ast::analysis::isOfRootType
bool isOfRootType(const Type &type, const Type &root)
Determines whether the given type is a sub-type of the given root type.
Definition: TypeSystem.cpp:155
souffle::ast::getRelation
Relation * getRelation(const Program &program, const QualifiedName &name)
Returns the relation with the given name in the program.
Definition: Utils.cpp:101
souffle::ast::analysis::getVariablesOutsideAggregate
std::set< std::string > getVariablesOutsideAggregate(const Clause &clause, const Aggregator &aggregate)
Computes the set of variables occurring outside the aggregate.
Definition: Aggregate.cpp:164
souffle::ast::analysis::getTypeAttribute
TypeAttribute getTypeAttribute(const Type &type)
Definition: TypeSystem.cpp:353
souffle::ast::analysis::isSubtypeOf
bool isSubtypeOf(const Type &a, const Type &b)
Determines whether type a is a subtype of type b.
Definition: TypeSystem.cpp:226
souffle::any_of
bool any_of(const Container &c, UnaryPredicate p)
A generic test checking whether any elements within a container satisfy a certain predicate.
Definition: FunctionalUtil.h:124
souffle::ast::analysis::getGreatestCommonSubtypes
TypeSet getGreatestCommonSubtypes(const TypeSet &a, const TypeSet &b)
The set of pair-wise greatest common sub types of the types in the two given sets.
Definition: TypeSystem.cpp:311
souffle::which
std::string which(const std::string &name)
Simple implementation of a which tool.
Definition: FileUtil.h:104
souffle::all_of
bool all_of(const Container &c, UnaryPredicate p)
A generic test checking whether all elements within a container satisfy a certain predicate.
Definition: FunctionalUtil.h:110
b
l j a showGridBackground &&c b raw series this eventEmitter b
Definition: htmlJsChartistMin.h:15
std
Definition: Brie.h:3053
souffle::ast::analysis::findUniqueVariableName
std::string findUniqueVariableName(const Clause &clause, std::string base)
Find a variable name using base to form a string like base1 Use this when you need to limit the scope...
Definition: Aggregate.cpp:179
souffle::ast::analysis::getTypeAttribute
std::optional< TypeAttribute > getTypeAttribute(const TypeSet &type)
Definition: TypeSystem.cpp:363
souffle::fatal
void fatal(const char *format, const Args &... args)
Definition: MiscUtil.h:198
souffle::ast::visitDepthFirst
void visitDepthFirst(const Node &root, Visitor< R, Ps... > &visitor, Args &... args)
A utility function visiting all nodes within the ast rooted by the given node recursively in a depth-...
Definition: Visitor.h:273
souffle::ast::analysis::isSubtypeOf
static TypeConstraint isSubtypeOf(const TypeVar &variable, const Type &type)
A constraint factory ensuring that all the types associated to the variable a are subtypes of type b.
Definition: TypeConstraints.cpp:35
souffle::ast::analysis::TypeVar
ConstraintAnalysisVar< type_lattice > TypeVar
The definition of the type of variable to be utilized in the type analysis.
Definition: TypeConstraints.h:72
souffle::ast::analysis::getWitnessVariables
std::set< std::string > getWitnessVariables(const TranslationUnit &tu, const Clause &clause, const Aggregator &aggregate)
Computes the set of witness variables that are used in the aggregate A variable is a witness if it oc...
Definition: Aggregate.cpp:75
std::type
ElementType type
Definition: span.h:640
souffle::ast::analysis::getInjectedVariables
std::set< std::string > getInjectedVariables(const TranslationUnit &tu, const Clause &clause, const Aggregator &aggregate)
Given an aggregate and a clause, we find all the variables that have been injected into the aggregate...
Definition: Aggregate.cpp:205