souffle  2.0.2-371-g6315b36
TypeEnvironment.cpp
Go to the documentation of this file.
1 /*
2  * Souffle - A Datalog Compiler
3  * Copyright (c) 2019, The Souffle Developers. All rights reserved.
4  * Licensed under the Universal Permissive License v 1.0 as shown at:
5  * - https://opensource.org/licenses/UPL
6  * - <souffle root>/licenses/SOUFFLE-UPL.txt
7  */
8 
9 /************************************************************************
10  *
11  * @file TypeEnvironment.cpp
12  *
13  * Implements AST Analysis methods for a Type Environment
14  *
15  ***********************************************************************/
16 
18 #include "GraphUtils.h"
19 #include "ast/AlgebraicDataType.h"
20 #include "ast/Attribute.h"
21 #include "ast/BranchDeclaration.h"
22 #include "ast/Program.h"
23 #include "ast/RecordType.h"
24 #include "ast/SubsetType.h"
25 #include "ast/TranslationUnit.h"
26 #include "ast/Type.h"
27 #include "ast/UnionType.h"
31 #include <algorithm>
32 #include <functional>
33 #include <ostream>
34 #include <typeinfo>
35 #include <utility>
36 #include <vector>
37 
38 namespace souffle::ast::analysis {
39 
40 namespace {
41 
42 Graph<QualifiedName> createTypeDependencyGraph(const std::vector<ast::Type*>& programTypes) {
43  Graph<QualifiedName> typeDependencyGraph;
44  for (const auto* astType : programTypes) {
45  if (auto type = as<ast::SubsetType>(astType)) {
46  typeDependencyGraph.insert(type->getQualifiedName(), type->getBaseType());
47  } else if (isA<ast::RecordType>(astType)) {
48  // do nothing
49  } else if (auto type = as<ast::UnionType>(astType)) {
50  for (const auto& subtype : type->getTypes()) {
51  typeDependencyGraph.insert(type->getQualifiedName(), subtype);
52  }
53  } else if (isA<ast::AlgebraicDataType>(astType)) {
54  // do nothing
55  } else {
56  fatal("unsupported type construct: %s", typeid(astType).name());
57  }
58  }
59  return typeDependencyGraph;
60 }
61 
62 /**
63  * Find all the type with a cyclic definition (in terms of being a subtype)
64  */
65 std::set<QualifiedName> analyseCyclicTypes(
66  const Graph<QualifiedName>& dependencyGraph, const std::vector<ast::Type*>& programTypes) {
67  std::set<QualifiedName> cyclicTypes;
68  for (const auto& astType : programTypes) {
69  QualifiedName typeName = astType->getQualifiedName();
70 
71  if (dependencyGraph.reaches(typeName, typeName)) {
72  cyclicTypes.insert(std::move(typeName));
73  }
74  }
75  return cyclicTypes;
76 }
77 
78 /**
79  * Find all the primitive types that are the subtypes of the union types.
80  */
81 std::map<QualifiedName, std::set<QualifiedName>> analysePrimitiveTypesInUnion(
82  const Graph<QualifiedName>& dependencyGraph, const std::vector<ast::Type*>& programTypes,
83  const TypeEnvironment& env) {
84  std::map<QualifiedName, std::set<QualifiedName>> primitiveTypesInUnions;
85 
86  for (const auto& astType : programTypes) {
87  auto* unionType = as<ast::UnionType>(astType);
88  if (unionType == nullptr) {
89  continue;
90  }
91  QualifiedName unionName = unionType->getQualifiedName();
92 
93  auto iteratorToUnion = primitiveTypesInUnions.find(unionName);
94 
95  // Initialize with the empty set
96  if (iteratorToUnion == primitiveTypesInUnions.end()) {
97  iteratorToUnion = primitiveTypesInUnions.insert({unionName, {}}).first;
98  }
99 
100  auto& associatedTypes = iteratorToUnion->second;
101 
102  // Insert any reachable primitive type
103  for (auto& type : env.getPrimitiveTypes()) {
104  if (dependencyGraph.reaches(unionName, type.getName())) {
105  associatedTypes.insert(type.getName());
106  }
107  }
108  }
109  return primitiveTypesInUnions;
110 }
111 
112 } // namespace
113 
114 void TypeEnvironmentAnalysis::run(const TranslationUnit& translationUnit) {
115  const Program& program = translationUnit.getProgram();
116 
117  auto rawProgramTypes = program.getTypes();
118  Graph<QualifiedName> typeDependencyGraph{createTypeDependencyGraph(rawProgramTypes)};
119 
120  cyclicTypes = analyseCyclicTypes(typeDependencyGraph, rawProgramTypes);
121  primitiveTypesInUnions = analysePrimitiveTypesInUnion(typeDependencyGraph, rawProgramTypes, env);
122 
123  std::map<QualifiedName, const ast::Type*> nameToType;
124 
125  // Filter redefined primitive types and cyclic types.
126  std::vector<ast::Type*> programTypes;
127  for (auto* type : program.getTypes()) {
128  if (env.isType(type->getQualifiedName()) || isCyclic(type->getQualifiedName())) {
129  continue;
130  }
131  programTypes.push_back(type);
132  nameToType.insert({type->getQualifiedName(), type});
133  }
134 
135  for (const auto* astType : programTypes) {
136  createType(astType->getQualifiedName(), nameToType);
137  }
138 }
139 
140 // TODO (darth_tytus): This procedure does too much.
142  const QualifiedName& typeName, const std::map<QualifiedName, const ast::Type*>& nameToType) {
143  // base case
144  if (env.isType(typeName)) {
145  return &env.getType(typeName);
146  }
147 
148  // Handle undeclared type in the definition of another type.
149  auto iterToType = nameToType.find(typeName);
150  if (iterToType == nameToType.end()) {
151  return nullptr;
152  }
153  const auto& astType = iterToType->second;
154 
155  if (isA<ast::SubsetType>(astType)) {
156  // First create a base type.
157  auto* baseType = createType(as<ast::SubsetType>(astType)->getBaseType(), nameToType);
158 
159  if (baseType == nullptr) {
160  return nullptr;
161  }
162 
163  return &env.createType<SubsetType>(typeName, *baseType);
164 
165  } else if (isA<ast::UnionType>(astType)) {
166  // Create all elements and then the type itself
167  std::vector<const Type*> elements;
168  for (const auto& element : as<ast::UnionType>(astType)->getTypes()) {
169  auto* elementType = createType(element, nameToType);
170  if (elementType == nullptr) {
171  return nullptr;
172  }
173  elements.push_back(elementType);
174  }
175  return &env.createType<UnionType>(typeName, elements);
176 
177  } else if (auto astRecordType = as<ast::RecordType>(astType)) {
178  // Create the corresponding type first, since it could be recursive.
179  auto& recordType = env.createType<RecordType>(typeName);
180 
181  std::vector<const Type*> elements;
182  for (const auto* field : astRecordType->getFields()) {
183  if (field->getTypeName() == typeName) {
184  elements.push_back(&recordType);
185  continue;
186  }
187 
188  // Recursively create element's type.
189  auto* elementType = createType(field->getTypeName(), nameToType);
190  if (elementType == nullptr) {
191  return nullptr;
192  }
193  elements.push_back(elementType);
194  }
195 
196  recordType.setFields(std::move(elements));
197  return &recordType;
198 
199  } else if (isA<ast::AlgebraicDataType>(astType)) {
200  // ADT can be recursive so its need to be forward initialized
201  auto& adt = env.createType<AlgebraicDataType>(typeName);
202 
203  std::vector<AlgebraicDataType::Branch> elements;
204 
205  // Create and collect branches types.
206  for (auto* branch : as<ast::AlgebraicDataType>(astType)->getBranches()) {
207  std::vector<const Type*> branchTypes;
208 
209  for (auto* attr : branch->getFields()) {
210  auto* type = createType(attr->getTypeName(), nameToType);
211  if (type == nullptr) return nullptr;
212  branchTypes.push_back(type);
213  }
214  elements.push_back({branch->getConstructor(), std::move(branchTypes)});
215  }
216 
217  adt.setBranches(std::move(elements));
218 
219  return &adt;
220  } else {
221  fatal("unsupported type construct: %s", typeid(*astType).name());
222  }
223 }
224 
225 void TypeEnvironmentAnalysis::print(std::ostream& os) const {
226  env.print(os);
227 }
228 
229 } // namespace souffle::ast::analysis
souffle::ast::analysis::TypeEnvironmentAnalysis::primitiveTypesInUnions
std::map< QualifiedName, std::set< QualifiedName > > primitiveTypesInUnions
Definition: TypeEnvironment.h:68
TranslationUnit.h
souffle::ast::analysis::TypeEnvironment::getType
const Type & getType(const QualifiedName &) const
Definition: TypeSystem.cpp:81
souffle::ast::analysis::TypeEnvironment::print
void print(std::ostream &out) const
Definition: TypeSystem.cpp:247
UnionType.h
MiscUtil.h
souffle::ast::analysis::getBaseType
static const Type & getBaseType(const Type *type)
Definition: TypeConstraints.cpp:118
souffle::ast::analysis::TypeEnvironment::isType
bool isType(const QualifiedName &) const
Definition: TypeSystem.cpp:73
souffle::ast::analysis::TypeEnvironmentAnalysis::isCyclic
bool isCyclic(const QualifiedName &identifier) const
Definition: TypeEnvironment.h:62
souffle::ast::analysis::SubsetType
A type being a subset of another type.
Definition: TypeSystem.h:108
tinyformat.h
souffle::ast::analysis::TypeEnvironmentAnalysis::env
TypeEnvironment env
Definition: TypeEnvironment.h:67
Attribute.h
souffle::ast::analysis::TypeEnvironmentAnalysis::run
void run(const TranslationUnit &translationUnit) override
run analysis for a Ast translation unit
Definition: TypeEnvironment.cpp:120
souffle::ast::analysis::TypeEnvironmentAnalysis::createType
const Type * createType(const QualifiedName &typeName, const std::map< QualifiedName, const ast::Type * > &nameToType)
Recursively create a type in env, that is first create its base types and then the type itself.
Definition: TypeEnvironment.cpp:147
souffle::ast::analysis::TypeEnvironmentAnalysis::cyclicTypes
std::set< QualifiedName > cyclicTypes
Definition: TypeEnvironment.h:69
GraphUtils.h
Type.h
SubsetType.h
Program.h
RecordType.h
souffle::fatal
void fatal(const char *format, const Args &... args)
Definition: MiscUtil.h:198
souffle::ast::analysis::TypeEnvironmentAnalysis::print
void print(std::ostream &os) const override
print the analysis result in HTML format
Definition: TypeEnvironment.cpp:231
souffle::ast::analysis
Definition: Aggregate.cpp:39
souffle::ast::analysis::TypeEnvironment::createType
T & createType(const QualifiedName &name, Args &&... args)
create type in this environment
Definition: TypeSystem.h:411
AlgebraicDataType.h
TypeEnvironment.h
BranchDeclaration.h
std::type
ElementType type
Definition: span.h:640
TypeSystem.h