Пример #1
0
bool is_concrete(ast_t* type)
{
  switch(ast_id(type))
  {
    case TK_UNIONTYPE:
    case TK_TUPLETYPE:
      return false;

    case TK_ISECTTYPE:
    {
      ast_t* child = ast_child(type);

      while(child != NULL)
      {
        if(is_concrete(child))
          return true;

        child = ast_sibling(child);
      }

      return false;
    }

    case TK_NOMINAL:
    {
      ast_t* def = (ast_t*)ast_data(type);

      switch(ast_id(def))
      {
        case TK_INTERFACE:
        case TK_TRAIT:
          return false;

        case TK_PRIMITIVE:
        case TK_CLASS:
        case TK_ACTOR:
          return true;

        default: {}
      }
      break;
    }

    case TK_TYPEPARAMREF:
    {
      ast_t* def = (ast_t*)ast_data(type);
      ast_t* constraint = ast_childidx(def, 1);

      return is_constructable(constraint);
    }

    case TK_ARROW:
      return is_concrete(ast_childidx(type, 1));

    default: {}
  }

  assert(0);
  return false;
}
Пример #2
0
void change_visibility(DexMethod* method) {
  auto code = method->get_code();
  always_assert(code != nullptr);

  editable_cfg_adapter::iterate(code, [](MethodItemEntry& mie) {
    auto insn = mie.insn;

    if (insn->has_field()) {
      auto cls = type_class(insn->get_field()->get_class());
      if (cls != nullptr && !cls->is_external()) {
        set_public(cls);
      }
      auto field =
          resolve_field(insn->get_field(), is_sfield_op(insn->opcode())
              ? FieldSearch::Static : FieldSearch::Instance);
      if (field != nullptr && field->is_concrete()) {
        set_public(field);
        set_public(type_class(field->get_class()));
        // FIXME no point in rewriting opcodes in the method
        insn->set_field(field);
      }
    } else if (insn->has_method()) {
      auto cls = type_class(insn->get_method()->get_class());
      if (cls != nullptr && !cls->is_external()) {
        set_public(cls);
      }
      auto current_method = resolve_method(
          insn->get_method(), opcode_to_search(insn));
      if (current_method != nullptr && current_method->is_concrete()) {
        set_public(current_method);
        set_public(type_class(current_method->get_class()));
        // FIXME no point in rewriting opcodes in the method
        insn->set_method(current_method);
      }
    } else if (insn->has_type()) {
      auto type = insn->get_type();
      auto cls = type_class(type);
      if (cls != nullptr && !cls->is_external()) {
        set_public(cls);
      }
    }
    return editable_cfg_adapter::LOOP_CONTINUE;
  });

  std::vector<DexType*> types;
  if (code->editable_cfg_built()) {
    code->cfg().gather_catch_types(types);
  } else {
    code->gather_catch_types(types);
  }
  for (auto type : types) {
    auto cls = type_class(type);
    if (cls != nullptr && !cls->is_external()) {
      set_public(cls);
    }
  }
}
Пример #3
0
static bool validate_sget(DexMethod* context, DexOpcodeField* opfield) {
  auto opcode = opfield->opcode();
  switch (opcode) {
  case OPCODE_SGET_WIDE:
    unhandled_inline++;
    return false;
  case OPCODE_SGET:
  case OPCODE_SGET_BOOLEAN:
  case OPCODE_SGET_BYTE:
  case OPCODE_SGET_CHAR:
  case OPCODE_SGET_SHORT:
    return true;
  default:
    auto field = resolve_field(opfield->field(), FieldSearch::Static);
    always_assert_log(field->is_concrete(), "Must be a concrete field");
    auto value = field->get_static_value();
    always_assert_log(
        false,
        "Unexpected field type in inline_*sget %s for field %s value %s in "
        "method %s\n",
        SHOW(opfield),
        SHOW(field),
        value != nullptr ? value->show().c_str() : "('nullptr')",
        SHOW(context));
  }
  return false;
}
Пример #4
0
void inline_field_values(Scope& fullscope) {
  std::unordered_set<DexField*> inline_field;
  std::unordered_set<DexField*> cheap_inline_field;
  std::vector<DexClass*> scope;
  uint32_t aflags = ACC_STATIC | ACC_FINAL;

  for (auto clazz : fullscope) {
    std::unordered_map<DexField*, bool> blank_statics;
    get_sput_in_clinit(clazz, blank_statics);
    auto sfields = clazz->get_sfields();
    for (auto sfield : sfields) {
      if ((sfield->get_access() & aflags) != aflags) continue;
      if (blank_statics[sfield]) continue;
      auto value = sfield->get_static_value();
      if (value == nullptr && !is_primitive(sfield->get_type())) {
        continue;
      }
      if (value != nullptr && !value->is_evtype_primitive()) {
        continue;
      }
      uint64_t v = value != nullptr ? value->value() : 0;
      if ((v & 0xffff) == v || (v & 0xffff0000) == v) {
        cheap_inline_field.insert(sfield);
      }
      inline_field.insert(sfield);
      scope.push_back(clazz);
    }
  }
  std::vector<std::pair<DexMethod*, DexOpcodeField*>> cheap_rewrites;
  std::vector<std::pair<DexMethod*, DexOpcodeField*>> simple_rewrites;
  walk_opcodes(
      fullscope,
      [](DexMethod* method) { return true; },
      [&](DexMethod* method, DexInstruction* insn) {
        if (insn->has_fields() && is_sfield_op(insn->opcode())) {
          auto fieldop = static_cast<DexOpcodeField*>(insn);
          auto field = resolve_field(fieldop->field(), FieldSearch::Static);
          if (field == nullptr || !field->is_concrete()) return;
          if (inline_field.count(field) == 0) return;
          if (cheap_inline_field.count(field) > 0) {
            cheap_rewrites.push_back(std::make_pair(method, fieldop));
            return;
          }
          simple_rewrites.push_back(std::make_pair(method, fieldop));
        }
      });
  TRACE(FINALINLINE, 1,
          "Method Re-writes Cheap %lu  Simple %lu\n",
          cheap_rewrites.size(),
          simple_rewrites.size());
  for (auto cheapcase : cheap_rewrites) {
    inline_cheap_sget(cheapcase.first, cheapcase.second);
  }
  for (auto simplecase : simple_rewrites) {
    inline_sget(simplecase.first, simplecase.second);
  }
  MethodTransform::sync_all();
}
Пример #5
0
/**
 * Change the visibility of members accessed in a callee as they are moved
 * to the caller context.
 * We make everything public but we could be more precise and only
 * relax visibility as needed.
 */
void MultiMethodInliner::change_visibility(DexMethod* callee) {
  TRACE(MMINL, 6, "checking visibility usage of members in %s\n",
      SHOW(callee));
  for (auto insn : callee->get_code()->get_instructions()) {
    if (insn->has_fields()) {
      auto fop = static_cast<DexOpcodeField*>(insn);
      auto field = fop->field();
      field = resolve_field(field, is_sfield_op(insn->opcode())
          ? FieldSearch::Static : FieldSearch::Instance);
      if (field != nullptr && field->is_concrete()) {
        TRACE(MMINL, 6, "changing visibility of %s.%s %s\n",
            SHOW(field->get_class()), SHOW(field->get_name()),
            SHOW(field->get_type()));
        set_public(field);
        set_public(type_class(field->get_class()));
        fop->rewrite_field(field);
      }
      continue;
    }
    if (insn->has_methods()) {
      auto mop = static_cast<DexOpcodeMethod*>(insn);
      auto method = mop->get_method();
      method = resolver(method, opcode_to_search(insn));
      if (method != nullptr && method->is_concrete()) {
        TRACE(MMINL, 6, "changing visibility of %s.%s: %s\n",
            SHOW(method->get_class()), SHOW(method->get_name()),
            SHOW(method->get_proto()));
        set_public(method);
        set_public(type_class(method->get_class()));
        mop->rewrite_method(method);
      }
      continue;
    }
    if (insn->has_types()) {
      auto type = static_cast<DexOpcodeType*>(insn)->get_type();
      auto cls = type_class(type);
      if (cls != nullptr && !cls->is_external()) {
        TRACE(MMINL, 6, "changing visibility of %s\n", SHOW(type));
        set_public(cls);
      }
      continue;
    }
  }
}
Пример #6
0
void inline_sget(DexMethod* method, DexOpcodeField* opfield) {
  if (!validate_sget(method, opfield)) return;
  auto opcode = OPCODE_CONST;
  auto dest = opfield->dest();
  auto field = resolve_field(opfield->field(), FieldSearch::Static);
  always_assert_log(field->is_concrete(), "Must be a concrete field");
  auto value = field->get_static_value();
  /* FIXME for sget_wide case */
  uint32_t v = value != nullptr ? (uint32_t)value->value() : 0;

  auto newopcode = (new DexInstruction(opcode))->set_dest(dest)->set_literal(v);
  replace_opcode(method, opfield, newopcode);
}
Пример #7
0
void MultiMethodInliner::inline_callees(
    InlineContext& inline_context, std::vector<DexMethod*>& callees) {
  size_t found = 0;
  auto caller = inline_context.caller;
  auto insns = caller->get_code()->get_instructions();

  // walk the caller opcodes collecting all candidates to inline
  // Build a callee to opcode map
  std::vector<std::pair<DexMethod*, DexOpcodeMethod*>> inlinables;
  for (auto insn = insns.begin(); insn != insns.end(); ++insn) {
    if (!is_invoke((*insn)->opcode())) continue;
    auto mop = static_cast<DexOpcodeMethod*>(*insn);
    auto callee = resolver(mop->get_method(), opcode_to_search(*insn));
    if (callee == nullptr) continue;
    if (std::find(callees.begin(), callees.end(), callee) == callees.end()) {
      continue;
    }
    always_assert(callee->is_concrete());
    found++;
    inlinables.push_back(std::make_pair(callee, mop));
    if (found == callees.size()) break;
  }
  if (found != callees.size()) {
    always_assert(found <= callees.size());
    info.not_found += callees.size() - found;
  }

  // attempt to inline all inlinable candidates
  for (auto inlinable : inlinables) {
    auto callee = inlinable.first;
    auto mop = inlinable.second;

    if (!is_inlinable(callee, caller)) continue;

    auto op = mop->opcode();
    if (is_invoke_range(op)) {
      info.invoke_range++;
      continue;
    }

    TRACE(MMINL, 4, "inline %s (%d) in %s (%d)\n",
        SHOW(callee), caller->get_code()->get_registers_size(),
        SHOW(caller),
        callee->get_code()->get_registers_size() -
        callee->get_code()->get_ins_size());
    change_visibility(callee);
    MethodTransform::inline_16regs(inline_context, callee, mop);
    info.calls_inlined++;
    inlined.insert(callee);
  }
}
Пример #8
0
std::unordered_set<DexField*> get_called_field_defs(Scope& scope) {
  std::vector<DexField*> field_refs;
  walk_methods(scope,
               [&](DexMethod* method) { method->gather_fields(field_refs); });
  sort_unique(field_refs);
  /* Okay, now we have a complete list of field refs
   * for this particular dex.  Map to the def actually invoked.
   */
  std::unordered_set<DexField*> field_defs;
  for (auto field_ref : field_refs) {
    auto field_def = resolve_field(field_ref);
    if (field_def == nullptr || !field_def->is_concrete()) continue;
    field_defs.insert(field_def);
  }
  return field_defs;
}
Пример #9
0
static void setup_dwarf(dwarf_t* dwarf, dwarf_meta_t* meta, gentype_t* g,
  bool opaque, bool field)
{
  memset(meta, 0, sizeof(dwarf_meta_t));

  ast_t* ast = g->ast;
  LLVMTypeRef type = g->primitive;

  if(is_machine_word(ast))
  {
    if(is_float(ast))
      meta->flags |= DWARF_FLOAT;
    else if(is_signed(dwarf->opt, ast))
      meta->flags |= DWARF_SIGNED;
    else if(is_bool(ast))
      meta->flags |= DWARF_BOOLEAN;
  }
  else if(is_pointer(ast) || is_maybe(ast) || !is_concrete(ast) ||
    (is_constructable(ast) && field))
  {
    type = g->use_type;
  }
  else if(is_constructable(ast))
  {
    type = g->structure;
  }

  bool defined_type = g->underlying != TK_TUPLETYPE &&
    g->underlying != TK_UNIONTYPE && g->underlying != TK_ISECTTYPE;

  source_t* source;

  if(defined_type)
    ast = (ast_t*)ast_data(ast);

  source = ast_source(ast);
  meta->file = source->file;
  meta->name = g->type_name;
  meta->line = ast_line(ast);
  meta->pos = ast_pos(ast);

  if(!opaque)
  {
    meta->size = LLVMABISizeOfType(dwarf->target_data, type) << 3;
    meta->align = LLVMABIAlignmentOfType(dwarf->target_data, type) << 3;
  }
}
Пример #10
0
/**
 * The callee contains a *get/put instruction to an unknown field.
 * Given the caller may not be in the same hierarchy/package we cannot inline
 * it unless we make the field public.
 * But we need to make all fields public across the hierarchy and for fields
 * we don't know we have no idea whether the field was public or not anyway.
 */
bool MultiMethodInliner::unknown_field(DexInstruction* insn, DexMethod* context) {
  if (is_ifield_op(insn->opcode()) || is_sfield_op(insn->opcode())) {
    auto fop = static_cast<DexOpcodeField*>(insn);
    auto field = fop->field();
    field = resolve_field(field, is_sfield_op(insn->opcode())
        ? FieldSearch::Static : FieldSearch::Instance);
    if (field == nullptr) {
      info.escaped_field++;
      return true;
    }
    if (!field->is_concrete() && !is_public(field)) {
      info.non_pub_field++;
      return true;
    }
  }
  return false;
}
Пример #11
0
/*
 * There's no "good way" to differentiate blank vs. non-blank
 * finals.  So, we just scan the code in the CL-init.  If
 * it's sput there, then it's a blank.  Lame, agreed, but functional.
 *
 */
void get_sput_in_clinit(DexClass* clazz,
                        std::unordered_map<DexField*, bool>& blank_statics) {
  auto methods = clazz->get_dmethods();
  for (auto method : methods) {
    if (is_clinit(method)) {
      always_assert_log(is_static(method) && is_constructor(method),
          "static constructor doesn't have the proper access bits set\n");
      auto& code = method->get_code();
      auto opcodes = code->get_instructions();
      for (auto opcode : opcodes) {
        if (opcode->has_fields() && is_sput(opcode->opcode())) {
          auto fieldop = static_cast<DexOpcodeField*>(opcode);
          auto field = resolve_field(fieldop->field(), FieldSearch::Static);
          if (field == nullptr || !field->is_concrete()) continue;
          if (field->get_class() != clazz->get_type()) continue;
          blank_statics[field] = true;
        }
      }
    }
  }
}
Пример #12
0
void inline_cheap_sget(DexMethod* method, DexOpcodeField* opfield) {
  if (!validate_sget(method, opfield)) return;
  auto dest = opfield->dest();
  auto field = resolve_field(opfield->field(), FieldSearch::Static);
  always_assert_log(field->is_concrete(), "Must be a concrete field");
  auto value = field->get_static_value();
  /* FIXME for sget_wide case */
  uint32_t v = value != nullptr ? (uint32_t)value->value() : 0;
  auto opcode = [&] {
    if ((v & 0xffff) == v) {
      return OPCODE_CONST_16;
    } else if ((v & 0xffff0000) == v) {
      return OPCODE_CONST_HIGH16;
    }
    always_assert_log(false,
                      "Bad inline_cheap_sget queued up, can't fit to"
                      " CONST_16 or CONST_HIGH16, bailing\n");
  }();

  auto newopcode = (new DexInstruction(opcode, 0))->set_dest(dest)->set_literal(v);
  replace_opcode(method, opfield, newopcode);
}
Пример #13
0
/**
 * Check if a visibility/accessibility change would turn a method referenced
 * in a callee to virtual methods as they are inlined into the caller.
 * That is, once a callee is inlined we need to ensure that everything that was
 * referenced by a callee is visible and accessible in the caller context.
 * This step would not be needed if we changed all private instance to static.
 */
bool MultiMethodInliner::create_vmethod(DexInstruction* insn) {
  auto opcode = insn->opcode();
  if (opcode == OPCODE_INVOKE_DIRECT || opcode == OPCODE_INVOKE_DIRECT_RANGE) {
    auto method = static_cast<DexOpcodeMethod*>(insn)->get_method();
    method = resolver(method, MethodSearch::Direct);
    if (method == nullptr) {
      info.need_vmethod++;
      return true;
    }
    always_assert(method->is_def());
    if (is_init(method)) {
      if (!method->is_concrete() && !is_public(method)) {
        info.non_pub_ctor++;
        return true;
      }
      // concrete ctors we can handle because they stay invoke_direct
      return false;
    }
    info.need_vmethod++;
    return true;
  }
  return false;
}
Пример #14
0
MultiMethodInliner::MultiMethodInliner(
    std::vector<DexClass*>& scope,
    DexClasses& primary_dex,
    std::unordered_set<DexMethod*>& candidates,
    std::function<DexMethod*(DexMethod*, MethodSearch)> resolver)
        : resolver(resolver) {
  for (const auto& cls : primary_dex) {
    primary.insert(cls->get_type());
  }
  // walk every opcode in scope looking for calls to inlinable candidates
  // and build a map of callers to callees and the reverse callees to callers
  walk_opcodes(scope, [](DexMethod* meth) { return true; },
      [&](DexMethod* meth, DexInstruction* opcode) {
        if (is_invoke(opcode->opcode())) {
          auto mop = static_cast<DexOpcodeMethod*>(opcode);
          auto callee = resolver(mop->get_method(), opcode_to_search(opcode));
          if (callee != nullptr && callee->is_concrete() &&
              candidates.find(callee) != candidates.end()) {
            callee_caller[callee].push_back(meth);
            caller_callee[meth].push_back(callee);
          }
        }
      });
}
Пример #15
0
/**
 * Add to the list the single called.
 */
void SimpleInlinePass::select_single_called(
    Scope& scope, std::unordered_set<DexMethod*>& methods) {
  std::unordered_map<DexMethod*, int> calls;
  for (const auto& method : methods) {
    calls[method] = 0;
  }
  // count call sites for each method
  walk_opcodes(scope, [](DexMethod* meth) { return true; },
      [&](DexMethod* meth, DexInstruction* insn) {
        if (is_invoke(insn->opcode())) {
          auto mop = static_cast<DexOpcodeMethod*>(insn);
          auto callee = resolve_method(
              mop->get_method(), opcode_to_search(insn), resolved_refs);
          if (callee != nullptr && callee->is_concrete()
              && methods.count(callee) > 0) {
            calls[callee]++;
          }
        }
      });

  // pick methods with a single call site and add to candidates.
  // This vector usage is only because of logging we should remove it
  // once the optimization is "closed"
  std::vector<std::vector<DexMethod*>> calls_group(MAX_COUNT);
  for (auto call_it : calls) {
    if (call_it.second >= MAX_COUNT) {
      calls_group[MAX_COUNT - 1].push_back(call_it.first);
      continue;
    }
    calls_group[call_it.second].push_back(call_it.first);
  }
  assert(method_breakup(calls_group));
  for (auto callee : calls_group[1]) {
    inlinable.insert(callee);
  }
}
Пример #16
0
bool check_constraints(ast_t* orig, ast_t* typeparams, ast_t* typeargs,
  bool report_errors, pass_opt_t* opt)
{
  ast_t* typeparam = ast_child(typeparams);
  ast_t* typearg = ast_child(typeargs);

  while(typeparam != NULL)
  {
    if(is_bare(typearg))
    {
      if(report_errors)
      {
        ast_error(opt->check.errors, typearg,
          "a bare type cannot be used as a type argument");
      }

      return false;
    }

    switch(ast_id(typearg))
    {
      case TK_NOMINAL:
      {
        ast_t* def = (ast_t*)ast_data(typearg);

        if(ast_id(def) == TK_STRUCT)
        {
          if(report_errors)
          {
            ast_error(opt->check.errors, typearg,
              "a struct cannot be used as a type argument");
          }

          return false;
        }
        break;
      }

      case TK_TYPEPARAMREF:
      {
        ast_t* def = (ast_t*)ast_data(typearg);

        if(def == typeparam)
        {
          typeparam = ast_sibling(typeparam);
          typearg = ast_sibling(typearg);
          continue;
        }
        break;
      }

      default: {}
    }

    // Reify the constraint.
    ast_t* constraint = ast_childidx(typeparam, 1);
    ast_t* r_constraint = reify(constraint, typeparams, typeargs, opt,
      true);

    // A bound type must be a subtype of the constraint.
    errorframe_t info = NULL;
    errorframe_t* infop = (report_errors ? &info : NULL);
    if(!is_subtype_constraint(typearg, r_constraint, infop, opt))
    {
      if(report_errors)
      {
        errorframe_t frame = NULL;
        ast_error_frame(&frame, orig,
          "type argument is outside its constraint");
        ast_error_frame(&frame, typearg,
          "argument: %s", ast_print_type(typearg));
        ast_error_frame(&frame, typeparam,
          "constraint: %s", ast_print_type(r_constraint));
        errorframe_append(&frame, &info);
        errorframe_report(&frame, opt->check.errors);
      }

      ast_free_unattached(r_constraint);
      return false;
    }

    ast_free_unattached(r_constraint);

    // A constructable constraint can only be fulfilled by a concrete typearg.
    if(is_constructable(constraint) && !is_concrete(typearg))
    {
      if(report_errors)
      {
        ast_error(opt->check.errors, orig, "a constructable constraint can "
          "only be fulfilled by a concrete type argument");
        ast_error_continue(opt->check.errors, typearg, "argument: %s",
          ast_print_type(typearg));
        ast_error_continue(opt->check.errors, typeparam, "constraint: %s",
          ast_print_type(constraint));
      }

      return false;
    }

    typeparam = ast_sibling(typeparam);
    typearg = ast_sibling(typearg);
  }

  pony_assert(typeparam == NULL);
  pony_assert(typearg == NULL);
  return true;
}
Пример #17
0
bool check_constraints(ast_t* orig, ast_t* typeparams, ast_t* typeargs,
  bool report_errors, pass_opt_t* opt)
{
  ast_t* typeparam = ast_child(typeparams);
  ast_t* typearg = ast_child(typeargs);

  while(typeparam != NULL)
  {
    if(ast_id(typearg) == TK_TYPEPARAMREF)
    {
      ast_t* def = (ast_t*)ast_data(typearg);

      if(def == typeparam)
      {
        typeparam = ast_sibling(typeparam);
        typearg = ast_sibling(typearg);
        continue;
      }
    }

    // Reify the constraint.
    ast_t* constraint = ast_childidx(typeparam, 1);
    ast_t* bind_constraint = bind_type(constraint);
    ast_t* r_constraint = reify(bind_constraint, typeparams, typeargs, opt);

    if(bind_constraint != r_constraint)
      ast_free_unattached(bind_constraint);

    // A bound type must be a subtype of the constraint.
    errorframe_t info = NULL;
    if(!is_subtype(typearg, r_constraint, report_errors ? &info : NULL, opt))
    {
      if(report_errors)
      {
        ast_error(opt->check.errors, orig,
          "type argument is outside its constraint");
        ast_error_continue(opt->check.errors, typearg,
          "argument: %s", ast_print_type(typearg));
        ast_error_continue(opt->check.errors, typeparam,
          "constraint: %s", ast_print_type(r_constraint));
      }

      ast_free_unattached(r_constraint);
      return false;
    }

    ast_free_unattached(r_constraint);

    // A constructable constraint can only be fulfilled by a concrete typearg.
    if(is_constructable(constraint) && !is_concrete(typearg))
    {
      if(report_errors)
      {
        ast_error(opt->check.errors, orig, "a constructable constraint can "
          "only be fulfilled by a concrete type argument");
        ast_error_continue(opt->check.errors, typearg, "argument: %s",
          ast_print_type(typearg));
        ast_error_continue(opt->check.errors, typeparam, "constraint: %s",
          ast_print_type(constraint));
      }

      return false;
    }

    typeparam = ast_sibling(typeparam);
    typearg = ast_sibling(typearg);
  }

  assert(typeparam == NULL);
  assert(typearg == NULL);
  return true;
}
Пример #18
0
Файл: reify.c Проект: DevL/ponyc
bool check_constraints(ast_t* orig, ast_t* typeparams, ast_t* typeargs,
  bool report_errors)
{
  ast_t* typeparam = ast_child(typeparams);
  ast_t* typearg = ast_child(typeargs);

  while(typeparam != NULL)
  {
    // Reify the constraint.
    ast_t* constraint = ast_childidx(typeparam, 1);
    ast_t* bind_constraint = bind_type(constraint);
    ast_t* r_constraint = reify(bind_constraint, typeparams, typeargs);

    if(bind_constraint != r_constraint)
      ast_free_unattached(bind_constraint);

    // A bound type must be a subtype of the constraint.
    errorframe_t info = NULL;
    if(!is_subtype(typearg, r_constraint, report_errors ? &info : NULL))
    {
      if(report_errors)
      {
        errorframe_t frame = NULL;
        ast_error_frame(&frame, orig,
          "type argument is outside its constraint");
        ast_error_frame(&frame, typearg,
          "argument: %s", ast_print_type(typearg));
        ast_error_frame(&frame, typeparam,
          "constraint: %s", ast_print_type(r_constraint));
        errorframe_append(&frame, &info);
        errorframe_report(&frame);
      }

      ast_free_unattached(r_constraint);
      return false;
    }

    ast_free_unattached(r_constraint);

    // A constructable constraint can only be fulfilled by a concrete typearg.
    if(is_constructable(constraint) && !is_concrete(typearg))
    {
      if(report_errors)
      {
        ast_error(orig, "a constructable constraint can only be fulfilled "
          "by a concrete type argument");
        ast_error(typearg, "argument: %s", ast_print_type(typearg));
        ast_error(typeparam, "constraint: %s", ast_print_type(constraint));
      }

      return false;
    }

    typeparam = ast_sibling(typeparam);
    typearg = ast_sibling(typearg);
  }

  assert(typeparam == NULL);
  assert(typearg == NULL);
  return true;
}
Пример #19
0
bool check_constraints(ast_t* orig, ast_t* typeparams, ast_t* typeargs,
  bool report_errors)
{
  // Reify the type parameters with the typeargs.
  ast_t* r_typeparams = reify(orig, typeparams, typeparams, typeargs);

  if(r_typeparams == NULL)
    return false;

  ast_t* r_typeparam = ast_child(r_typeparams);
  ast_t* typeparam = ast_child(typeparams);
  ast_t* typearg = ast_child(typeargs);

  while(r_typeparam != NULL)
  {
    // Use the reified constraint.
    ast_t* r_constraint = ast_childidx(r_typeparam, 1);
    r_constraint = bind_type(r_constraint);

    // A bound type must be a subtype of the constraint.
    if(!is_subtype(typearg, r_constraint))
    {
      if(report_errors)
      {
        ast_error(orig, "type argument is outside its constraint");
        ast_error(typearg, "argument: %s", ast_print_type(typearg));
        ast_error(typeparam, "constraint: %s", ast_print_type(r_constraint));
      }

      ast_free_unattached(r_typeparams);
      ast_free_unattached(r_constraint);
      return false;
    }

    ast_free_unattached(r_constraint);

    // A constructable constraint can only be fulfilled by a concrete typearg.
    ast_t* constraint = ast_childidx(typeparam, 1);

    if(is_constructable(constraint) && !is_concrete(typearg))
    {
      if(report_errors)
      {
        ast_error(orig, "a constructable constraint can only be fulfilled "
          "by a concrete type argument");
        ast_error(typearg, "argument: %s", ast_print_type(typearg));
        ast_error(typeparam, "constraint: %s", ast_print_type(constraint));
      }

      ast_free_unattached(r_typeparams);
      return false;
    }

    r_typeparam = ast_sibling(r_typeparam);
    typeparam = ast_sibling(typeparam);
    typearg = ast_sibling(typearg);
  }

  assert(r_typeparam == NULL);
  assert(typearg == NULL);

  ast_free_unattached(r_typeparams);
  return true;
}