mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			420 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			420 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
    This file is part of TON Blockchain Library.
 | 
						|
 | 
						|
    TON Blockchain Library is free software: you can redistribute it and/or modify
 | 
						|
    it under the terms of the GNU Lesser General Public License as published by
 | 
						|
    the Free Software Foundation, either version 2 of the License, or
 | 
						|
    (at your option) any later version.
 | 
						|
 | 
						|
    TON Blockchain Library 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 Lesser General Public License for more details.
 | 
						|
 | 
						|
    You should have received a copy of the GNU Lesser General Public License
 | 
						|
    along with TON Blockchain Library.  If not, see <http://www.gnu.org/licenses/>.
 | 
						|
 | 
						|
    Copyright 2017-2020 Telegram Systems LLP
 | 
						|
*/
 | 
						|
#include "func.h"
 | 
						|
 | 
						|
namespace funC {
 | 
						|
 | 
						|
/*
 | 
						|
 * 
 | 
						|
 *   TYPE EXPRESSIONS
 | 
						|
 * 
 | 
						|
 */
 | 
						|
 | 
						|
int TypeExpr::holes = 0, TypeExpr::type_vars = 0;  // not thread safe, but it is ok for now
 | 
						|
 | 
						|
void TypeExpr::compute_width() {
 | 
						|
  switch (constr) {
 | 
						|
    case te_Atomic:
 | 
						|
    case te_Map:
 | 
						|
      minw = maxw = 1;
 | 
						|
      break;
 | 
						|
    case te_Tensor:
 | 
						|
      minw = maxw = 0;
 | 
						|
      for (TypeExpr* arg : args) {
 | 
						|
        minw += arg->minw;
 | 
						|
        maxw += arg->maxw;
 | 
						|
      }
 | 
						|
      if (minw > w_inf) {
 | 
						|
        minw = w_inf;
 | 
						|
      }
 | 
						|
      if (maxw > w_inf) {
 | 
						|
        maxw = w_inf;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case te_Tuple:
 | 
						|
      minw = maxw = 1;
 | 
						|
      for (TypeExpr* arg : args) {
 | 
						|
        arg->compute_width();
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case te_Indirect:
 | 
						|
      minw = args[0]->minw;
 | 
						|
      maxw = args[0]->maxw;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      minw = 0;
 | 
						|
      maxw = w_inf;
 | 
						|
      break;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool TypeExpr::recompute_width() {
 | 
						|
  switch (constr) {
 | 
						|
    case te_Tensor:
 | 
						|
    case te_Indirect: {
 | 
						|
      int min = 0, max = 0;
 | 
						|
      for (TypeExpr* arg : args) {
 | 
						|
        min += arg->minw;
 | 
						|
        max += arg->maxw;
 | 
						|
      }
 | 
						|
      if (min > maxw || max < minw) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      if (min > w_inf) {
 | 
						|
        min = w_inf;
 | 
						|
      }
 | 
						|
      if (max > w_inf) {
 | 
						|
        max = w_inf;
 | 
						|
      }
 | 
						|
      if (minw < min) {
 | 
						|
        minw = min;
 | 
						|
      }
 | 
						|
      if (maxw > max) {
 | 
						|
        maxw = max;
 | 
						|
      }
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    case te_Tuple: {
 | 
						|
      for (TypeExpr* arg : args) {
 | 
						|
        if (arg->minw > 1 || arg->maxw < 1 || arg->minw > arg->maxw) {
 | 
						|
          return false;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    default:
 | 
						|
      return false;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
int TypeExpr::extract_components(std::vector<TypeExpr*>& comp_list) {
 | 
						|
  if (constr != te_Indirect && constr != te_Tensor) {
 | 
						|
    comp_list.push_back(this);
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
  int res = 0;
 | 
						|
  for (TypeExpr* arg : args) {
 | 
						|
    res += arg->extract_components(comp_list);
 | 
						|
  }
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
TypeExpr* TypeExpr::new_map(TypeExpr* from, TypeExpr* to) {
 | 
						|
  return new TypeExpr{te_Map, std::vector<TypeExpr*>{from, to}};
 | 
						|
}
 | 
						|
 | 
						|
void TypeExpr::replace_with(TypeExpr* te2) {
 | 
						|
  if (te2 == this) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  constr = te_Indirect;
 | 
						|
  value = 0;
 | 
						|
  minw = te2->minw;
 | 
						|
  maxw = te2->maxw;
 | 
						|
  args.clear();
 | 
						|
  args.push_back(te2);
 | 
						|
}
 | 
						|
 | 
						|
bool TypeExpr::remove_indirect(TypeExpr*& te, TypeExpr* forbidden) {
 | 
						|
  assert(te);
 | 
						|
  while (te->constr == te_Indirect) {
 | 
						|
    te = te->args[0];
 | 
						|
  }
 | 
						|
  if (te->constr == te_Unknown) {
 | 
						|
    return te != forbidden;
 | 
						|
  }
 | 
						|
  bool res = true;
 | 
						|
  for (auto& x : te->args) {
 | 
						|
    res &= remove_indirect(x, forbidden);
 | 
						|
  }
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
bool TypeExpr::remove_forall(TypeExpr*& te) {
 | 
						|
  assert(te);
 | 
						|
  if (te->constr != te_ForAll) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  assert(te->args.size() >= 1);
 | 
						|
  std::vector<TypeExpr*> new_vars;
 | 
						|
  for (std::size_t i = 1; i < te->args.size(); i++) {
 | 
						|
    new_vars.push_back(new_hole(1));
 | 
						|
  }
 | 
						|
  TypeExpr* te2 = te;
 | 
						|
  // std::cerr << "removing universal quantifier in " << te << std::endl;
 | 
						|
  te = te->args[0];
 | 
						|
  remove_forall_in(te, te2, new_vars);
 | 
						|
  // std::cerr << "-> " << te << std::endl;
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
bool TypeExpr::remove_forall_in(TypeExpr*& te, TypeExpr* te2, const std::vector<TypeExpr*>& new_vars) {
 | 
						|
  assert(te);
 | 
						|
  assert(te2 && te2->constr == te_ForAll);
 | 
						|
  if (te->constr == te_Var) {
 | 
						|
    for (std::size_t i = 0; i < new_vars.size(); i++) {
 | 
						|
      if (te == te2->args[i + 1]) {
 | 
						|
        te = new_vars[i];
 | 
						|
        return true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  if (te->constr == te_ForAll) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  if (te->args.empty()) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  auto te1 = new TypeExpr(*te);
 | 
						|
  bool res = false;
 | 
						|
  for (auto& arg : te1->args) {
 | 
						|
    res |= remove_forall_in(arg, te2, new_vars);
 | 
						|
  }
 | 
						|
  if (res) {
 | 
						|
    te = te1;
 | 
						|
  } else {
 | 
						|
    delete te1;
 | 
						|
  }
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
void TypeExpr::show_width(std::ostream& os) {
 | 
						|
  os << minw;
 | 
						|
  if (maxw != minw) {
 | 
						|
    os << "..";
 | 
						|
    if (maxw < w_inf) {
 | 
						|
      os << maxw;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
std::ostream& operator<<(std::ostream& os, TypeExpr* type_expr) {
 | 
						|
  if (!type_expr) {
 | 
						|
    return os << "(null-type-ptr)";
 | 
						|
  }
 | 
						|
  return type_expr->print(os);
 | 
						|
}
 | 
						|
 | 
						|
std::ostream& TypeExpr::print(std::ostream& os, int lex_level) {
 | 
						|
  switch (constr) {
 | 
						|
    case te_Unknown:
 | 
						|
      return os << "??" << value;
 | 
						|
    case te_Var:
 | 
						|
      if (value >= -26 && value < 0) {
 | 
						|
        return os << "_" << (char)(91 + value);
 | 
						|
      } else if (value >= 0 && value < 26) {
 | 
						|
        return os << (char)(65 + value);
 | 
						|
      } else {
 | 
						|
        return os << "TVAR" << value;
 | 
						|
      }
 | 
						|
    case te_Indirect:
 | 
						|
      return os << args[0];
 | 
						|
    case te_Atomic: {
 | 
						|
      switch (value) {
 | 
						|
        case _Int:
 | 
						|
          return os << "int";
 | 
						|
        case _Cell:
 | 
						|
          return os << "cell";
 | 
						|
        case _Slice:
 | 
						|
          return os << "slice";
 | 
						|
        case _Builder:
 | 
						|
          return os << "builder";
 | 
						|
        case _Cont:
 | 
						|
          return os << "cont";
 | 
						|
        case _Tuple:
 | 
						|
          return os << "tuple";
 | 
						|
        case _Type:
 | 
						|
          return os << "type";
 | 
						|
        default:
 | 
						|
          return os << "atomic-type-" << value;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    case te_Tensor: {
 | 
						|
      if (lex_level > -127) {
 | 
						|
        os << "(";
 | 
						|
      }
 | 
						|
      auto c = args.size();
 | 
						|
      if (c) {
 | 
						|
        for (const auto& x : args) {
 | 
						|
          x->print(os);
 | 
						|
          if (--c) {
 | 
						|
            os << ", ";
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
      if (lex_level > -127) {
 | 
						|
        os << ")";
 | 
						|
      }
 | 
						|
      return os;
 | 
						|
    }
 | 
						|
    case te_Tuple: {
 | 
						|
      os << "[";
 | 
						|
      auto c = args.size();
 | 
						|
      if (c == 1 && args[0]->constr == te_Tensor) {
 | 
						|
        args[0]->print(os, -127);
 | 
						|
      } else if (c) {
 | 
						|
        for (const auto& x : args) {
 | 
						|
          x->print(os);
 | 
						|
          if (--c) {
 | 
						|
            os << ", ";
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
      return os << "]";
 | 
						|
    }
 | 
						|
    case te_Map: {
 | 
						|
      assert(args.size() == 2);
 | 
						|
      if (lex_level > 0) {
 | 
						|
        os << "(";
 | 
						|
      }
 | 
						|
      args[0]->print(os, 1);
 | 
						|
      os << " -> ";
 | 
						|
      args[1]->print(os);
 | 
						|
      if (lex_level > 0) {
 | 
						|
        os << ")";
 | 
						|
      }
 | 
						|
      return os;
 | 
						|
    }
 | 
						|
    case te_ForAll: {
 | 
						|
      assert(args.size() >= 1);
 | 
						|
      if (lex_level > 0) {
 | 
						|
        os << '(';
 | 
						|
      }
 | 
						|
      os << "Forall ";
 | 
						|
      for (std::size_t i = 1; i < args.size(); i++) {
 | 
						|
        os << (i > 1 ? ' ' : '(');
 | 
						|
        args[i]->print(os);
 | 
						|
      }
 | 
						|
      os << ") ";
 | 
						|
      args[0]->print(os);
 | 
						|
      if (lex_level > 0) {
 | 
						|
        os << ')';
 | 
						|
      }
 | 
						|
      return os;
 | 
						|
    }
 | 
						|
    default:
 | 
						|
      return os << "unknown-type-expr-" << constr;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void UnifyError::print_message(std::ostream& os) const {
 | 
						|
  os << "cannot unify type " << te1 << " with " << te2;
 | 
						|
  if (!msg.empty()) {
 | 
						|
    os << ": " << msg;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
std::ostream& operator<<(std::ostream& os, const UnifyError& ue) {
 | 
						|
  ue.print_message(os);
 | 
						|
  return os;
 | 
						|
}
 | 
						|
 | 
						|
std::string UnifyError::message() const {
 | 
						|
  std::ostringstream os;
 | 
						|
  UnifyError::print_message(os);
 | 
						|
  return os.str();
 | 
						|
}
 | 
						|
 | 
						|
void check_width_compat(TypeExpr* te1, TypeExpr* te2) {
 | 
						|
  if (te1->minw > te2->maxw || te2->minw > te1->maxw) {
 | 
						|
    std::ostringstream os{"cannot unify types of widths ", std::ios_base::ate};
 | 
						|
    te1->show_width(os);
 | 
						|
    os << " and ";
 | 
						|
    te2->show_width(os);
 | 
						|
    throw UnifyError{te1, te2, os.str()};
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void check_update_widths(TypeExpr* te1, TypeExpr* te2) {
 | 
						|
  check_width_compat(te1, te2);
 | 
						|
  te1->minw = te2->minw = std::max(te1->minw, te2->minw);
 | 
						|
  te1->maxw = te2->maxw = std::min(te1->maxw, te2->maxw);
 | 
						|
  assert(te1->minw <= te1->maxw);
 | 
						|
}
 | 
						|
 | 
						|
void unify(TypeExpr*& te1, TypeExpr*& te2) {
 | 
						|
  assert(te1 && te2);
 | 
						|
  // std::cerr << "unify( " << te1 << " , " << te2 << " )\n";
 | 
						|
  while (te1->constr == TypeExpr::te_Indirect) {
 | 
						|
    te1 = te1->args[0];
 | 
						|
  }
 | 
						|
  while (te2->constr == TypeExpr::te_Indirect) {
 | 
						|
    te2 = te2->args[0];
 | 
						|
  }
 | 
						|
  if (te1 == te2) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (te1->constr == TypeExpr::te_ForAll) {
 | 
						|
    TypeExpr* te = te1;
 | 
						|
    if (!TypeExpr::remove_forall(te)) {
 | 
						|
      throw UnifyError{te1, te2, "cannot remove universal type quantifier while performing type unification"};
 | 
						|
    }
 | 
						|
    unify(te, te2);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (te2->constr == TypeExpr::te_ForAll) {
 | 
						|
    TypeExpr* te = te2;
 | 
						|
    if (!TypeExpr::remove_forall(te)) {
 | 
						|
      throw UnifyError{te2, te1, "cannot remove universal type quantifier while performing type unification"};
 | 
						|
    }
 | 
						|
    unify(te1, te);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (te1->constr == TypeExpr::te_Unknown) {
 | 
						|
    if (te2->constr == TypeExpr::te_Unknown) {
 | 
						|
      assert(te1->value != te2->value);
 | 
						|
    }
 | 
						|
    if (!TypeExpr::remove_indirect(te2, te1)) {
 | 
						|
      throw UnifyError{te1, te2, "type unification results in an infinite cyclic type"};
 | 
						|
    }
 | 
						|
    check_update_widths(te1, te2);
 | 
						|
    te1->replace_with(te2);
 | 
						|
    te1 = te2;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (te2->constr == TypeExpr::te_Unknown) {
 | 
						|
    if (!TypeExpr::remove_indirect(te1, te2)) {
 | 
						|
      throw UnifyError{te2, te1, "type unification results in an infinite cyclic type"};
 | 
						|
    }
 | 
						|
    check_update_widths(te2, te1);
 | 
						|
    te2->replace_with(te1);
 | 
						|
    te2 = te1;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (te1->constr != te2->constr || te1->value != te2->value || te1->args.size() != te2->args.size()) {
 | 
						|
    throw UnifyError{te1, te2};
 | 
						|
  }
 | 
						|
  for (std::size_t i = 0; i < te1->args.size(); i++) {
 | 
						|
    unify(te1->args[i], te2->args[i]);
 | 
						|
  }
 | 
						|
  if (te1->constr == TypeExpr::te_Tensor) {
 | 
						|
    if (!te1->recompute_width()) {
 | 
						|
      throw UnifyError{te1, te2, "type unification incompatible with known width of first type"};
 | 
						|
    }
 | 
						|
    if (!te2->recompute_width()) {
 | 
						|
      throw UnifyError{te2, te1, "type unification incompatible with known width of first type"};
 | 
						|
    }
 | 
						|
    check_update_widths(te1, te2);
 | 
						|
  }
 | 
						|
  te1->replace_with(te2);
 | 
						|
  te1 = te2;
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace funC
 |