Example #1
0
// Search for a sequence of conversions from expression `e` to a destination
// category c and type t.
Expr&
standard_conversion(Context& cxt, Expr& e, Type& t)
{
  // No conversions are required if the types are the same.
  if (is_equivalent(e.type(), t))
    return e;

  // Try a categorical conversion.
  Expr& c1 = convert_category(cxt, e, t);
  if (is_equivalent(c1.type(), t))
    return c1;

  // Try a value conversion.
  Expr& c2 = convert_value(cxt, c1, t);
  if (is_equivalent(c2.type(), t))
    return c2;

  // Try a qualification adjustment.
  Expr& c3 = convert_qualifier(cxt, c2, t);
  if (is_equivalent(c3.type(), t))
    return c3;

  error(cxt, "cannot convert '{}' (type '{}') to '{}'", e, e.type(), t);
  throw Type_error();
}
Example #2
0
void
test_types()
{

  Type& t1 = *new Void_type();
  Type& t2 = *new Boolean_type();
  Type& t3 = *new Integer_type();
  Type& t4 = *new Float_type();

  assert(is_equivalent(t1, t1));
  assert(is_equivalent(t2, t2));
  assert(is_equivalent(t3, t3));
  assert(is_equivalent(t4, t4));
  assert(!is_equivalent(t1, t2));
}
Example #3
0
bool action::is_compatible(action const & a) const {
    if (is_equivalent(a))
        return true;
    auto k1 = kind();
    auto k2 = a.kind();
    return is_compatible_core(k1, k2) || is_compatible_core(k2, k1);
}
Example #4
0
// Perform the usual arithmetic conversions on `e1` and `e2`. This
// tries to find a common type for `e1` and `e2` and convert both
// expressions to that type.
//
// NOTE: In C++ lvalue-to-rvalue conversions are required on a
// per-expression basis, independently of converting to a common
// type. Also, non-user-defined types are unqualified prior to
// analysis. It would be easier if we found an absolute common
// type and then instantiated a declaration suitable for overload
// resolution. Maybe.
//
// TODO: Handle conversions for character types (or promote to a
// corresponding integer type?).
//
// TODO: How does bool work with this set of conversions? Promote
// bool to int?
//
// TODO: Can we unify this with the common type required by the
// conditional expression? Note that the arithmetic version converts
// to values, and the conditional expression can retain references.
Expr_pair
arithmetic_conversions(Context& cxt, Expr& e1, Expr& e2)
{
  // If the types are the same, no conversions are applied.
  if (is_equivalent(e1.type(), e2.type()))
    return {e1, e2};

  // If either operand has floating point type, convert to the type
  // with the greatest precision.
  //
  // TODO: Why isn't convert_to_common_float symmetric? This seems like
  // an issue.
  if (has_floating_point_type(e1))
    return convert_to_common_float(cxt, e2, e1);
  if (has_floating_point_type(e2))
    return convert_to_common_float(cxt, e1, e2);

  // If both operands have integer type, the following rules apply.
  if (has_integer_type(e1) && has_integer_type(e2))
    return convert_to_common_int(cxt, e1, e2);

  // TODO: No conversion from e1 to e2.
  error(cxt, "no usual arithmetic conversion of '{}' and '{}'", e1, e2);
  throw Type_error();
}
Example #5
0
inline bool
is_equivalent(List<T> const& a, List<T> const& b)
{
  auto cmp = [](T const& x, T const& y) {
    return is_equivalent(x, y);
  };
  return std::equal(a.begin(), a.end(), b.begin(), b.end(), cmp);
}
Example #6
0
// Try to find a standard conversion sequence from a source
// expression `e` and a destination type `t`.
//
// FIXME: Should `t` be an object type? That is we should perform
// conversions iff we can declare an object of type T?
Expr&
standard_conversion(Expr& e, Type& t)
{
  Expr& c1 = convert_category(e, t);
  if (is_equivalent(c1.type(), t))
    return c1;

  Expr& c2 = convert_value(c1, t);
  if (is_equivalent(c2.type(), t))
    return c2;

  Expr& c3 = convert_qualifier(c2, t);
  if (is_equivalent(c3.type(), t))
    return c3;

  // FIXME: Emit better diagnostics.
  throw std::runtime_error("conversion error");
}
Example #7
0
// FIXME: Handle precedence.
Prop const*
simplify(Or const* p)
{
  Prop const* p1 = simplify(p->left());
  Prop const* p2 = simplify(p->right());
  
  // idempotence: p or p <=> p
  if (is_equivalent(p1, p2))
    return p1;

  // absorption: p or (p and q) <=> p
  //
  // Because 'and' and 'or' are comutative, we need to 
  // check a bunch of different combinations.
  if (And const* p3 = as<And>(p2)) {
    // p or (p and q)
    if (is_equivalent(p1, p3->left()))
      return p1;    
    // p or (q and p)
    if (is_equivalent(p1, p3->right()))
      return p1;
  } else if (And const* p3 = as<And>(p1)) {
    // (p and q) or p
    if (is_equivalent(p2, p3->left()))
      return p2;    
    // (q and p) or p
    if (is_equivalent(p2, p3->right()))
      return p2;
  }

  // contraction: p or (p or q) <=> p or q
  if (Or const* p3 = as<Or>(p2)) {
    // p or (p or q)
    if (is_equivalent(p1, p3->left()))
      return p3;
    // p or (q or p)
    if (is_equivalent(p1, p3->right()))
      return p3;
  } else if (Or const* p3 = as<Or>(p1)) {
    // (p or q) or p
    if (is_equivalent(p2, p3->left()))
      return p3;
    // (q or p) or p
    if (is_equivalent(p2, p3->right()))
      return p3;
  }

  return new Or(p1, p2);
}
Example #8
0
// Returns true if a subsumes c.
//
// TODO: How do I know when I've exhuasted all opportunities.
bool
subsumes(Context& cxt, Cons const& a, Cons const& c)
{
  // Check the easy cases before setting up a proof.
  if (is_equivalent(a, c))
    return true;
  if (is_memoized(cxt, a, c))
    return true;

  // Alas... no quick check. We have to prove the implication.
  Proof p(cxt);
  Sequent& s = p.front();
  s.antecedents().insert(a);
  s.consequents().insert(c);
  std::cout << "INIT: " << s << '\n';

  // NOTE: I wonder if the current load implementation is
  // too aggressive when expanding concepts.

  // Initially load consquents.
  load_consequents(p);

  // Continue manipulating the proof state until we know that
  // the implication is valid or not.
  int n = 1;
  Validation v = valid_proof;
  do {
    // Load a round of antecedents.
    load_antecedents(p);

    std::cout << "STEP " << n << ": " << p.front() << '\n';

    // Having done that, determine if the proof is valid (or not).
    // In either case, we can stop.
    v = check_proof(p);
    std::cout << "VALID? " << v << '\n';
    if (v == valid_proof)
      return true;
    if (v == invalid_proof)
      return false;

    // Otherwise, select a term in each goal to expand.
    expand_proof(p);
    ++n;

    // TODO: Actually diagnose implementation limits. Note that the
    // real limiting factor is going to be the goal size, not
    // the step count.
    if (p.size() > 32)
      throw Limitation_error("exceeded proof subgoal limit");
    if (n > 1024)
      throw Limitation_error("exceeded proof step limit");
  } while (v == incomplete_proof);

  return false;
}
Example #9
0
Prop const*
simplify(And const* p)
{
  Prop const* p1 = simplify(p->left());
  Prop const* p2 = simplify(p->right());
  
  // idempotence: p and p <=> p
  if (is_equivalent(p1, p2))
    return p1;

  // absorption: p and (p or q) <=> p
  if (Or const* p3 = as<Or>(p2)) {
    // p and (p or q)
    if (is_equivalent(p1, p3->left()))
      return p1;    
    // p and (q or p)
    if (is_equivalent(p1, p3->right()))
      return p1;
  } else if (Or const* p3 = as<Or>(p1)) {
    // (p or q) and p
    if (is_equivalent(p2, p3->left()))
      return p2;
    // (q or p) and p
    if (is_equivalent(p2, p3->right()))
      return p2;
  }

  // contraction: p and (p and q) <=> p and q
  if (And const* p3 = as<And>(p2)) {
    // p and (p and q)
    if (is_equivalent(p1, p3->left()))
      return p3;
    // p and (q and p)
    if (is_equivalent(p1, p3->right()))
      return p3;
  } else if (And const* p3 = as<And>(p1)) {
    // (p and q) and p
    if (is_equivalent(p2, p3->left()))
      return p3;
    // (q and p) and p
    if (is_equivalent(p2, p3->right()))
      return p3;
  }
  
  return new And(p1, p2);
}
Example #10
0
// An expression is admissible for a disjinction of assumptions if
// support is found both the left and right operand.
inline Expr*
admit_disjunction_expr(Context& cxt, Disjunction_cons& c, Expr& e)
{
  if (Expr* e1 = admit_expression(cxt, c.left(), e)) {
    if (Expr* e2 = admit_expression(cxt, c.right(), e)) {
      if (!is_equivalent(e1->type(), e2->type()))
        throw Translation_error(cxt, "multiple types deduced for '{}'", e);
      return e1;
    }
  }
  return nullptr;
}
Example #11
0
// Perform the usual arithmetic conversions on `e1` and `e2`. This
// tries to find a common type for `e1` and `e2` and convert both
// expressions to that type.
//
// NOTE: In C++ lvalue-to-rvalue conversions are required on a
// per-expression basis, independently of converting to a common
// type. Also, non-user-defined types are unqualified prior to
// analysis. It would be easier if we found an absolute common
// type and then instantiated a declaration suitable for overload
// resolution. Maybe.
//
// TODO: Handle conversions for character types (or promote to a
// corresponding integer type?).
//
// TODO: How does bool work with this set of conversions? Promote
// bool to int?
//
// TODO: Can we unify this with the common type required by the
// conditional expression? Note that the arithmetic version converts
// to values, and the conditional expression can retain references.
Expr_pair
arithmetic_conversion(Expr& e1, Expr& e2)
{
  // If the types are the same, no conversions are applied.
  if (is_equivalent(e1.type(), e2.type()))
    return {e1, e2};

  // If either operand has floating point type, convert to the type
  // with the greatest precision.
  if (has_floating_point_type(e1))
    return convert_to_common_float(e2, e1);
  if (has_floating_point_type(e2))
    return convert_to_common_float(e1, e2);

  // If both oerands have integer type, the following rules apply.
  if (has_integer_type(e1) && has_integer_type(e2))
    return convert_to_common_int(e1, e2);

  // TODO: No conversion from e1 to e2.
  throw std::runtime_error("incompatible types");
}
Example #12
0
Expr*
admit_usage_conv(Context& cxt, Conversion_cons& c, Expr& e, Type& t)
{
  struct fn
  {
    Context&         cxt;
    Conversion_cons& c;
    Expr* operator()(Expr& e)        { banjo_unhandled_case(e); }
    Expr* operator()(Binary_expr& e) { return admit_binary_conv(cxt, c, e); }
  };

  // An expression of a different kind prove admissibility.
  Expr& e2 = c.expression();
  if (typeid(e2) != typeid(e))
    return nullptr;

  // If the expression's type is not equivalent to t, this constraint
  // does not prove admissibility.
  if (!is_equivalent(c.type(), t))
    return nullptr;

  return apply(e, fn{cxt, c});
}
Example #13
0
 bool operator()(Float_type const& a)     { return is_equivalent(a, cast<Float_type>(b)); }
Example #14
0
 bool operator()(Void_type const& a)      { return is_equivalent(a, cast<Void_type>(b)); }
Example #15
0
 bool operator()(Synthetic_type const& a) { return is_equivalent(a, cast<Synthetic_type>(b)); }
Example #16
0
 bool operator()(Typename_type const& a)  { return is_equivalent(a, cast<Typename_type>(b)); }
Example #17
0
 bool operator()(Enum_type const& a)      { return is_equivalent(a, cast<Enum_type>(b)); }
Example #18
0
 bool operator()(Type const& a)           { return is_equivalent(a, b); }
Example #19
0
// Returns true when type t1 differs from type t2.
inline bool
is_different(Type const& t1, Type const& t2)
{
  return !is_equivalent(t1, t2);
}
Example #20
0
 bool operator()(Boolean_type const& a)   { return is_equivalent(a, cast<Boolean_type>(b)); }
Example #21
0
 bool operator()(T const& a, T const& b) const
 {
   return is_equivalent(a, b);
 }
Example #22
0
 bool operator()(Integer_type const& a)   { return is_equivalent(a, cast<Integer_type>(b)); }
Example #23
0
vm_obj level_eqv(vm_obj const & o1, vm_obj const & o2) {
    return mk_vm_bool(is_equivalent(to_level(o1), to_level(o2)));
}
Example #24
0
 bool operator()(Declauto_type const& a)  { return is_equivalent(a, cast<Declauto_type>(b)); }
Example #25
0
 bool operator()(List<T> const& a, List<T> const& b) const
 {
   return is_equivalent(a, b);
 }
Example #26
0
 bool operator()(Reference_type const& a) { return is_equivalent(a, cast<Reference_type>(b)); }
Example #27
0
 bool operator()(Class_type const& a)     { return is_equivalent(a, cast<Class_type>(b)); }
Example #28
0
 bool operator()(Union_type const& a)     { return is_equivalent(a, cast<Union_type>(b)); }
Example #29
0
 bool operator()(T const* a, T const* b) const
 {
   return is_equivalent(*a, *b);
 }
Example #30
0
// Two tuple types are similar only when they are equivalent.
//
// TODO: There is probably a valid generalization of this. Maybe
// two tuple types are similar if all their elements are similar?
bool
is_similar(Tuple_type const& a, Tuple_type const& b)
{
  return is_equivalent(a, b);
}