Assert(!isResolved());
Assert(!selectorType.isNull());
SkolemManager* sm = NodeManager::currentNM()->getSkolemManager();
- Node type = sm->mkDummySkolem(
+ Node sel = sm->mkDummySkolem(
"unresolved_" + selectorName,
selectorType,
"is an unresolved selector type placeholder",
NodeManager::SKOLEM_EXACT_NAME | NodeManager::SKOLEM_NO_NOTIFY);
- Trace("datatypes") << "DTypeConstructor::addArg: " << type << std::endl;
+ // can use null updater for now
+ Node nullNode;
+ Trace("datatypes") << "DTypeConstructor::addArg: " << sel << std::endl;
std::shared_ptr<DTypeSelector> a =
- std::make_shared<DTypeSelector>(selectorName, type);
+ std::make_shared<DTypeSelector>(selectorName, sel, nullNode);
addArg(a);
}
void DTypeConstructor::addArgSelf(std::string selectorName)
{
Trace("datatypes") << "DTypeConstructor::addArgSelf" << std::endl;
+ Node nullNode;
std::shared_ptr<DTypeSelector> a =
- std::make_shared<DTypeSelector>(selectorName + '\0', Node::null());
+ std::make_shared<DTypeSelector>(selectorName + '\0', nullNode, nullNode);
addArg(a);
}
{
Trace("datatypes-init") << " ...self selector" << std::endl;
range = self;
- arg->d_selector = sm->mkDummySkolem(
- argName,
- nm->mkSelectorType(self, self),
- "is a selector",
- NodeManager::SKOLEM_EXACT_NAME | NodeManager::SKOLEM_NO_NOTIFY);
}
else
{
{
Trace("datatypes-init") << " ...resolved selector" << std::endl;
range = (*j).second;
- arg->d_selector = sm->mkDummySkolem(
- argName,
- nm->mkSelectorType(self, range),
- "is a selector",
- NodeManager::SKOLEM_EXACT_NAME | NodeManager::SKOLEM_NO_NOTIFY);
}
}
}
}
Trace("datatypes-init")
<< " ...range after parametric substitution " << range << std::endl;
- arg->d_selector = sm->mkDummySkolem(
- argName,
- nm->mkSelectorType(self, range),
- "is a selector",
- NodeManager::SKOLEM_EXACT_NAME | NodeManager::SKOLEM_NO_NOTIFY);
}
+ // Internally, selectors (and updaters) are fresh internal skolems which
+ // we constructor via mkDummySkolem.
+ arg->d_selector = sm->mkDummySkolem(
+ argName,
+ nm->mkSelectorType(self, range),
+ "is a selector",
+ NodeManager::SKOLEM_EXACT_NAME | NodeManager::SKOLEM_NO_NOTIFY);
+ std::string updateName("update_" + argName);
+ arg->d_updater = sm->mkDummySkolem(
+ updateName,
+ nm->mkDatatypeUpdateType(self, range),
+ "is a selector",
+ NodeManager::SKOLEM_EXACT_NAME | NodeManager::SKOLEM_NO_NOTIFY);
+ // must set indices to ensure datatypes::utils::indexOf works
arg->d_selector.setAttribute(DTypeConsIndexAttr(), cindex);
- arg->d_selector.setAttribute(DTypeIndexAttr(), index++);
+ arg->d_selector.setAttribute(DTypeIndexAttr(), index);
+ arg->d_updater.setAttribute(DTypeConsIndexAttr(), cindex);
+ arg->d_updater.setAttribute(DTypeIndexAttr(), index);
+ index = index + 1;
arg->d_resolved = true;
argTypes.push_back(range);
// We use \0 as a distinguished marker for unresolved selectors for doing
namespace cvc5 {
-DTypeSelector::DTypeSelector(std::string name, Node selector)
- : d_name(name), d_selector(selector), d_resolved(false)
+DTypeSelector::DTypeSelector(std::string name, Node selector, Node updater)
+ : d_name(name), d_selector(selector), d_updater(updater), d_resolved(false)
{
Assert(name != "");
}
Assert(d_resolved);
return d_selector;
}
+Node DTypeSelector::getUpdater() const
+{
+ Assert(d_resolved);
+ return d_updater;
+}
Node DTypeSelector::getConstructor() const
{
public:
/** constructor */
- DTypeSelector(std::string name, Node selector);
+ DTypeSelector(std::string name, Node selector, Node updater);
/** Get the name of this constructor argument. */
const std::string& getName() const;
* only permitted after resolution.
*/
Node getSelector() const;
+ /**
+ * Get the upater for this constructor argument; this call is
+ * only permitted after resolution.
+ */
+ Node getUpdater() const;
/**
* Get the associated constructor for this constructor argument;
std::string d_name;
/** the selector expression */
Node d_selector;
+ /** the updater expression */
+ Node d_updater;
/**
* The constructor associated with this selector. This field is initialized
* by the constructor of this selector during a call to
return mkTypeNode(kind::CONSTRUCTOR_TYPE, sorts);
}
+TypeNode NodeManager::mkSelectorType(TypeNode domain, TypeNode range)
+{
+ CheckArgument(
+ domain.isDatatype(), domain, "cannot create non-datatype selector type");
+ return mkTypeNode(kind::SELECTOR_TYPE, domain, range);
+}
+
+TypeNode NodeManager::mkTesterType(TypeNode domain)
+{
+ CheckArgument(
+ domain.isDatatype(), domain, "cannot create non-datatype tester");
+ return mkTypeNode(kind::TESTER_TYPE, domain);
+}
+
+TypeNode NodeManager::mkDatatypeUpdateType(TypeNode domain, TypeNode range)
+{
+ CheckArgument(
+ domain.isDatatype(), domain, "cannot create non-datatype upater type");
+ // It is a function type domain x range -> domain, we store only the
+ // arguments
+ return mkTypeNode(kind::DT_UPDATE_TYPE, domain, range);
+}
+
TypeNode NodeManager::TupleTypeCache::getTupleType( NodeManager * nm, std::vector< TypeNode >& types, unsigned index ) {
if( index==types.size() ){
if( d_data.isNull() ){
TypeNode mkConstructorType(const std::vector<TypeNode>& args, TypeNode range);
/** Make a type representing a selector with the given parameterization */
- inline TypeNode mkSelectorType(TypeNode domain, TypeNode range);
+ TypeNode mkSelectorType(TypeNode domain, TypeNode range);
/** Make a type representing a tester with given parameterization */
- inline TypeNode mkTesterType(TypeNode domain);
+ TypeNode mkTesterType(TypeNode domain);
+
+ /** Make a type representing an updater with the given parameterization */
+ TypeNode mkDatatypeUpdateType(TypeNode domain, TypeNode range);
/** Bits for use in mkSort() flags. */
enum
return mkTypeNode(kind::SET_TYPE, elementType);
}
-inline TypeNode NodeManager::mkSelectorType(TypeNode domain, TypeNode range) {
- CheckArgument(domain.isDatatype(), domain,
- "cannot create non-datatype selector type");
- CheckArgument(range.isFirstClass(),
- range,
- "cannot have selector fields that are not first-class types. "
- "Try option --uf-ho.");
- return mkTypeNode(kind::SELECTOR_TYPE, domain, range);
-}
-
-inline TypeNode NodeManager::mkTesterType(TypeNode domain) {
- CheckArgument(domain.isDatatype(), domain,
- "cannot create non-datatype tester");
- return mkTypeNode(kind::TESTER_TYPE, domain );
-}
-
inline expr::NodeValue* NodeManager::poolLookup(expr::NodeValue* nv) const {
NodeValuePool::const_iterator find = d_nodeValuePool.find(nv);
if(find == d_nodeValuePool.end()) {
bool TypeNode::isFirstClass() const
{
return getKind() != kind::CONSTRUCTOR_TYPE && getKind() != kind::SELECTOR_TYPE
- && getKind() != kind::TESTER_TYPE
+ && getKind() != kind::TESTER_TYPE && getKind() != kind::DT_UPDATE_TYPE
&& (getKind() != kind::TYPE_CONSTANT
|| (getConst<TypeConstant>() != REGEXP_TYPE
&& getConst<TypeConstant>() != SEXPR_TYPE));
bool TypeNode::isTester() const { return getKind() == kind::TESTER_TYPE; }
+bool TypeNode::isDatatypeUpdater() const
+{
+ return getKind() == kind::DT_UPDATE_TYPE;
+}
+
bool TypeNode::isCodatatype() const
{
if (isDatatype())
/** Is this a tester type */
bool isTester() const;
+ /** Is this a datatype updater type */
+ bool isDatatypeUpdater() const;
+
/** Get the internal Datatype specification from a datatype type */
const DType& getDType() const;
case kind::APPLY_TESTER:
case kind::APPLY_SELECTOR:
case kind::APPLY_SELECTOR_TOTAL:
+ case kind::APPLY_DT_UPDATE:
case kind::PARAMETRIC_DATATYPE: break;
// separation logic
"::cvc5::theory::builtin::FunctionProperties::computeCardinality(%TYPE%)" \
"theory/builtin/theory_builtin_type_rules.h"
+# tester type has a constructor type
+operator DT_UPDATE_TYPE 2 "datatype update"
+# can re-use function cardinality
+cardinality DT_UPDATE_TYPE \
+ "::cvc5::theory::builtin::FunctionProperties::computeCardinality(%TYPE%)" \
+ "theory/builtin/theory_builtin_type_rules.h"
+
parameterized APPLY_CONSTRUCTOR APPLY_TYPE_ASCRIPTION 0: "constructor application; first parameter is the constructor, remaining parameters (if any) are parameters to the constructor"
parameterized APPLY_SELECTOR SELECTOR_TYPE 1 "selector application; parameter is a datatype term (undefined if mis-applied)"
parameterized APPLY_TESTER TESTER_TYPE 1 "tester application; first parameter is a tester, second is a datatype term"
+parameterized APPLY_DT_UPDATE DT_UPDATE_TYPE 2 "datatype update application; first parameter is an update, second is a datatype term to update, third is the value to update with"
+
constant DATATYPE_TYPE \
::cvc5::DatatypeIndexConstant \
"::cvc5::DatatypeIndexConstantHashFunction" \
typerule APPLY_SELECTOR ::cvc5::theory::datatypes::DatatypeSelectorTypeRule
typerule APPLY_SELECTOR_TOTAL ::cvc5::theory::datatypes::DatatypeSelectorTypeRule
typerule APPLY_TESTER ::cvc5::theory::datatypes::DatatypeTesterTypeRule
+typerule APPLY_DT_UPDATE ::cvc5::theory::datatypes::DatatypeUpdateTypeRule
typerule APPLY_TYPE_ASCRIPTION ::cvc5::theory::datatypes::DatatypeAscriptionTypeRule
# constructor applications are constant if they are applied only to constants
typerule RECORD_UPDATE_OP ::cvc5::theory::datatypes::RecordUpdateOpTypeRule
typerule RECORD_UPDATE ::cvc5::theory::datatypes::RecordUpdateTypeRule
-
operator DT_SIZE 1 "datatypes size"
typerule DT_SIZE ::cvc5::theory::datatypes::DtSizeTypeRule
return nodeManager->booleanType();
}
+TypeNode DatatypeUpdateTypeRule::computeType(NodeManager* nodeManager,
+ TNode n,
+ bool check)
+{
+ Assert(n.getKind() == kind::APPLY_DT_UPDATE);
+ TypeNode updType = n.getOperator().getType(check);
+ Assert(updType.getNumChildren() == 2);
+ if (check)
+ {
+ for (size_t i = 0; i < 2; i++)
+ {
+ TypeNode childType = n[i].getType(check);
+ TypeNode t = updType[i];
+ if (t.isParametricDatatype())
+ {
+ Debug("typecheck-idt")
+ << "typecheck parameterized update: " << n << std::endl;
+ TypeMatcher m(t);
+ if (!m.doMatching(t, childType))
+ {
+ throw TypeCheckingExceptionPrivate(
+ n,
+ "matching failed for update argument of parameterized datatype");
+ }
+ }
+ else
+ {
+ Debug("typecheck-idt") << "typecheck update: " << n << std::endl;
+ Debug("typecheck-idt") << "test type: " << updType << std::endl;
+ if (!t.isComparableTo(childType))
+ {
+ throw TypeCheckingExceptionPrivate(n, "bad type for update argument");
+ }
+ }
+ }
+ }
+ // type is the first argument
+ return updType[0];
+}
+
TypeNode DatatypeAscriptionTypeRule::computeType(NodeManager* nodeManager,
TNode n,
bool check)
static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check);
};
+struct DatatypeUpdateTypeRule
+{
+ static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check);
+};
+
struct DatatypeAscriptionTypeRule {
static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check);
};
{
case CONSTRUCTOR_TYPE: return t[t.getNumChildren() - 1].getDType();
case SELECTOR_TYPE:
- case TESTER_TYPE: return t[0].getDType();
+ case TESTER_TYPE:
+ case DT_UPDATE_TYPE: return t[0].getDType();
default:
Unhandled() << "arg must be a datatype constructor, selector, or tester";
}
int isTester(Node n);
/**
* Get the index of a constructor or tester in its datatype, or the
- * index of a selector in its constructor. (Zero is always the
+ * index of a selector or updater in its constructor. (Zero is always the
* first index.)
*/
size_t indexOf(Node n);
/**
- * Get the index of constructor corresponding to selector.
+ * Get the index of constructor corresponding to selector or updater.
* (Zero is always the first index.)
*/
size_t cindexOf(Node n);
ASSERT_TRUE(listType.mkGroundTerm().getType() == listType);
}
+TEST_F(TestUtilBlackDatatype, listIntUpdate)
+{
+ DType list("list");
+ TypeNode integerType = d_nodeManager->integerType();
+
+ std::shared_ptr<DTypeConstructor> cons =
+ std::make_shared<DTypeConstructor>("cons");
+ cons->addArg("car", integerType);
+ cons->addArgSelf("cdr");
+ list.addConstructor(cons);
+
+ std::shared_ptr<DTypeConstructor> nil =
+ std::make_shared<DTypeConstructor>("nil");
+ list.addConstructor(nil);
+
+ TypeNode listType = d_nodeManager->mkDatatypeType(list);
+ const DType& ldt = listType.getDType();
+ Node updater = ldt[0][0].getUpdater();
+ Node gt = listType.mkGroundTerm();
+ Node zero = d_nodeManager->mkConst(Rational(0));
+ Node truen = d_nodeManager->mkConst(true);
+ // construct an update term
+ Node uterm = d_nodeManager->mkNode(kind::APPLY_DT_UPDATE, updater, gt, zero);
+ // construct a non well-formed update term
+ ASSERT_THROW(d_nodeManager->mkNode(kind::APPLY_DT_UPDATE, updater, gt, truen)
+ .getType(true),
+ TypeCheckingExceptionPrivate);
+}
+
TEST_F(TestUtilBlackDatatype, mutual_list_trees1)
{
/* Create two mutual datatypes corresponding to this definition