<commit_before>package exm.stc.frontend.typecheck;

import static exm.stc.frontend.typecheck.FunctionTypeChecker.checkOverloadsAmbiguity;
import static exm.stc.frontend.typecheck.FunctionTypeChecker.concretiseInputsNonOverloaded;
import static exm.stc.frontend.typecheck.FunctionTypeChecker.concretiseInputsOverloaded;
import static exm.stc.frontend.typecheck.FunctionTypeChecker.selectArgType;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import exm.stc.common.Logging;
import exm.stc.common.exceptions.InvalidOverloadException;
import exm.stc.common.exceptions.TypeMismatchException;
import exm.stc.common.lang.DefaultVals;
import exm.stc.common.lang.FnID;
import exm.stc.common.lang.ForeignFunctions;
import exm.stc.common.lang.Types;
import exm.stc.common.lang.Types.FunctionType;
import exm.stc.common.lang.Types.SubType;
import exm.stc.common.lang.Types.Type;
import exm.stc.common.lang.Types.UnionType;
import exm.stc.common.lang.Var;
import exm.stc.common.lang.Var.Alloc;
import exm.stc.common.lang.Var.DefType;
import exm.stc.common.lang.Var.VarProvenance;
import exm.stc.common.util.Pair;
import exm.stc.frontend.Context.FnOverload;
import exm.stc.frontend.GlobalContext;
import exm.stc.frontend.typecheck.FunctionTypeChecker.FnCallInfo;
import exm.stc.frontend.typecheck.FunctionTypeChecker.FnMatch;

public class FunctionTypeCheckerTest {

  private static final FnID FAKE_FN_ID = new FnID("foobar", "foobar");

  private static final SubType FLOAT_SUB_TYPE = new SubType(Types.F_FLOAT, "float2");
  private static final Type INT_OR_FLOAT =
      UnionType.createUnionType(Types.F_INT, Types.F_FLOAT);
  private static final Type FLOAT_OR_INT =
      UnionType.createUnionType(Types.F_FLOAT, Types.F_INT);
  private static final Type INT_OR_STRING =
      UnionType.createUnionType(Types.F_INT, Types.F_STRING);

  private static final Type STRING_OR_INT =
      UnionType.createUnionType(Types.F_STRING, Types.F_INT);

  private static final GlobalContext FAKE_CONTEXT =
      new GlobalContext("fake.swift", Logging.getSTCLogger(),
      new ForeignFunctions());
  @Rule
  public ExpectedException exception = ExpectedException.none();

  @BeforeClass
  public static void setupLogging() throws Exception {
    Logging.setupLogging("FunctionTypeCheckerTest.stc.log", true);
  }

  @Test
  public void testSelectArgTypeSimple() {
    assertEquals("Basic matching should work",
        Pair.create(Types.F_FLOAT, Types.F_FLOAT),
        selectArgType(Types.F_FLOAT, Types.F_FLOAT, false));
  }

  @Test
  public void testSelectArgTypeSubtype() {
    assertEquals("Subtype upcasting should work",
        Pair.create(Types.F_FLOAT, FLOAT_SUB_TYPE),
        selectArgType(Types.F_FLOAT, FLOAT_SUB_TYPE, false));

    assertTrue("Subtype downcasting not allowed",
        selectArgType(FLOAT_SUB_TYPE, Types.F_FLOAT, false) == null);
  }

  @Test
  public void testSelectArgTypeUnion() {
    assertEquals("Int matches IntOrFloat",
        Pair.create(Types.F_INT, Types.F_INT),
        selectArgType(Types.F_INT, INT_OR_FLOAT, false));

    assertEquals("Float matches IntOrFloat",
        Pair.create(Types.F_INT, Types.F_INT),
        selectArgType(Types.F_INT, INT_OR_FLOAT, false));

    assertEquals("IntOrFloat matches IntOrFloat",
        Pair.create(Types.F_INT, Types.F_INT),
        selectArgType(INT_OR_FLOAT, INT_OR_FLOAT, false));

    assertEquals("IntOrFloat matches Float",
        Pair.create(Types.F_FLOAT, Types.F_FLOAT),
        selectArgType(INT_OR_FLOAT, Types.F_FLOAT, false));
  }

  private static final FunctionType VARARGS_TYPE = new FunctionType(
                            Arrays.asList(Types.F_INT, INT_OR_FLOAT),
                            Arrays.asList(Types.F_STRING), true);

  @Test
  public void testMatchNoVarArgs() throws TypeMismatchException {
    FnCallInfo fc = makeFnCallInfo(VARARGS_TYPE,
        Arrays.asList(Types.F_INT, Types.F_FLOAT, Types.F_INT, Types.F_FLOAT));

    FnMatch match = concretiseInputsOverloaded(FAKE_CONTEXT, fc);

    assertEquals("One matching type", 1, match.concreteAlts.size());
    assertEquals("Varargs expanded",
        Arrays.asList(Types.F_INT, Types.F_FLOAT, Types.F_INT, Types.F_FLOAT),
        match.concreteAlts.get(0).getInputs());
  }

  @Test
  public void testMatchMultiVarArgs() throws TypeMismatchException {
    FnCallInfo fc = makeFnCallInfo(VARARGS_TYPE,
                                    Arrays.asList(Types.F_INT));

    FnMatch match = concretiseInputsOverloaded(FAKE_CONTEXT, fc);

    assertEquals("One matching type", 1, match.concreteAlts.size());
    assertEquals("Varargs expanded", Arrays.asList(Types.F_INT),
                  match.concreteAlts.get(0).getInputs());
  }

  @Test
  public void testMatchVarArgsFail() throws TypeMismatchException {
    exception.expect(TypeMismatchException.class);
    FnCallInfo fc = makeFnCallInfo(VARARGS_TYPE,
                      Arrays.asList(Types.F_INT, Types.F_STRING));
    concretiseInputsOverloaded(FAKE_CONTEXT, fc);
  }


  @Test
  public void testMatchOptional() throws TypeMismatchException {
    // Function is f(int x, bool y=true)
    String name = "f";
    FunctionType ft = makeSimpleFT(Types.F_INT, Types.F_BOOL);
    Var constVar = new Var(Types.F_BOOL, "true", Alloc.GLOBAL_CONST,
                        DefType.GLOBAL_CONST, VarProvenance.unknown());
    DefaultVals<Var> defaults = DefaultVals.fromDefaultValVector(
                                  Arrays.asList(null, constVar));
    FnOverload fo = makeFnOverload(new FnID(name, name), ft, defaults);

    // Try calling with both args
    List<Type> intBoolArgs = Arrays.asList(Types.F_INT, Types.F_BOOL);
    FnCallInfo fc = makeFnCallInfo(name, fo.asList(), intBoolArgs);
    FnMatch match = concretiseInputsOverloaded(FAKE_CONTEXT, fc);

    assertEquals("One matching type", 1, match.concreteAlts.size());
    assertEquals("Matched args", intBoolArgs,
                  match.concreteAlts.get(0).getInputs());

    List<Type> intArgs = Arrays.asList(Types.F_INT);
    fc = makeFnCallInfo(name, fo.asList(), intArgs);
    match = concretiseInputsOverloaded(FAKE_CONTEXT, fc);

    // Should resolve to (int)
    assertEquals("One matching type", 1, match.concreteAlts.size());
    assertEquals("Matched args", intArgs,
                  match.concreteAlts.get(0).getInputs());
  }

  /**
   * Regression test - not enough arguments provided
   * @throws TypeMismatchException
   */
  @Test
  public void testMissingArgs() throws TypeMismatchException {
    exception.expect(TypeMismatchException.class);

    FunctionType intFn = makeSimpleFT(Types.F_INT, Types.F_INT);
    FnID intFnID = new FnID("int", "int");
    FnOverload intOverload = makeFnOverload(intFnID, intFn);

    // Shouldn't match - not enough args
    FnMatch match = concretiseInputsNonOverloaded(FAKE_CONTEXT,
        intOverload, Arrays.asList(Types.F_INT),
        Collections.<String, Type>emptyMap(), true);

    System.err.println(match);
  }

  /**<commit_msg>Update unit test to reflect change in typechecker behaviour<commit_after>package exm.stc.frontend.typecheck;

import static exm.stc.frontend.typecheck.FunctionTypeChecker.checkOverloadsAmbiguity;
import static exm.stc.frontend.typecheck.FunctionTypeChecker.concretiseInputsNonOverloaded;
import static exm.stc.frontend.typecheck.FunctionTypeChecker.concretiseInputsOverloaded;
import static exm.stc.frontend.typecheck.FunctionTypeChecker.selectArgType;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import exm.stc.common.Logging;
import exm.stc.common.exceptions.InvalidOverloadException;
import exm.stc.common.exceptions.TypeMismatchException;
import exm.stc.common.lang.DefaultVals;
import exm.stc.common.lang.FnID;
import exm.stc.common.lang.ForeignFunctions;
import exm.stc.common.lang.Types;
import exm.stc.common.lang.Types.FunctionType;
import exm.stc.common.lang.Types.SubType;
import exm.stc.common.lang.Types.Type;
import exm.stc.common.lang.Types.UnionType;
import exm.stc.common.lang.Var;
import exm.stc.common.lang.Var.Alloc;
import exm.stc.common.lang.Var.DefType;
import exm.stc.common.lang.Var.VarProvenance;
import exm.stc.common.util.Pair;
import exm.stc.frontend.Context.FnOverload;
import exm.stc.frontend.GlobalContext;
import exm.stc.frontend.typecheck.FunctionTypeChecker.FnCallInfo;
import exm.stc.frontend.typecheck.FunctionTypeChecker.FnMatch;

public class FunctionTypeCheckerTest {

  private static final FnID FAKE_FN_ID = new FnID("foobar", "foobar");

  private static final SubType FLOAT_SUB_TYPE = new SubType(Types.F_FLOAT, "float2");
  private static final Type INT_OR_FLOAT =
      UnionType.createUnionType(Types.F_INT, Types.F_FLOAT);
  private static final Type FLOAT_OR_INT =
      UnionType.createUnionType(Types.F_FLOAT, Types.F_INT);
  private static final Type INT_OR_STRING =
      UnionType.createUnionType(Types.F_INT, Types.F_STRING);

  private static final Type STRING_OR_INT =
      UnionType.createUnionType(Types.F_STRING, Types.F_INT);

  private static final GlobalContext FAKE_CONTEXT =
      new GlobalContext("fake.swift", Logging.getSTCLogger(),
      new ForeignFunctions());
  @Rule
  public ExpectedException exception = ExpectedException.none();

  @BeforeClass
  public static void setupLogging() throws Exception {
    Logging.setupLogging("FunctionTypeCheckerTest.stc.log", true);
  }

  @Test
  public void testSelectArgTypeSimple() {
    assertEquals("Basic matching should work",
        Pair.create(Types.F_FLOAT, Types.F_FLOAT),
        selectArgType(Types.F_FLOAT, Types.F_FLOAT, false));
  }

  @Test
  public void testSelectArgTypeSubtype() {
    assertEquals("Subtype upcasting should work",
        Pair.create(Types.F_FLOAT, FLOAT_SUB_TYPE),
        selectArgType(Types.F_FLOAT, FLOAT_SUB_TYPE, false));

    assertTrue("Subtype downcasting not allowed",
        selectArgType(FLOAT_SUB_TYPE, Types.F_FLOAT, false) == null);
  }

  @Test
  public void testSelectArgTypeUnion() {
    assertEquals("Int matches IntOrFloat",
        Pair.create(Types.F_INT, Types.F_INT),
        selectArgType(Types.F_INT, INT_OR_FLOAT, false));

    assertEquals("Float matches IntOrFloat",
        Pair.create(Types.F_INT, Types.F_INT),
        selectArgType(Types.F_INT, INT_OR_FLOAT, false));

    assertEquals("IntOrFloat matches IntOrFloat",
        Pair.create(Types.F_INT, Types.F_INT),
        selectArgType(INT_OR_FLOAT, INT_OR_FLOAT, false));

    assertEquals("IntOrFloat matches Float",
        Pair.create(Types.F_FLOAT, Types.F_FLOAT),
        selectArgType(INT_OR_FLOAT, Types.F_FLOAT, false));
  }

  private static final FunctionType VARARGS_TYPE = new FunctionType(
                            Arrays.asList(Types.F_INT, INT_OR_FLOAT),
                            Arrays.asList(Types.F_STRING), true);

  @Test
  public void testMatchNoVarArgs() throws TypeMismatchException {
    FnCallInfo fc = makeFnCallInfo(VARARGS_TYPE,
        Arrays.asList(Types.F_INT, Types.F_FLOAT, Types.F_INT, Types.F_FLOAT));

    FnMatch match = concretiseInputsOverloaded(FAKE_CONTEXT, fc);

    assertEquals("One matching type", 1, match.concreteAlts.size());
    assertEquals("Varargs expanded",
        Arrays.asList(Types.F_INT, Types.F_FLOAT, Types.F_INT, Types.F_FLOAT),
        match.concreteAlts.get(0).getInputs());
  }

  @Test
  public void testMatchMultiVarArgs() throws TypeMismatchException {
    FnCallInfo fc = makeFnCallInfo(VARARGS_TYPE,
                                    Arrays.asList(Types.F_INT));

    FnMatch match = concretiseInputsOverloaded(FAKE_CONTEXT, fc);

    assertEquals("One matching type", 1, match.concreteAlts.size());
    assertEquals("Varargs expanded", Arrays.asList(Types.F_INT),
                  match.concreteAlts.get(0).getInputs());
  }

  @Test
  public void testMatchVarArgsFail() throws TypeMismatchException {
    exception.expect(TypeMismatchException.class);
    FnCallInfo fc = makeFnCallInfo(VARARGS_TYPE,
                      Arrays.asList(Types.F_INT, Types.F_STRING));
    concretiseInputsOverloaded(FAKE_CONTEXT, fc);
  }


  @Test
  public void testMatchOptional() throws TypeMismatchException {
    // Function is f(int x, bool y=true)
    String name = "f";
    FunctionType ft = makeSimpleFT(Types.F_INT, Types.F_BOOL);
    Var constVar = new Var(Types.F_BOOL, "true", Alloc.GLOBAL_CONST,
                        DefType.GLOBAL_CONST, VarProvenance.unknown());
    DefaultVals<Var> defaults = DefaultVals.fromDefaultValVector(
                                  Arrays.asList(null, constVar));
    FnOverload fo = makeFnOverload(new FnID(name, name), ft, defaults);

    // Try calling with both args
    List<Type> intBoolArgs = Arrays.asList(Types.F_INT, Types.F_BOOL);
    FnCallInfo fc = makeFnCallInfo(name, fo.asList(), intBoolArgs);
    FnMatch match = concretiseInputsOverloaded(FAKE_CONTEXT, fc);

    assertEquals("One matching type", 1, match.concreteAlts.size());
    assertEquals("Matched args", intBoolArgs,
                  match.concreteAlts.get(0).getInputs());

    List<Type> intArgs = Arrays.asList(Types.F_INT);
    fc = makeFnCallInfo(name, fo.asList(), intArgs);
    match = concretiseInputsOverloaded(FAKE_CONTEXT, fc);

    // Should resolve to (int, bool) - last value filled in by default
    assertEquals("One matching type", 1, match.concreteAlts.size());
    assertEquals("Matched args", intBoolArgs,
                  match.concreteAlts.get(0).getInputs());
  }

  /**
   * Regression test - not enough arguments provided
   * @throws TypeMismatchException
   */
  @Test
  public void testMissingArgs() throws TypeMismatchException {
    exception.expect(TypeMismatchException.class);

    FunctionType intFn = makeSimpleFT(Types.F_INT, Types.F_INT);
    FnID intFnID = new FnID("int", "int");
    FnOverload intOverload = makeFnOverload(intFnID, intFn);

    // Shouldn't match - not enough args
    FnMatch match = concretiseInputsNonOverloaded(FAKE_CONTEXT,
        intOverload, Arrays.asList(Types.F_INT),
        Collections.<String, Type>emptyMap(), true);

    System.err.println(match);
  }

  /**
{'fraction_non_alphanumeric': 0.1055738605161999, 'fraction_numerical': 0.0012355848434925864, 'mean_word_length': 4.009972489683632, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1464373', 'n_tokens_mistral': 4721, 'n_tokens_neox': 4654, 'n_words': 865}