/* Tuplr Unification * Copyright (C) 2008-2009 David Robillard * * Tuplr is free software: you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * Tuplr is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General * Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Tuplr. If not, see . */ /** @file * @brief Unify type constraints */ #include #include "tuplr.hpp" /** Build a type substitution for calling a generic function type * with a specific set of argument types */ Subst TEnv::buildSubst(AType* genericT, const AType& argsT) { Subst subst; // Build substitution to apply to generic type const ATuple* genericProtT = (*(genericT->begin() + 1))->as(); ATuple::const_iterator g = genericProtT->begin(); AType::const_iterator a = argsT.begin(); for (; a != argsT.end(); ++a, ++g) { const AType* genericArgT = (*g)->to(); AType* callArgT = (*a)->to(); if (callArgT->kind == AType::EXPR) { assert(genericArgT->kind == AType::EXPR); ATuple::const_iterator gi = genericArgT->begin(); ATuple::const_iterator ci = callArgT->begin(); for (; gi != genericArgT->end(); ++gi, ++ci) { const AType* gT = (*gi)->to(); AType* aT = (*ci)->to(); if (gT && aT) subst.add(gT, aT); } } else { subst.add(genericArgT, callArgT); } } return subst; } void Constraints::constrain(TEnv& tenv, const AST* o, AType* t) { assert(o); assert(t); assert(!o->to()); push_back(Constraint(tenv.var(o), t, o->loc)); } static void substitute(ATuple* tup, const AST* from, AST* to) { if (!tup) return; FOREACHP(ATuple::iterator, i, tup) if (**i == *from) *i = to; else if (*i != to) substitute((*i)->to(), from, to); } /// Compose two substitutions (TAPL 22.1.1) Subst Subst::compose(const Subst& delta, const Subst& gamma) { Subst r; for (Subst::const_iterator g = gamma.begin(); g != gamma.end(); ++g) { Subst::const_iterator d = delta.find(g->second); r.add(g->first, ((d != delta.end()) ? d : g)->second); } for (Subst::const_iterator d = delta.begin(); d != delta.end(); ++d) { if (gamma.find(d->first) == gamma.end()) r.add(d->first, d->second); } return r; } /// Replace all occurrences of @a s with @a t void Constraints::replace(AType* s, AType* t) { for (Constraints::iterator c = begin(); c != end();) { Constraints::iterator next = c; ++next; if (*c->first == *s) c->first = t; if (*c->second == *s) c->second = t; substitute(c->first, s, t); substitute(c->second, s, t); c = next; } } /// Unify a type constraint set (TAPL 22.4) Subst unify(const Constraints& constraints) { if (constraints.empty()) return Subst(); AType* s = constraints.begin()->first; AType* t = constraints.begin()->second; Constraints cp = constraints; cp.erase(cp.begin()); if (*s == *t) { return unify(cp); } else if (s->kind == AType::VAR && !t->contains(s)) { cp.replace(s, t); return Subst::compose(unify(cp), Subst(s, t)); } else if (t->kind == AType::VAR && !s->contains(t)) { cp.replace(t, s); return Subst::compose(unify(cp), Subst(t, s)); } else if (s->kind == AType::EXPR && t->kind == AType::EXPR) { AType::iterator si = s->begin() + 1; AType::iterator ti = t->begin() + 1; for (; si != s->end() && ti != t->end(); ++si, ++ti) { AType* st = (*si)->as(); AType* tt = (*ti)->as(); if (st->kind == AType::DOTS || tt->kind == AType::DOTS) return unify(cp); else cp.push_back(Constraint(st, tt, st->loc)); } if (si == s->end() && ti == t->end() || (*ti)->as()->kind == AType::DOTS || (*si)->as()->kind == AType::DOTS) return unify(cp); } throw Error(s->loc ? s->loc : t->loc, (format("type is `%1%' but should be `%2%'") % s->str() % t->str()).str()); }