1 /********************* */
2 /*! \file symbol_table.cpp
4 ** Top contributors (to current version):
5 ** Morgan Deters, Christopher L. Conway, Francois Bobot
6 ** This file is part of the CVC4 project.
7 ** Copyright (c) 2009-2017 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.\endverbatim
12 ** \brief Convenience class for scoping variable and type
13 ** declarations (implementation)
15 ** Convenience class for scoping variable and type declarations
19 #include "expr/symbol_table.h"
23 #include <unordered_map>
26 #include "context/cdhashmap.h"
27 #include "context/cdhashset.h"
28 #include "context/context.h"
29 #include "expr/expr.h"
30 #include "expr/expr_manager_scope.h"
31 #include "expr/type.h"
35 using ::CVC4::context::CDHashMap
;
36 using ::CVC4::context::CDHashSet
;
37 using ::CVC4::context::Context
;
40 using ::std::ostream_iterator
;
45 /** Overloaded type trie.
47 * This data structure stores a trie of expressions with
48 * the same name, and must be distinguished by their argument types.
49 * It is context-dependent.
51 * Using the argument allowFunVariants,
52 * it may either be configured to allow function variants or not,
53 * where a function variant is function that expects the same
54 * argument types as another.
56 * For example, the following definitions introduce function
57 * variants for the symbol f:
59 * 1. (declare-fun f (Int) Int) and
60 * (declare-fun f (Int) Bool)
62 * 2. (declare-fun f (Int) Int) and
63 * (declare-fun f (Int) Int)
65 * 3. (declare-datatypes ((Tup 0)) ((f (data Int)))) and
66 * (declare-fun f (Int) Tup)
68 * 4. (declare-datatypes ((Tup 0)) ((mkTup (f Int)))) and
69 * (declare-fun f (Tup) Bool)
71 * If function variants is set to true, we allow function variants
72 * but not function redefinition. In examples 2 and 3, f is
73 * declared twice as a symbol of identical argument and range
74 * types. We never accept these definitions. However, we do
75 * allow examples 1 and 4 above when allowFunVariants is true.
77 * For 0-argument functions (constants), we always allow
78 * function variants. That is, we always accept these examples:
80 * 5. (declare-fun c () Int)
81 * (declare-fun c () Bool)
83 * 6. (declare-datatypes ((Enum 0)) ((c)))
84 * (declare-fun c () Int)
86 * and always reject constant redefinition such as:
88 * 7. (declare-fun c () Int)
89 * (declare-fun c () Int)
91 * 8. (declare-datatypes ((Enum 0)) ((c))) and
92 * (declare-fun c () Enum)
94 class OverloadedTypeTrie
{
96 OverloadedTypeTrie(Context
* c
, bool allowFunVariants
= false)
97 : d_overloaded_symbols(new (true) CDHashSet
<Expr
, ExprHashFunction
>(c
)),
98 d_allowFunctionVariants(allowFunVariants
)
101 ~OverloadedTypeTrie() { d_overloaded_symbols
->deleteSelf(); }
103 /** is this function overloaded? */
104 bool isOverloadedFunction(Expr fun
) const;
106 /** Get overloaded constant for type.
107 * If possible, it returns a defined symbol with name
108 * that has type t. Otherwise returns null expression.
110 Expr
getOverloadedConstantForType(const std::string
& name
, Type t
) const;
113 * If possible, returns a defined function for a name
114 * and a vector of expected argument types. Otherwise returns
117 Expr
getOverloadedFunctionForTypes(const std::string
& name
,
118 const std::vector
<Type
>& argTypes
) const;
119 /** called when obj is bound to name, and prev_bound_obj was already bound to
120 * name Returns false if the binding is invalid.
122 bool bind(const string
& name
, Expr prev_bound_obj
, Expr obj
);
125 /** Marks expression obj with name as overloaded.
126 * Adds relevant information to the type arg trie data structure.
127 * It returns false if there is already an expression bound to that name
128 * whose type expects the same arguments as the type of obj but is not
129 * identical to the type of obj. For example, if we declare :
131 * (declare-datatypes () ((List (cons (hd Int) (tl List)) (nil))))
132 * (declare-fun cons (Int List) List)
134 * cons : constructor_type( Int, List, List )
135 * cons : function_type( Int, List, List )
137 * These are put in the same place in the trie but do not have identical type,
138 * hence we return false.
140 bool markOverloaded(const string
& name
, Expr obj
);
141 /** the null expression */
143 // The (context-independent) trie storing that maps expected argument
144 // vectors to symbols. All expressions stored in d_symbols are only
145 // interpreted as active if they also appear in the context-dependent
146 // set d_overloaded_symbols.
149 // children of this node
150 std::map
<Type
, TypeArgTrie
> d_children
;
151 // symbols at this node
152 std::map
<Type
, Expr
> d_symbols
;
154 /** for each string with operator overloading, this stores the data structure
156 std::unordered_map
<std::string
, TypeArgTrie
> d_overload_type_arg_trie
;
157 /** The set of overloaded symbols. */
158 CDHashSet
<Expr
, ExprHashFunction
>* d_overloaded_symbols
;
159 /** allow function variants
160 * This is true if we allow overloading (non-constant) functions that expect
161 * the same argument types.
163 bool d_allowFunctionVariants
;
164 /** get unique overloaded function
165 * If tat->d_symbols contains an active overloaded function, it
166 * returns that function, where that function must be unique
168 * Otherwise, it returns the null expression.
170 Expr
getOverloadedFunctionAt(const TypeArgTrie
* tat
, bool reqUnique
=true) const;
173 bool OverloadedTypeTrie::isOverloadedFunction(Expr fun
) const {
174 return d_overloaded_symbols
->find(fun
) != d_overloaded_symbols
->end();
177 Expr
OverloadedTypeTrie::getOverloadedConstantForType(const std::string
& name
,
179 std::unordered_map
<std::string
, TypeArgTrie
>::const_iterator it
=
180 d_overload_type_arg_trie
.find(name
);
181 if (it
!= d_overload_type_arg_trie
.end()) {
182 std::map
<Type
, Expr
>::const_iterator its
= it
->second
.d_symbols
.find(t
);
183 if (its
!= it
->second
.d_symbols
.end()) {
184 Expr expr
= its
->second
;
185 // must be an active symbol
186 if (isOverloadedFunction(expr
)) {
194 Expr
OverloadedTypeTrie::getOverloadedFunctionForTypes(
195 const std::string
& name
, const std::vector
<Type
>& argTypes
) const {
196 std::unordered_map
<std::string
, TypeArgTrie
>::const_iterator it
=
197 d_overload_type_arg_trie
.find(name
);
198 if (it
!= d_overload_type_arg_trie
.end()) {
199 const TypeArgTrie
* tat
= &it
->second
;
200 for (unsigned i
= 0; i
< argTypes
.size(); i
++) {
201 std::map
<Type
, TypeArgTrie
>::const_iterator itc
=
202 tat
->d_children
.find(argTypes
[i
]);
203 if (itc
!= tat
->d_children
.end()) {
206 // no functions match
210 // we ensure that there is *only* one active symbol at this node
211 return getOverloadedFunctionAt(tat
);
216 bool OverloadedTypeTrie::bind(const string
& name
, Expr prev_bound_obj
,
219 if (!isOverloadedFunction(prev_bound_obj
)) {
220 // mark previous as overloaded
221 retprev
= markOverloaded(name
, prev_bound_obj
);
223 // mark this as overloaded
224 bool retobj
= markOverloaded(name
, obj
);
225 return retprev
&& retobj
;
228 bool OverloadedTypeTrie::markOverloaded(const string
& name
, Expr obj
) {
229 Trace("parser-overloading") << "Overloaded function : " << name
;
230 Trace("parser-overloading") << " with type " << obj
.getType() << std::endl
;
231 // get the argument types
232 Type t
= obj
.getType();
234 std::vector
<Type
> argTypes
;
235 if (t
.isFunction()) {
236 argTypes
= static_cast<FunctionType
>(t
).getArgTypes();
237 rangeType
= static_cast<FunctionType
>(t
).getRangeType();
238 } else if (t
.isConstructor()) {
239 argTypes
= static_cast<ConstructorType
>(t
).getArgTypes();
240 rangeType
= static_cast<ConstructorType
>(t
).getRangeType();
241 } else if (t
.isTester()) {
242 argTypes
.push_back(static_cast<TesterType
>(t
).getDomain());
243 rangeType
= static_cast<TesterType
>(t
).getRangeType();
244 } else if (t
.isSelector()) {
245 argTypes
.push_back(static_cast<SelectorType
>(t
).getDomain());
246 rangeType
= static_cast<SelectorType
>(t
).getRangeType();
249 TypeArgTrie
* tat
= &d_overload_type_arg_trie
[name
];
250 for (unsigned i
= 0; i
< argTypes
.size(); i
++) {
251 tat
= &(tat
->d_children
[argTypes
[i
]]);
254 // check if function variants are allowed here
255 if (d_allowFunctionVariants
|| argTypes
.empty())
257 // they are allowed, check for redefinition
258 std::map
<Type
, Expr
>::iterator it
= tat
->d_symbols
.find(rangeType
);
259 if (it
!= tat
->d_symbols
.end())
261 Expr prev_obj
= it
->second
;
262 // if there is already an active function with the same name and expects
263 // the same argument types and has the same return type, we reject the
264 // re-declaration here.
265 if (isOverloadedFunction(prev_obj
))
273 // they are not allowed, we cannot have any function defined here.
274 Expr existingFun
= getOverloadedFunctionAt(tat
, false);
275 if (!existingFun
.isNull())
281 // otherwise, update the symbols
282 d_overloaded_symbols
->insert(obj
);
283 tat
->d_symbols
[rangeType
] = obj
;
287 Expr
OverloadedTypeTrie::getOverloadedFunctionAt(
288 const OverloadedTypeTrie::TypeArgTrie
* tat
, bool reqUnique
) const
291 for (std::map
<Type
, Expr
>::const_iterator its
= tat
->d_symbols
.begin();
292 its
!= tat
->d_symbols
.end();
295 Expr expr
= its
->second
;
296 if (isOverloadedFunction(expr
))
298 if (retExpr
.isNull())
311 // multiple functions match
319 class SymbolTable::Implementation
{
323 d_exprMap(new (true) CDHashMap
<string
, Expr
>(&d_context
)),
324 d_typeMap(new (true) TypeMap(&d_context
)),
325 d_functions(new (true) CDHashSet
<Expr
, ExprHashFunction
>(&d_context
)) {
326 d_overload_trie
= new OverloadedTypeTrie(&d_context
);
330 d_exprMap
->deleteSelf();
331 d_typeMap
->deleteSelf();
332 d_functions
->deleteSelf();
333 delete d_overload_trie
;
336 bool bind(const string
& name
, Expr obj
, bool levelZero
, bool doOverload
);
337 bool bindDefinedFunction(const string
& name
, Expr obj
, bool levelZero
,
339 void bindType(const string
& name
, Type t
, bool levelZero
= false);
340 void bindType(const string
& name
, const vector
<Type
>& params
, Type t
,
341 bool levelZero
= false);
342 bool isBound(const string
& name
) const;
343 bool isBoundDefinedFunction(const string
& name
) const;
344 bool isBoundDefinedFunction(Expr func
) const;
345 bool isBoundType(const string
& name
) const;
346 Expr
lookup(const string
& name
) const;
347 Type
lookupType(const string
& name
) const;
348 Type
lookupType(const string
& name
, const vector
<Type
>& params
) const;
349 size_t lookupArity(const string
& name
);
352 size_t getLevel() const;
354 //------------------------ operator overloading
355 /** implementation of function from header */
356 bool isOverloadedFunction(Expr fun
) const;
358 /** implementation of function from header */
359 Expr
getOverloadedConstantForType(const std::string
& name
, Type t
) const;
361 /** implementation of function from header */
362 Expr
getOverloadedFunctionForTypes(const std::string
& name
,
363 const std::vector
<Type
>& argTypes
) const;
364 //------------------------ end operator overloading
366 /** The context manager for the scope maps. */
369 /** A map for expressions. */
370 CDHashMap
<string
, Expr
>* d_exprMap
;
372 /** A map for types. */
373 using TypeMap
= CDHashMap
<string
, std::pair
<vector
<Type
>, Type
>>;
376 /** A set of defined functions. */
377 CDHashSet
<Expr
, ExprHashFunction
>* d_functions
;
379 //------------------------ operator overloading
380 // the null expression
382 // overloaded type trie, stores all information regarding overloading
383 OverloadedTypeTrie
* d_overload_trie
;
384 /** bind with overloading
385 * This is called whenever obj is bound to name where overloading symbols is
386 * allowed. If a symbol is previously bound to that name, it marks both as
387 * overloaded. Returns false if the binding was invalid.
389 bool bindWithOverloading(const string
& name
, Expr obj
);
390 //------------------------ end operator overloading
391 }; /* SymbolTable::Implementation */
393 bool SymbolTable::Implementation::bind(const string
& name
, Expr obj
,
394 bool levelZero
, bool doOverload
) {
395 PrettyCheckArgument(!obj
.isNull(), obj
, "cannot bind to a null Expr");
396 ExprManagerScope
ems(obj
);
398 if (!bindWithOverloading(name
, obj
)) {
403 d_exprMap
->insertAtContextLevelZero(name
, obj
);
405 d_exprMap
->insert(name
, obj
);
410 bool SymbolTable::Implementation::bindDefinedFunction(const string
& name
,
411 Expr obj
, bool levelZero
,
413 PrettyCheckArgument(!obj
.isNull(), obj
, "cannot bind to a null Expr");
414 ExprManagerScope
ems(obj
);
416 if (!bindWithOverloading(name
, obj
)) {
421 d_exprMap
->insertAtContextLevelZero(name
, obj
);
422 d_functions
->insertAtContextLevelZero(obj
);
424 d_exprMap
->insert(name
, obj
);
425 d_functions
->insert(obj
);
430 bool SymbolTable::Implementation::isBound(const string
& name
) const {
431 return d_exprMap
->find(name
) != d_exprMap
->end();
434 bool SymbolTable::Implementation::isBoundDefinedFunction(
435 const string
& name
) const {
436 CDHashMap
<string
, Expr
>::iterator found
= d_exprMap
->find(name
);
437 return found
!= d_exprMap
->end() && d_functions
->contains((*found
).second
);
440 bool SymbolTable::Implementation::isBoundDefinedFunction(Expr func
) const {
441 return d_functions
->contains(func
);
444 Expr
SymbolTable::Implementation::lookup(const string
& name
) const {
445 Assert(isBound(name
));
446 Expr expr
= (*d_exprMap
->find(name
)).second
;
447 if (isOverloadedFunction(expr
)) {
454 void SymbolTable::Implementation::bindType(const string
& name
, Type t
,
457 d_typeMap
->insertAtContextLevelZero(name
, make_pair(vector
<Type
>(), t
));
459 d_typeMap
->insert(name
, make_pair(vector
<Type
>(), t
));
463 void SymbolTable::Implementation::bindType(const string
& name
,
464 const vector
<Type
>& params
, Type t
,
466 if (Debug
.isOn("sort")) {
467 Debug("sort") << "bindType(" << name
<< ", [";
468 if (params
.size() > 0) {
469 copy(params
.begin(), params
.end() - 1,
470 ostream_iterator
<Type
>(Debug("sort"), ", "));
471 Debug("sort") << params
.back();
473 Debug("sort") << "], " << t
<< ")" << endl
;
476 d_typeMap
->insertAtContextLevelZero(name
, make_pair(params
, t
));
478 d_typeMap
->insert(name
, make_pair(params
, t
));
482 bool SymbolTable::Implementation::isBoundType(const string
& name
) const {
483 return d_typeMap
->find(name
) != d_typeMap
->end();
486 Type
SymbolTable::Implementation::lookupType(const string
& name
) const {
487 pair
<vector
<Type
>, Type
> p
= (*d_typeMap
->find(name
)).second
;
488 PrettyCheckArgument(p
.first
.size() == 0, name
,
489 "type constructor arity is wrong: "
490 "`%s' requires %u parameters but was provided 0",
491 name
.c_str(), p
.first
.size());
495 Type
SymbolTable::Implementation::lookupType(const string
& name
,
496 const vector
<Type
>& params
) const {
497 pair
<vector
<Type
>, Type
> p
= (*d_typeMap
->find(name
)).second
;
498 PrettyCheckArgument(p
.first
.size() == params
.size(), params
,
499 "type constructor arity is wrong: "
500 "`%s' requires %u parameters but was provided %u",
501 name
.c_str(), p
.first
.size(), params
.size());
502 if (p
.first
.size() == 0) {
503 PrettyCheckArgument(p
.second
.isSort(), name
.c_str());
506 if (p
.second
.isSortConstructor()) {
507 if (Debug
.isOn("sort")) {
508 Debug("sort") << "instantiating using a sort constructor" << endl
;
509 Debug("sort") << "have formals [";
510 copy(p
.first
.begin(), p
.first
.end() - 1,
511 ostream_iterator
<Type
>(Debug("sort"), ", "));
512 Debug("sort") << p
.first
.back() << "]" << endl
<< "parameters [";
513 copy(params
.begin(), params
.end() - 1,
514 ostream_iterator
<Type
>(Debug("sort"), ", "));
515 Debug("sort") << params
.back() << "]" << endl
516 << "type ctor " << name
<< endl
517 << "type is " << p
.second
<< endl
;
520 Type instantiation
= SortConstructorType(p
.second
).instantiate(params
);
522 Debug("sort") << "instance is " << instantiation
<< endl
;
524 return instantiation
;
525 } else if (p
.second
.isDatatype()) {
526 PrettyCheckArgument(DatatypeType(p
.second
).isParametric(), name
,
527 "expected parametric datatype");
528 return DatatypeType(p
.second
).instantiate(params
);
530 if (Debug
.isOn("sort")) {
531 Debug("sort") << "instantiating using a sort substitution" << endl
;
532 Debug("sort") << "have formals [";
533 copy(p
.first
.begin(), p
.first
.end() - 1,
534 ostream_iterator
<Type
>(Debug("sort"), ", "));
535 Debug("sort") << p
.first
.back() << "]" << endl
<< "parameters [";
536 copy(params
.begin(), params
.end() - 1,
537 ostream_iterator
<Type
>(Debug("sort"), ", "));
538 Debug("sort") << params
.back() << "]" << endl
539 << "type ctor " << name
<< endl
540 << "type is " << p
.second
<< endl
;
543 Type instantiation
= p
.second
.substitute(p
.first
, params
);
545 Debug("sort") << "instance is " << instantiation
<< endl
;
547 return instantiation
;
551 size_t SymbolTable::Implementation::lookupArity(const string
& name
) {
552 pair
<vector
<Type
>, Type
> p
= (*d_typeMap
->find(name
)).second
;
553 return p
.first
.size();
556 void SymbolTable::Implementation::popScope() {
557 if (d_context
.getLevel() == 0) {
558 throw ScopeException();
563 void SymbolTable::Implementation::pushScope() { d_context
.push(); }
565 size_t SymbolTable::Implementation::getLevel() const {
566 return d_context
.getLevel();
569 void SymbolTable::Implementation::reset() {
570 this->SymbolTable::Implementation::~Implementation();
571 new (this) SymbolTable::Implementation();
574 bool SymbolTable::Implementation::isOverloadedFunction(Expr fun
) const {
575 return d_overload_trie
->isOverloadedFunction(fun
);
578 Expr
SymbolTable::Implementation::getOverloadedConstantForType(
579 const std::string
& name
, Type t
) const {
580 return d_overload_trie
->getOverloadedConstantForType(name
, t
);
583 Expr
SymbolTable::Implementation::getOverloadedFunctionForTypes(
584 const std::string
& name
, const std::vector
<Type
>& argTypes
) const {
585 return d_overload_trie
->getOverloadedFunctionForTypes(name
, argTypes
);
588 bool SymbolTable::Implementation::bindWithOverloading(const string
& name
,
590 CDHashMap
<string
, Expr
>::const_iterator it
= d_exprMap
->find(name
);
591 if (it
!= d_exprMap
->end()) {
592 const Expr
& prev_bound_obj
= (*it
).second
;
593 if (prev_bound_obj
!= obj
) {
594 return d_overload_trie
->bind(name
, prev_bound_obj
, obj
);
600 bool SymbolTable::isOverloadedFunction(Expr fun
) const {
601 return d_implementation
->isOverloadedFunction(fun
);
604 Expr
SymbolTable::getOverloadedConstantForType(const std::string
& name
,
606 return d_implementation
->getOverloadedConstantForType(name
, t
);
609 Expr
SymbolTable::getOverloadedFunctionForTypes(
610 const std::string
& name
, const std::vector
<Type
>& argTypes
) const {
611 return d_implementation
->getOverloadedFunctionForTypes(name
, argTypes
);
614 SymbolTable::SymbolTable()
615 : d_implementation(new SymbolTable::Implementation()) {}
617 SymbolTable::~SymbolTable() {}
618 bool SymbolTable::bind(const string
& name
,
623 return d_implementation
->bind(name
, obj
, levelZero
, doOverload
);
626 bool SymbolTable::bindDefinedFunction(const string
& name
,
631 return d_implementation
->bindDefinedFunction(name
, obj
, levelZero
,
635 void SymbolTable::bindType(const string
& name
, Type t
, bool levelZero
)
637 d_implementation
->bindType(name
, t
, levelZero
);
640 void SymbolTable::bindType(const string
& name
,
641 const vector
<Type
>& params
,
645 d_implementation
->bindType(name
, params
, t
, levelZero
);
648 bool SymbolTable::isBound(const string
& name
) const
650 return d_implementation
->isBound(name
);
653 bool SymbolTable::isBoundDefinedFunction(const string
& name
) const
655 return d_implementation
->isBoundDefinedFunction(name
);
658 bool SymbolTable::isBoundDefinedFunction(Expr func
) const
660 return d_implementation
->isBoundDefinedFunction(func
);
662 bool SymbolTable::isBoundType(const string
& name
) const
664 return d_implementation
->isBoundType(name
);
666 Expr
SymbolTable::lookup(const string
& name
) const
668 return d_implementation
->lookup(name
);
670 Type
SymbolTable::lookupType(const string
& name
) const
672 return d_implementation
->lookupType(name
);
675 Type
SymbolTable::lookupType(const string
& name
,
676 const vector
<Type
>& params
) const
678 return d_implementation
->lookupType(name
, params
);
680 size_t SymbolTable::lookupArity(const string
& name
) {
681 return d_implementation
->lookupArity(name
);
683 void SymbolTable::popScope() { d_implementation
->popScope(); }
684 void SymbolTable::pushScope() { d_implementation
->pushScope(); }
685 size_t SymbolTable::getLevel() const { return d_implementation
->getLevel(); }
686 void SymbolTable::reset() { d_implementation
->reset(); }