public static TypecheckingResult coerce()

in base/src/main/java/org/arend/core/definition/CoerceData.java [95:179]


  public static TypecheckingResult coerce(TypecheckingResult result, Expression expectedType, Concrete.SourceNode sourceNode, CheckTypeVisitor visitor) {
    DefCallExpression actualDefCall = result.type.cast(DefCallExpression.class);
    DefCallExpression expectedDefCall = expectedType.cast(DefCallExpression.class);
    if (actualDefCall != null && expectedDefCall != null && (actualDefCall.getDefinition() == expectedDefCall.getDefinition() || actualDefCall.getDefinition() instanceof ClassDefinition && expectedDefCall.getDefinition() instanceof ClassDefinition && ((ClassDefinition) actualDefCall.getDefinition()).isSubClassOf((ClassDefinition) expectedDefCall.getDefinition()))) {
      return null;
    }
    CoerceData actualCoerceData = actualDefCall != null ? actualDefCall.getDefinition().getCoerceData() : null;
    if (actualCoerceData != null && actualCoerceData.myMapTo.isEmpty()) {
      actualCoerceData = null;
    }
    CoerceData expectedCoerceData = expectedDefCall != null ? expectedDefCall.getDefinition().getCoerceData() : null;
    if (expectedCoerceData != null && expectedCoerceData.myMapFrom.isEmpty()) {
      expectedCoerceData = null;
    }
    ClassCallExpression classCall = actualDefCall instanceof ClassCallExpression ? (ClassCallExpression) actualDefCall : null;
    if (classCall != null && classCall.getDefinition().getClassifyingField() == null) classCall = null;
    if (actualCoerceData == null && expectedCoerceData == null && classCall == null) {
      return null;
    }

    Key actualKey = getKey(result.type);
    Key originalExpectedKey = getKey(expectedType);
    Key expectedKey = originalExpectedKey;

    // Coerce from a definition
    if (expectedCoerceData != null && !(actualKey instanceof AnyKey)) {
      List<Definition> defs = expectedCoerceData.myMapFrom.get(actualKey);
      if (defs != null) {
        return coerceResult(result, defs, expectedType, sourceNode, visitor, false, false);
      }
    }

    // Coerce to a definition
    if (actualCoerceData != null && !(expectedKey instanceof AnyKey)) {
      List<Definition> defs = actualCoerceData.myMapTo.get(expectedKey);
      if (defs != null) {
        return coerceResult(result, defs, expectedType, sourceNode, visitor, false, false);
      }
    }

    // Can't coerce from or to a definition
    if (expectedCoerceData != null && !(actualKey instanceof AnyKey)) {
      actualKey = new AnyKey();
    }
    if (actualCoerceData != null && !(expectedKey instanceof AnyKey)) {
      expectedKey = new AnyKey();
    }

    // Coerce from an arbitrary type
    if (expectedCoerceData != null) {
      List<Definition> defs = expectedCoerceData.myMapFrom.get(actualKey);
      if (defs != null) {
        return coerceResult(result, defs, expectedType, sourceNode, visitor, true, false);
      }
    }

    // Coerce to an arbitrary type
    if (actualCoerceData != null) {
      List<Definition> defs = actualCoerceData.myMapTo.get(expectedKey);
      if (defs != null) {
        TypecheckingResult tcResult = coerceResult(result, defs, expectedType, sourceNode, visitor, false, true);
        if (tcResult != null) return tcResult;
      }
    }

    // Coerce using classifying field
    if (classCall != null) {
      List<Definition> defs = new ArrayList<>();
      while (true) {
        ClassField classifyingField = classCall.getDefinition().getClassifyingField();
        if (classifyingField == null) break;
        defs.add(classifyingField);
        Expression type = getClassifyingFieldType(classCall);
        Key key = getKey(type);
        if (key.equals(originalExpectedKey)) {
          return coerceResult(result, defs, expectedType, sourceNode, visitor, false, originalExpectedKey instanceof AnyKey);
        }
        if (!(type instanceof ClassCallExpression)) break;
        classCall = (ClassCallExpression) type;
      }
    }

    // Can't coerce
    return null;
  }