Package org.openquark.cal.compiler

Source Code of org.openquark.cal.compiler.DataConstructorCode_Test

/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*     * Redistributions of source code must retain the above copyright notice,
*       this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of Business Objects nor the names of its contributors
*       may be used to endorse or promote products derived from this software
*       without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/


/*
* DataConstructorCode_Test.java
* Creation date: Jul 13, 2005.
* By: Edward Lam
*/
package org.openquark.cal.compiler;

import java.util.Arrays;
import java.util.List;

import junit.extensions.TestSetup;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

import org.openquark.cal.CALPlatformTestModuleNames;
import org.openquark.cal.compiler.SourceModel.TypeConstructorDefn.AlgebraicType.DataConsDefn;
import org.openquark.cal.module.Cal.Core.CAL_Prelude;
import org.openquark.cal.runtime.CALExecutorException;
import org.openquark.cal.runtime.MachineType;
import org.openquark.cal.services.BasicCALServices;
import org.openquark.cal.services.CALServicesTestUtilities;
import org.openquark.cal.services.GemCompilationException;
import org.openquark.cal.services.Status;
import org.openquark.cal.services.StringModuleSourceDefinition;
import org.openquark.cal.services.WorkspaceManager;


/**
* A set of JUnit test cases for verifying the static analysis of data constructors in .cal code.
* Note: these tests test for failure cases -- we can include cases which should succeed as part of actual module code (eg. M2.cal).
*
* @author Edward Lam
*/
public class DataConstructorCode_Test extends TestCase {
   
    private static final ModuleName Prelude = CAL_Prelude.MODULE_NAME;

    /**
     * A copy of CAL services for use in the test cases.
     */
    private static BasicCALServices leccCALServices;

    private static final ModuleName DEFAULT_TEST_MODULE_NAME = ModuleName.make("DataConstructorCodeTestModule");
    private static final String DEFAULT_TEST_FUNCTION_NAME = "dataConstructorCodeTestFunction";
    private static final String DEFAULT_DATA_CONS_NAME = "DataConstructorCodeTestType";
   
    private static final SourceModel.Name.DataCons DEFAULT_DATA_CONS_QUALIFIED_NAME =
            SourceModel.Name.DataCons.make(QualifiedName.make(DEFAULT_TEST_MODULE_NAME, DEFAULT_DATA_CONS_NAME));
   
   
    /**
     * @return a test suite containing all the test cases for testing CAL source
     *         generation.
     */
    public static Test suite() {

        TestSuite suite = new TestSuite(DataConstructorCode_Test.class);

        return new TestSetup(suite) {

            protected void setUp() {
                oneTimeSetUp();
               
            }
   
            protected void tearDown() {
                oneTimeTearDown();
            }
        };
    }
   
    /**
     * Performs the setup for the test suite.
     */
    private static void oneTimeSetUp() {
        leccCALServices = CALServicesTestUtilities.getCommonCALServices(MachineType.LECC, "cal.platform.test.cws");
    }
   
    /**
     * Performs the tear down for the test suite.
     */
    private static void oneTimeTearDown() {
        leccCALServices = null;
    }
   
    /**
     * Constructor for DataConstructorCode_Test.
     *
     * @param name
     *            the name of the test.
     */
    public DataConstructorCode_Test(String name) {
        super(name);
    }

    /**
     * Tests that an argument name can't be used twice for a data constructor.
     */
    public void testRepeatedDataConstructorArgumentNameFails() {
        // Field names {foo, foo, bar} -- field name "foo" is repeated.
        String[] textualFieldNames = new String[] {"foo", "foo", "bar"};
       
        // Create the type constructor with the data cons definition.
        SourceModel.TypeConstructorDefn typeConsDefn = getSingleDataConstructorTypeConstructorDefinition(textualFieldNames);
       
        // Compile as a new module.
        CompilerMessageLogger logger = compileAndRemoveModuleForTopLevelSourceElements(DEFAULT_TEST_MODULE_NAME, new SourceModel.TopLevelSourceElement[]{typeConsDefn});
       
        // Check that we got an error message of the expected type.
        CompilerMessage error = logger.getFirstError();
        assertTrue("No errors logged.", error != null);
        assertEquals(MessageKind.Error.RepeatedFieldNameInDataConstructorDeclaration.class, error.getMessageKind().getClass());
    }
   
   
    /**
     * Tests that an error is given for unknown field names when using the matching notation for data constructor unpacking.
     * eg. where SomeDataCons does not have an argument named "unknownDataConsArg":
     *     myFunction arg = case arg of SomeDataCons {unknownDataConsArg = patternVar} -> patternVar;;
     */
    public void testMatchingUnknownDataConstructorArgument() {
        // Create the type constructor definition..
        String[] textualFieldNames = new String[] {"foo", "bar"};
        SourceModel.TypeConstructorDefn typeConsDefn = getSingleDataConstructorTypeConstructorDefinition(textualFieldNames);

        // Create a case alt for a data constructor using the matching notation, where a field name is unknown.
        FieldName fieldName = FieldName.make("unknownFieldName");
        SourceModel.Pattern pattern = SourceModel.Pattern.Var.make("unusedPatternVar");
        SourceModel.FieldPattern fieldPattern = SourceModel.FieldPattern.make(SourceModel.Name.Field.make(fieldName), pattern);
        SourceModel.FieldPattern[] fieldPatterns = new SourceModel.FieldPattern[] {fieldPattern};
       
        SourceModel.Expr.Case.Alt caseAlt =
            SourceModel.Expr.Case.Alt.UnpackDataCons.make(DEFAULT_DATA_CONS_QUALIFIED_NAME, fieldPatterns, SourceModel.Expr.makeBooleanValue(true));

        // Put into a case expression (conditional upon "var"), wrap with a function.
        SourceModel.Expr.Var varExpr = SourceModel.Expr.Var.makeUnqualified("var");
        SourceModel.Expr.Case caseExpr = SourceModel.Expr.Case.make(varExpr, new SourceModel.Expr.Case.Alt[]{caseAlt});
       
        SourceModel.Parameter[] parameters = new SourceModel.Parameter[] {SourceModel.Parameter.make("var", false)};
        SourceModel.FunctionDefn functionDefn =
                SourceModel.FunctionDefn.Algebraic.make(DEFAULT_TEST_FUNCTION_NAME, Scope.PRIVATE, parameters, caseExpr);
       
        // Compile our elements to a new module.
        SourceModel.TopLevelSourceElement[] topLevelSourceElements = new SourceModel.TopLevelSourceElement[]{typeConsDefn, functionDefn};
        CompilerMessageLogger logger = compileAndRemoveModuleForTopLevelSourceElements(DEFAULT_TEST_MODULE_NAME, topLevelSourceElements);
       
        // Check that we got an error message of the expected type.
        CompilerMessage error = logger.getFirstError();
        assertTrue("No errors logged.", error != null);
        assertEquals(MessageKind.Error.UnknownDataConstructorField.class, error.getMessageKind().getClass());
    }

    /**
     * Tests that an error is given for repeated field names when using the matching notation for data constructor unpacking.
     * eg. myFunction arg = case arg of SomeDataCons {repeatedFieldName = patternVar1, repeatedFieldName = patternVar2} -> True;;
     */
    public void testRepeatedFieldNameInFieldBinding() {
        // Create the type constructor definition..
        String[] textualFieldNames = new String[] {"foo", "bar"};
        SourceModel.TypeConstructorDefn typeConsDefn = getSingleDataConstructorTypeConstructorDefinition(textualFieldNames);

        // Create a case alt for a data constructor using the matching notation, with two field patterns where the field names are the same.
        FieldName repeatedFieldName = FieldName.make("foo");
        SourceModel.Pattern pattern1 = SourceModel.Pattern.Var.make("unusedPatternVar1");
        SourceModel.Pattern pattern2 = SourceModel.Pattern.Var.make("unusedPatternVar2");
       
        SourceModel.FieldPattern fieldPattern1 = SourceModel.FieldPattern.make(SourceModel.Name.Field.make(repeatedFieldName), pattern1);
        SourceModel.FieldPattern fieldPattern2 = SourceModel.FieldPattern.make(SourceModel.Name.Field.make(repeatedFieldName), pattern2);
        SourceModel.FieldPattern[] fieldPatterns = new SourceModel.FieldPattern[] {fieldPattern1, fieldPattern2};
       
        SourceModel.Expr.Case.Alt caseAlt =
            SourceModel.Expr.Case.Alt.UnpackDataCons.make(DEFAULT_DATA_CONS_QUALIFIED_NAME, fieldPatterns, SourceModel.Expr.makeBooleanValue(true));

        // Put into a case expression (conditional upon "var")
        SourceModel.Expr.Case caseExpr = SourceModel.Expr.Case.make(SourceModel.Expr.Var.makeUnqualified("var"),
                                                                    new SourceModel.Expr.Case.Alt[]{caseAlt});
       
        // wrap with a function.
        SourceModel.Parameter[] parameters = new SourceModel.Parameter[] {SourceModel.Parameter.make("var", false)};
        SourceModel.FunctionDefn functionDefn =
                SourceModel.FunctionDefn.Algebraic.make(DEFAULT_TEST_FUNCTION_NAME, Scope.PRIVATE, parameters, caseExpr);
       
        // Compile our elements to a new module.
        SourceModel.TopLevelSourceElement[] topLevelSourceElements = new SourceModel.TopLevelSourceElement[]{typeConsDefn, functionDefn};
        CompilerMessageLogger logger = compileAndRemoveModuleForTopLevelSourceElements(DEFAULT_TEST_MODULE_NAME, topLevelSourceElements);

       
        // Check that we got a single error message of the expected type.
        CompilerMessage error = logger.getFirstError();
        assertTrue("No errors logged.", error != null);
        assertEquals(MessageKind.Error.RepeatedFieldNameInFieldBindingPattern.class, error.getMessageKind().getClass());
    }
    // repeated pattern variable in field binding.

    /**
     * Tests that an error is given for repeated pattern variables when using the matching notation for data constructor unpacking.
     * eg. myFunction arg = case arg of SomeDataCons {dataConsArg1 = repeatedPatternVar, dataConsArg2 = repeatedPatternVar} -> True;;
     */
    public void testRepeatedPatternVarInFieldBinding() {
        // Create the type constructor definition..
        String[] textualFieldNames = new String[] {"foo", "bar"};
        SourceModel.TypeConstructorDefn typeConsDefn = getSingleDataConstructorTypeConstructorDefinition(textualFieldNames);

        // Create a case alt for a data constructor using the matching notation, with two field patterns where the field names are the same.
        FieldName fieldName1 = FieldName.make("foo");
        FieldName fieldName2 = FieldName.make("bar");
        SourceModel.Pattern repeatedPattern = SourceModel.Pattern.Var.make("repeatedPatternVar");
       
        SourceModel.FieldPattern fieldPattern1 = SourceModel.FieldPattern.make(SourceModel.Name.Field.make(fieldName1), repeatedPattern);
        SourceModel.FieldPattern fieldPattern2 = SourceModel.FieldPattern.make(SourceModel.Name.Field.make(fieldName2), repeatedPattern);
        SourceModel.FieldPattern[] fieldPatterns = new SourceModel.FieldPattern[] {fieldPattern1, fieldPattern2};
       
        SourceModel.Expr.Case.Alt caseAlt =
            SourceModel.Expr.Case.Alt.UnpackDataCons.make(DEFAULT_DATA_CONS_QUALIFIED_NAME, fieldPatterns, SourceModel.Expr.makeBooleanValue(true));

        // Put into a case expression (conditional upon "var")
        SourceModel.Expr.Case caseExpr = SourceModel.Expr.Case.make(SourceModel.Expr.Var.makeUnqualified("var"),
                                                                    new SourceModel.Expr.Case.Alt[]{caseAlt});
       
        // wrap with a function.
        SourceModel.Parameter[] parameters = new SourceModel.Parameter[] {SourceModel.Parameter.make("var", false)};
        SourceModel.FunctionDefn functionDefn =
                SourceModel.FunctionDefn.Algebraic.make(DEFAULT_TEST_FUNCTION_NAME, Scope.PRIVATE, parameters, caseExpr);
       
        // Compile our elements to a new module.
        SourceModel.TopLevelSourceElement[] topLevelSourceElements = new SourceModel.TopLevelSourceElement[]{typeConsDefn, functionDefn};
        CompilerMessageLogger logger = compileAndRemoveModuleForTopLevelSourceElements(DEFAULT_TEST_MODULE_NAME, topLevelSourceElements);

       
        // Check that we got an error message of the expected type.
        CompilerMessage error = logger.getFirstError();
        assertTrue("No errors logged.", error != null);
        assertEquals(MessageKind.Error.RepeatedPatternVariableInFieldBindingPattern.class, error.getMessageKind().getClass());
    }
   

    /**
     * Helper function to get a source model for a type constructor consisting of a single data constructor with the given fields.
     * Each of the fields will have the Unit type, so the type constructor will take a single parameter of type Unit.
     *
     * @param textualFieldNames the names of the fields.  These must be valid textual field names.
     * @return the source model for the corresponding type constructor.
     */
    private static SourceModel.TypeConstructorDefn getSingleDataConstructorTypeConstructorDefinition(String[] textualFieldNames) {
        // Create the field names.
        FieldName[] fieldNames = new FieldName[textualFieldNames.length];
        for (int i = 0; i < fieldNames.length; i++) {
            fieldNames[i] = FieldName.make(textualFieldNames[i]);
        }

        // Create the type arguments.
        DataConsDefn.TypeArgument[] typeArguments = new DataConsDefn.TypeArgument[fieldNames.length];
        for (int i = 0; i < fieldNames.length; i++) {
            typeArguments[i] = DataConsDefn.TypeArgument.make(SourceModel.Name.Field.make(fieldNames[i]), SourceModel.TypeExprDefn.Unit.make(), false);
        }
       
        // Create the data constructor definition.
        DataConsDefn dataConsDefnWithRepeatedArgName = DataConsDefn.make(DEFAULT_DATA_CONS_NAME, Scope.PRIVATE, typeArguments);
       
        // Create the type constructor with the data cons definition.
        SourceModel.TypeConstructorDefn typeConsDefn =
            SourceModel.TypeConstructorDefn.AlgebraicType.make(DEFAULT_DATA_CONS_NAME, Scope.PRIVATE, null,
                                                               new DataConsDefn[]{dataConsDefnWithRepeatedArgName},
                                                               SourceModel.TypeConstructorDefn.NO_DERIVING_CLAUSE);
       
        return typeConsDefn;
    }
   
    /**
     * Check that a given set of outer defns gives a compile error when compiled to a module.
     * @param outerDefnTextLines the lines of text of the outer defns.
     * @param expectedErrorClass the class of the expected error.
     */
    private static void checkDefnForExpectedError(String[] outerDefnTextLines, Class<? extends MessageKind.Error> expectedErrorClass) {
        CompilerTestUtilities.checkDefnForExpectedError(outerDefnTextLines, expectedErrorClass, leccCALServices);
    }
   
    /**
     * Compile a new module containing only the provided top-level source elements, plus required imports, and remove it afterwards.
     * @param moduleName the name of the module.
     * @param topLevelSourceElements the top-level source elements to include.
     * @return a logger which logged the results of the compile.
     */
    private static CompilerMessageLogger compileAndRemoveModuleForTopLevelSourceElements(ModuleName moduleName, SourceModel.TopLevelSourceElement[] topLevelSourceElements) {
        // Get the workspace manager and the logger.
        WorkspaceManager workspaceManager = leccCALServices.getWorkspaceManager();
        CompilerMessageLogger logger = new MessageLogger();

        // Get the module defn.
        SourceModel.ModuleDefn moduleDefnWithoutImports = SourceModel.ModuleDefn.make(moduleName, new SourceModel.Import[0], topLevelSourceElements);
        SourceModel.ModuleDefn moduleDefn = SourceModelUtilities.ImportAugmenter.augmentWithImports(moduleDefnWithoutImports);

        // Compile the module.
        SourceModelModuleSource moduleSource = new SourceModelModuleSource(moduleDefn);
        workspaceManager.makeModule(moduleSource, logger);

        // Remove the module.
        leccCALServices.getWorkspaceManager().removeModule(DEFAULT_TEST_MODULE_NAME, new Status("Remove module status."));

        return logger;
    }
   
    /**
     * Test that a data constructor argument without a field name gives an error.
     */
    public void testUnnamedDCArgError() {
        // Get the workspace manager and the logger.
        WorkspaceManager workspaceManager = leccCALServices.getWorkspaceManager();
        CompilerMessageLogger logger = new MessageLogger();

        ModuleName moduleName = DEFAULT_TEST_MODULE_NAME;
        final String moduleCode =
            "module " + DEFAULT_TEST_MODULE_NAME + ";" +
            "import " + Prelude + ";" +
           
            "data Foo = Foo " + Prelude + ".Int;";

        // Compile the module.
        ModuleSourceDefinition moduleSourceDef = new StringModuleSourceDefinition(moduleName, moduleCode);
        workspaceManager.makeModule(moduleSourceDef, logger);

        // Remove the module.
        leccCALServices.getWorkspaceManager().removeModule(DEFAULT_TEST_MODULE_NAME, new Status("Remove module status."));
       
        // Check that we got an error message of the expected type.
        // Note: this can only check for a syntax error.  However, there are many ways in which a syntax error can occur.
        CompilerMessage error = logger.getFirstError();
        assertTrue("No errors logged.", error != null);
        assertEquals(MessageKind.Error.SyntaxErrorWithParaphrase.class, error.getMessageKind().getClass());
    }

    /**
     * Check that an error is given if, in a data constructor field selection expression, the dataCons-valued expression
     *   doesn't evaluate to the expected data constructor.
     *  
     * ie. "[].Cons.head" should give a runtime error, since the data constructor for "[]" is "Nil".
     *
     */
    public void testUnexpectedDCForSelectDCFieldExpr() throws GemCompilationException {

        try {       
            CALServicesTestUtilities.runNamedFunction(QualifiedName.make(CALPlatformTestModuleNames.M2, "selectDCFieldShouldFail"), leccCALServices);
            fail("This should have failed");
           
        } catch (CALExecutorException e){
            // Can't check position - no position information because the call is evaluated lazily.
        }

    }
   
    /**
     * Check for an error when the field name is not valid for a selectDCField expression.
     */
    public void testUnknownDCFieldNameInSelectDCFieldExpr() {
        {
            CompilerMessageLogger logger = compileSelectDCExprFromSingleElementDoubleList("hd");
           
            // Check that we got an error message of the expected type.
            CompilerMessage error = logger.getFirstError();
            assertTrue("No errors logged.", error != null);
            assertEquals(MessageKind.Error.UnknownDataConstructorField.class, error.getMessageKind().getClass());
        }
       
        {
            CompilerMessageLogger logger = compileSelectDCExprFromSingleElementDoubleList("#1");
           
            // Check that we got an error message of the expected type.
            CompilerMessage error = logger.getFirstError();
            assertTrue("No errors logged.", error != null);
            assertEquals(MessageKind.Error.UnknownDataConstructorField.class, error.getMessageKind().getClass());
        }
    }
   
    /**
     * A helper method for tests of errors in field selection.
     * Creates and compiles a function of the form:
     *   someFunctionName = [1.0].Cons.(calSourceFormFieldName);
     *
     * @param calSourceFormFieldName the cal source form of the field to select.
     * @return the message logger resulting from the compile
     */
    private CompilerMessageLogger compileSelectDCExprFromSingleElementDoubleList(String calSourceFormFieldName) {
       
        SourceModel.Expr dataConsValuedExpr = SourceModel.Expr.List.make(new SourceModel.Expr[]{SourceModel.Expr.makeDoubleValue(1.0)});
        SourceModel.Name.DataCons dataConsName = SourceModel.Name.DataCons.make(Prelude, CAL_Prelude.DataConstructors.Cons.getUnqualifiedName());
       
        SourceModel.Expr selectDCExpr = SourceModel.Expr.SelectDataConsField.make(dataConsValuedExpr, dataConsName, SourceModel.Name.Field.make(FieldName.make(calSourceFormFieldName)));
       
        SourceModel.FunctionDefn functionDefn =
            SourceModel.FunctionDefn.Algebraic.make(DEFAULT_TEST_FUNCTION_NAME, Scope.PRIVATE, null, selectDCExpr);

        // Compile our elements to a new module.
        SourceModel.TopLevelSourceElement[] topLevelSourceElements = new SourceModel.TopLevelSourceElement[]{functionDefn};
        CompilerMessageLogger logger = compileAndRemoveModuleForTopLevelSourceElements(DEFAULT_TEST_MODULE_NAME, topLevelSourceElements);

        return logger;
    }

    /**
     * Check for an error when an incorrect number of args appears in a case alt using pattern group syntax.
     */
    public void testIncorrectNArgsWithPatternGroup() {
        String[][] dcFieldNames = new String[][] {
                {"foo", "bar"},
                {"foo"},
                {"qux"}
        };
       
        SourceModel.TypeConstructorDefn typeConsDefn = getTypeConstructorDefinition(dcFieldNames);
       
        /*
         * foo var =
         *     case var of
         *     ( (DEFAULT_DATA_CONS_NAME)0 | (DEFAULT_DATA_CONS_NAME)1 ) foo bar -> True;
         *     ;
         */
        {
            SourceModel.Name.DataCons[] dataConsNames = new SourceModel.Name.DataCons[] {
                    SourceModel.Name.DataCons.make(QualifiedName.make(DEFAULT_TEST_MODULE_NAME, DEFAULT_DATA_CONS_NAME + 0)),
                    SourceModel.Name.DataCons.make(QualifiedName.make(DEFAULT_TEST_MODULE_NAME, DEFAULT_DATA_CONS_NAME + 1))
            };
           
            // Create a case alt using positional notation, where the names are the same.
            SourceModel.Pattern.Var[] patterns = {
                    SourceModel.Pattern.Var.make("foo"),
                    SourceModel.Pattern.Var.make("bar")
            };
           
            SourceModel.Expr.Case.Alt caseAlt =
                SourceModel.Expr.Case.Alt.UnpackDataCons.make(dataConsNames, patterns, SourceModel.Expr.makeBooleanValue(true));
           
            // Put into a case expression (conditional upon "var"), wrap with a function.
            SourceModel.Expr.Var varExpr = SourceModel.Expr.Var.makeUnqualified("var");
            SourceModel.Expr.Case caseExpr = SourceModel.Expr.Case.make(varExpr, new SourceModel.Expr.Case.Alt[]{caseAlt});
           
            SourceModel.Parameter[] parameters = new SourceModel.Parameter[] {SourceModel.Parameter.make("var", false)};
            SourceModel.FunctionDefn functionDefn =
                SourceModel.FunctionDefn.Algebraic.make(DEFAULT_TEST_FUNCTION_NAME, Scope.PRIVATE, parameters, caseExpr);
           
            // Compile our elements to a new module.
            SourceModel.TopLevelSourceElement[] topLevelSourceElements = new SourceModel.TopLevelSourceElement[]{typeConsDefn, functionDefn};
            CompilerMessageLogger logger = compileAndRemoveModuleForTopLevelSourceElements(DEFAULT_TEST_MODULE_NAME, topLevelSourceElements);
           
            // Check that we got a single error message of the expected type.
            CompilerMessage error = logger.getFirstError();
            assertTrue("No errors logged.", error != null);
            assertEquals(MessageKind.Error.ConstructorMustHaveExactlyNArgsInPattern.class, error.getMessageKind().getClass());
        }
    }
   
    /**
     * Check for an error when a repeated pattern appears in a case alt using pattern group syntax.
     */
    public void testRepeatedPatternWithPatternGroup() {
       
        String[][] dcFieldNames = new String[][] {
                {"foo", "bar"},
                {"baz"},
                {"qux"}
        };
       
        SourceModel.TypeConstructorDefn typeConsDefn = getTypeConstructorDefinition(dcFieldNames);
       
        /*
         * Repeated within an individual group.
         *
         * foo var =
         *     case var of
         *     ( (DEFAULT_DATA_CONS_NAME)0 | (DEFAULT_DATA_CONS_NAME)0 ) {} -> True;
         *     ;
         */
        {
            SourceModel.Name.DataCons[] dataConsNames = {
                    SourceModel.Name.DataCons.make(QualifiedName.make(DEFAULT_TEST_MODULE_NAME, DEFAULT_DATA_CONS_NAME + 0)),
                    SourceModel.Name.DataCons.make(QualifiedName.make(DEFAULT_TEST_MODULE_NAME, DEFAULT_DATA_CONS_NAME + 0))
            };
           
            SourceModel.FieldPattern[] fieldPatterns = SourceModel.FieldPattern.NO_FIELD_PATTERNS;
           
            SourceModel.Expr.Case.Alt caseAlt =
                SourceModel.Expr.Case.Alt.UnpackDataCons.make(dataConsNames, fieldPatterns, SourceModel.Expr.makeBooleanValue(true));
           
            // Put into a case expression (conditional upon "var"), wrap with a function.
            SourceModel.Expr.Var varExpr = SourceModel.Expr.Var.makeUnqualified("var");
            SourceModel.Expr.Case caseExpr = SourceModel.Expr.Case.make(varExpr, new SourceModel.Expr.Case.Alt[]{caseAlt});
           
            SourceModel.Parameter[] parameters = new SourceModel.Parameter[] {SourceModel.Parameter.make("var", false)};
            SourceModel.FunctionDefn functionDefn =
                SourceModel.FunctionDefn.Algebraic.make(DEFAULT_TEST_FUNCTION_NAME, Scope.PRIVATE, parameters, caseExpr);
           
            // Compile our elements to a new module.
            SourceModel.TopLevelSourceElement[] topLevelSourceElements = new SourceModel.TopLevelSourceElement[]{typeConsDefn, functionDefn};
            CompilerMessageLogger logger = compileAndRemoveModuleForTopLevelSourceElements(DEFAULT_TEST_MODULE_NAME, topLevelSourceElements);
           
            // Check that we got a single error message of the expected type.
            CompilerMessage error = logger.getFirstError();
            assertTrue("No errors logged.", error != null);
            assertEquals(MessageKind.Error.RepeatedPatternInCaseExpression.class, error.getMessageKind().getClass());
        }

        /*
         * Repeated among separate groups.
         *
         * foo var =
         *     case var of
         *     ( (DEFAULT_DATA_CONS_NAME)0 | (DEFAULT_DATA_CONS_NAME)1 ) {} -> True;
         *     ( (DEFAULT_DATA_CONS_NAME)2 | (DEFAULT_DATA_CONS_NAME)1 ) {} -> False;
         *     ;
         */
        {
            SourceModel.Name.DataCons[] dataConsNames1 = {
                    SourceModel.Name.DataCons.make(QualifiedName.make(DEFAULT_TEST_MODULE_NAME, DEFAULT_DATA_CONS_NAME + 0)),
                    SourceModel.Name.DataCons.make(QualifiedName.make(DEFAULT_TEST_MODULE_NAME, DEFAULT_DATA_CONS_NAME + 1))
            };
            SourceModel.Name.DataCons[] dataConsNames2 = {
                    SourceModel.Name.DataCons.make(QualifiedName.make(DEFAULT_TEST_MODULE_NAME, DEFAULT_DATA_CONS_NAME + 2)),
                    SourceModel.Name.DataCons.make(QualifiedName.make(DEFAULT_TEST_MODULE_NAME, DEFAULT_DATA_CONS_NAME + 1))
            };
           
            SourceModel.FieldPattern[] fieldPatterns = SourceModel.FieldPattern.NO_FIELD_PATTERNS;
           
            SourceModel.Expr.Case.Alt caseAlt1 =
                SourceModel.Expr.Case.Alt.UnpackDataCons.make(dataConsNames1, fieldPatterns, SourceModel.Expr.makeBooleanValue(true));
            SourceModel.Expr.Case.Alt caseAlt2 =
                SourceModel.Expr.Case.Alt.UnpackDataCons.make(dataConsNames2, fieldPatterns, SourceModel.Expr.makeBooleanValue(false));
           
            // Put into a case expression (conditional upon "var"), wrap with a function.
            SourceModel.Expr.Var varExpr = SourceModel.Expr.Var.makeUnqualified("var");
            SourceModel.Expr.Case caseExpr = SourceModel.Expr.Case.make(varExpr, new SourceModel.Expr.Case.Alt[]{caseAlt1, caseAlt2});
           
            SourceModel.Parameter[] parameters = new SourceModel.Parameter[] {SourceModel.Parameter.make("var", false)};
            SourceModel.FunctionDefn functionDefn =
                SourceModel.FunctionDefn.Algebraic.make(DEFAULT_TEST_FUNCTION_NAME, Scope.PRIVATE, parameters, caseExpr);
           
            // Compile our elements to a new module.
            SourceModel.TopLevelSourceElement[] topLevelSourceElements = new SourceModel.TopLevelSourceElement[]{typeConsDefn, functionDefn};
            CompilerMessageLogger logger = compileAndRemoveModuleForTopLevelSourceElements(DEFAULT_TEST_MODULE_NAME, topLevelSourceElements);
           
            // Check that we got a single error message of the expected type.
            List<CompilerMessage> errorList = logger.getCompilerMessages();
            assertFalse("No errors logged.", errorList.isEmpty());
            CompilerMessage error = errorList.get(0);
            assertEquals(MessageKind.Error.RepeatedPatternInCaseExpression.class, error.getMessageKind().getClass());
        }

    }
   
    /**
     * Check for an error when a repeated var name appears in a case alt using pattern group syntax.
     */
    public void testRepeatedVarNameWithPatternGroup() {
        String[][] dcFieldNames = new String[][] {
                {"foo", "bar"},
                {"foo", "baz"},
                {"qux"}
        };
       
        SourceModel.TypeConstructorDefn typeConsDefn = getTypeConstructorDefinition(dcFieldNames);
       
        /*
         * foo var =
         *     case var of
         *     ( (DEFAULT_DATA_CONS_NAME)0 | (DEFAULT_DATA_CONS_NAME)1 ) foo foo -> True;
         *     ;
         */
        {
            SourceModel.Name.DataCons[] dataConsNames = new SourceModel.Name.DataCons[] {
                    SourceModel.Name.DataCons.make(QualifiedName.make(DEFAULT_TEST_MODULE_NAME, DEFAULT_DATA_CONS_NAME + 0)),
                    SourceModel.Name.DataCons.make(QualifiedName.make(DEFAULT_TEST_MODULE_NAME, DEFAULT_DATA_CONS_NAME + 1))
            };
           
            // Create a case alt using positional notation, where the names are the same.
            SourceModel.Pattern.Var[] patterns = {
                    SourceModel.Pattern.Var.make("foo"),
                    SourceModel.Pattern.Var.make("foo")
            };
           
            SourceModel.Expr.Case.Alt caseAlt =
                SourceModel.Expr.Case.Alt.UnpackDataCons.make(dataConsNames, patterns, SourceModel.Expr.makeBooleanValue(true));
           
            // Put into a case expression (conditional upon "var"), wrap with a function.
            SourceModel.Expr.Var varExpr = SourceModel.Expr.Var.makeUnqualified("var");
            SourceModel.Expr.Case caseExpr = SourceModel.Expr.Case.make(varExpr, new SourceModel.Expr.Case.Alt[]{caseAlt});
           
            SourceModel.Parameter[] parameters = new SourceModel.Parameter[] {SourceModel.Parameter.make("var", false)};
            SourceModel.FunctionDefn functionDefn =
                SourceModel.FunctionDefn.Algebraic.make(DEFAULT_TEST_FUNCTION_NAME, Scope.PRIVATE, parameters, caseExpr);
           
            // Compile our elements to a new module.
            SourceModel.TopLevelSourceElement[] topLevelSourceElements = new SourceModel.TopLevelSourceElement[]{typeConsDefn, functionDefn};
            CompilerMessageLogger logger = compileAndRemoveModuleForTopLevelSourceElements(DEFAULT_TEST_MODULE_NAME, topLevelSourceElements);
           
            // Check that we got a single error message of the expected type.
            CompilerMessage error = logger.getFirstError();
            assertTrue("No errors logged.", error != null);
            assertEquals(MessageKind.Error.RepeatedVariableUsedInBinding.class, error.getMessageKind().getClass());
        }

        /*
         * foo var =
         *     case var of
         *     ( (DEFAULT_DATA_CONS_NAME)0 | (DEFAULT_DATA_CONS_NAME)1 ) {foo, foo} -> True;
         *     ;
         */
        {
            SourceModel.Name.DataCons[] dataConsNames = new SourceModel.Name.DataCons[] {
                    SourceModel.Name.DataCons.make(QualifiedName.make(DEFAULT_TEST_MODULE_NAME, DEFAULT_DATA_CONS_NAME + 0)),
                    SourceModel.Name.DataCons.make(QualifiedName.make(DEFAULT_TEST_MODULE_NAME, DEFAULT_DATA_CONS_NAME + 1))
            };
           
            // Create a case alt using matching notation, where the names are the same.
            FieldName fooFieldName = FieldName.make("foo");
            SourceModel.FieldPattern[] patterns = {
                    SourceModel.FieldPattern.make(SourceModel.Name.Field.make(fooFieldName), null),
                    SourceModel.FieldPattern.make(SourceModel.Name.Field.make(fooFieldName), null)
            };
           
            SourceModel.Expr.Case.Alt caseAlt =
                SourceModel.Expr.Case.Alt.UnpackDataCons.make(dataConsNames, patterns, SourceModel.Expr.makeBooleanValue(true));
           
            // Put into a case expression (conditional upon "var"), wrap with a function.
            SourceModel.Expr.Var varExpr = SourceModel.Expr.Var.makeUnqualified("var");
            SourceModel.Expr.Case caseExpr = SourceModel.Expr.Case.make(varExpr, new SourceModel.Expr.Case.Alt[]{caseAlt});
           
            SourceModel.Parameter[] parameters = new SourceModel.Parameter[] {SourceModel.Parameter.make("var", false)};
            SourceModel.FunctionDefn functionDefn =
                SourceModel.FunctionDefn.Algebraic.make(DEFAULT_TEST_FUNCTION_NAME, Scope.PRIVATE, parameters, caseExpr);
           
            // Compile our elements to a new module.
            SourceModel.TopLevelSourceElement[] topLevelSourceElements = new SourceModel.TopLevelSourceElement[]{typeConsDefn, functionDefn};
            CompilerMessageLogger logger = compileAndRemoveModuleForTopLevelSourceElements(DEFAULT_TEST_MODULE_NAME, topLevelSourceElements);
           
            // Check that we got a single error message of the expected type.
            List<CompilerMessage> errorList = logger.getCompilerMessages();
            assertFalse("No errors logged.", errorList.isEmpty());
            CompilerMessage error = errorList.get(0);
            assertEquals(MessageKind.Error.RepeatedFieldNameInFieldBindingPattern.class, error.getMessageKind().getClass());
        }
    }
   
    /**
     * Check for an error when a non-existent var name appears in a case alt using pattern group syntax.
     */
    public void testNonExistentVarWithPatternGroup() {
       
        String[][] dcFieldNames = new String[][] {
                {"foo", "bar"},
                {"foo"},
                {"qux"}
        };
       
        SourceModel.TypeConstructorDefn typeConsDefn = getTypeConstructorDefinition(dcFieldNames);

        /*
         * Exists - should be ok.
         *
         * foo var =
         *     case var of
         *     ( (DEFAULT_DATA_CONS_NAME)0 | (DEFAULT_DATA_CONS_NAME)1 ) {foo} -> foo;
         *     ;
         */
        {
            SourceModel.Name.DataCons[] dataConsNames = {
                    SourceModel.Name.DataCons.make(QualifiedName.make(DEFAULT_TEST_MODULE_NAME, DEFAULT_DATA_CONS_NAME + 0)),
                    SourceModel.Name.DataCons.make(QualifiedName.make(DEFAULT_TEST_MODULE_NAME, DEFAULT_DATA_CONS_NAME + 1))
            };
           
            FieldName fieldName = FieldName.make("foo");
            SourceModel.FieldPattern[] fieldPatterns = {SourceModel.FieldPattern.make(SourceModel.Name.Field.make(fieldName), null)};
           
            SourceModel.Expr.Case.Alt caseAlt =
                SourceModel.Expr.Case.Alt.UnpackDataCons.make(dataConsNames, fieldPatterns, SourceModel.Expr.Var.makeUnqualified("foo"));
           
            // Put into a case expression (conditional upon "var"), wrap with a function.
            SourceModel.Expr.Var varExpr = SourceModel.Expr.Var.makeUnqualified("var");
            SourceModel.Expr.Case caseExpr = SourceModel.Expr.Case.make(varExpr, new SourceModel.Expr.Case.Alt[]{caseAlt});
           
            SourceModel.Parameter[] parameters = new SourceModel.Parameter[] {SourceModel.Parameter.make("var", false)};
            SourceModel.FunctionDefn functionDefn =
                SourceModel.FunctionDefn.Algebraic.make(DEFAULT_TEST_FUNCTION_NAME, Scope.PRIVATE, parameters, caseExpr);
           
            // Compile our elements to a new module.
            SourceModel.TopLevelSourceElement[] topLevelSourceElements = new SourceModel.TopLevelSourceElement[]{typeConsDefn, functionDefn};
            CompilerMessageLogger logger = compileAndRemoveModuleForTopLevelSourceElements(DEFAULT_TEST_MODULE_NAME, topLevelSourceElements);
           
            // Check that we got a single error message of the expected type.
            List<CompilerMessage> errorList = logger.getCompilerMessages();
            assertTrue("Errors logged.", errorList.isEmpty());
        }
       
        /*
         * Doesn't exist..
         *
         * foo var =
         *     case var of
         *     ( (DEFAULT_DATA_CONS_NAME)0 | (DEFAULT_DATA_CONS_NAME)1 ) {bar} -> bar;
         *     ;
         */
        {
            SourceModel.Name.DataCons[] dataConsNames = {
                    SourceModel.Name.DataCons.make(QualifiedName.make(DEFAULT_TEST_MODULE_NAME, DEFAULT_DATA_CONS_NAME + 0)),
                    SourceModel.Name.DataCons.make(QualifiedName.make(DEFAULT_TEST_MODULE_NAME, DEFAULT_DATA_CONS_NAME + 1))
            };
           
            FieldName fieldName = FieldName.make("bar");
            SourceModel.FieldPattern[] fieldPatterns = {SourceModel.FieldPattern.make(SourceModel.Name.Field.make(fieldName), null)};
           
            SourceModel.Expr.Case.Alt caseAlt =
                SourceModel.Expr.Case.Alt.UnpackDataCons.make(dataConsNames, fieldPatterns, SourceModel.Expr.Var.makeUnqualified("bar"));
           
            // Put into a case expression (conditional upon "var"), wrap with a function.
            SourceModel.Expr.Var varExpr = SourceModel.Expr.Var.makeUnqualified("var");
            SourceModel.Expr.Case caseExpr = SourceModel.Expr.Case.make(varExpr, new SourceModel.Expr.Case.Alt[]{caseAlt});
           
            SourceModel.Parameter[] parameters = new SourceModel.Parameter[] {SourceModel.Parameter.make("var", false)};
            SourceModel.FunctionDefn functionDefn =
                SourceModel.FunctionDefn.Algebraic.make(DEFAULT_TEST_FUNCTION_NAME, Scope.PRIVATE, parameters, caseExpr);
           
            // Compile our elements to a new module.
            SourceModel.TopLevelSourceElement[] topLevelSourceElements = new SourceModel.TopLevelSourceElement[]{typeConsDefn, functionDefn};
            CompilerMessageLogger logger = compileAndRemoveModuleForTopLevelSourceElements(DEFAULT_TEST_MODULE_NAME, topLevelSourceElements);
           
            // Check that we got a single error message of the expected type.
            List<CompilerMessage> errorList = logger.getCompilerMessages();
            assertFalse("No errors logged.", errorList.isEmpty());
            CompilerMessage error = errorList.get(0);
            assertEquals(MessageKind.Error.UnknownDataConstructorField.class, error.getMessageKind().getClass());
        }
    }
   
    /**
     * Check for an error when a case alt unpack pattern using pattern group syntax uses an argument which cannot
     * be assigned a type.
     */
    public void testDataConstructorPatternGroupArgumentNotTypeable() {
        String[][] dcFieldNames = new String[][] {
                {"arg"},
                {"arg"}
        };
       
        SourceModel.TypeExprDefn[][] dcFieldTypes = new SourceModel.TypeExprDefn[][] {
                {SourceModel.TypeExprDefn.TypeCons.make(Prelude, CAL_Prelude.TypeConstructors.Int.getUnqualifiedName())},
                {SourceModel.TypeExprDefn.TypeCons.make(Prelude, CAL_Prelude.TypeConstructors.Double.getUnqualifiedName())}
        };
       
        SourceModel.TypeConstructorDefn typeConsDefn = getTypeConstructorDefinition(dcFieldNames, dcFieldTypes);

        SourceModel.Name.DataCons[] dataConsNames = {
                SourceModel.Name.DataCons.make(QualifiedName.make(DEFAULT_TEST_MODULE_NAME, DEFAULT_DATA_CONS_NAME + 0)),
                SourceModel.Name.DataCons.make(QualifiedName.make(DEFAULT_TEST_MODULE_NAME, DEFAULT_DATA_CONS_NAME + 1))
        };
       
        SourceModel.Pattern argPattern = SourceModel.Pattern.Var.make("arg");
       
        /*
         * foo var =
         *     case var of
         *     ( (DEFAULT_DATA_CONS_NAME)0 | (DEFAULT_DATA_CONS_NAME)1 ) arg -> arg;
         *     ;
         */
        {
            SourceModel.Pattern[] argPatterns = {argPattern};
           
            SourceModel.Expr altExpr = SourceModel.Expr.Var.makeUnqualified("arg");
           
            SourceModel.Expr.Case.Alt caseAlt =
                SourceModel.Expr.Case.Alt.UnpackDataCons.make(dataConsNames, argPatterns, altExpr);
           
            // Put into a case expression (conditional upon "var"), wrap with a function.
            SourceModel.Expr.Var varExpr = SourceModel.Expr.Var.makeUnqualified("var");
            SourceModel.Expr.Case caseExpr = SourceModel.Expr.Case.make(varExpr, new SourceModel.Expr.Case.Alt[]{caseAlt});
           
            SourceModel.Parameter[] parameters = new SourceModel.Parameter[] {SourceModel.Parameter.make("var", false)};
            SourceModel.FunctionDefn functionDefn =
                SourceModel.FunctionDefn.Algebraic.make(DEFAULT_TEST_FUNCTION_NAME, Scope.PRIVATE, parameters, caseExpr);
           
            // Compile our elements to a new module.
            SourceModel.TopLevelSourceElement[] topLevelSourceElements = new SourceModel.TopLevelSourceElement[]{typeConsDefn, functionDefn};
            CompilerMessageLogger logger = compileAndRemoveModuleForTopLevelSourceElements(DEFAULT_TEST_MODULE_NAME, topLevelSourceElements);
           
            // Check that we got a single error message of the expected type.
            CompilerMessage error = logger.getFirstError();
            assertTrue("No errors logged.", error != null);
            assertEquals(MessageKind.Error.DataConstructorPatternGroupArgumentNotTypeable.class, error.getMessageKind().getClass());
        }

        /*
         * foo var =
         *     case var of
         *     ( (DEFAULT_DATA_CONS_NAME)0 | (DEFAULT_DATA_CONS_NAME)1 ) arg -> True;
         *     ;
         *
         * arg is unused, but we still should check whether its type is unifiable for all dc's.
         */
        {
            SourceModel.Pattern[] argPatterns = {argPattern};
           
            SourceModel.Expr.Case.Alt caseAlt =
                SourceModel.Expr.Case.Alt.UnpackDataCons.make(dataConsNames, argPatterns, SourceModel.Expr.makeBooleanValue(true));
           
            // Put into a case expression (conditional upon "var"), wrap with a function.
            SourceModel.Expr.Var varExpr = SourceModel.Expr.Var.makeUnqualified("var");
            SourceModel.Expr.Case caseExpr = SourceModel.Expr.Case.make(varExpr, new SourceModel.Expr.Case.Alt[]{caseAlt});
           
            SourceModel.Parameter[] parameters = new SourceModel.Parameter[] {SourceModel.Parameter.make("var", false)};
            SourceModel.FunctionDefn functionDefn =
                SourceModel.FunctionDefn.Algebraic.make(DEFAULT_TEST_FUNCTION_NAME, Scope.PRIVATE, parameters, caseExpr);
           
            // Compile our elements to a new module.
            SourceModel.TopLevelSourceElement[] topLevelSourceElements = new SourceModel.TopLevelSourceElement[]{typeConsDefn, functionDefn};
            CompilerMessageLogger logger = compileAndRemoveModuleForTopLevelSourceElements(DEFAULT_TEST_MODULE_NAME, topLevelSourceElements);
           
            // Check that we got a single error message of the expected type.
            List<CompilerMessage> errorList = logger.getCompilerMessages();
            assertFalse("No errors logged.", errorList.isEmpty());
            CompilerMessage error = errorList.get(0);
            assertEquals(MessageKind.Error.DataConstructorPatternGroupArgumentNotTypeable.class, error.getMessageKind().getClass());
        }

        /*
         * foo var =
         *     case var of
         *     ( (DEFAULT_DATA_CONS_NAME)0 | (DEFAULT_DATA_CONS_NAME)1 ) {arg} -> True;
         *     ;
         */
        {
            FieldName fieldName = FieldName.make("arg");
            SourceModel.FieldPattern[] fieldPatterns = {SourceModel.FieldPattern.make(SourceModel.Name.Field.make(fieldName), argPattern)};
           
            SourceModel.Expr.Case.Alt caseAlt =
                SourceModel.Expr.Case.Alt.UnpackDataCons.make(dataConsNames, fieldPatterns, SourceModel.Expr.makeBooleanValue(true));
           
            // Put into a case expression (conditional upon "var"), wrap with a function.
            SourceModel.Expr.Var varExpr = SourceModel.Expr.Var.makeUnqualified("var");
            SourceModel.Expr.Case caseExpr = SourceModel.Expr.Case.make(varExpr, new SourceModel.Expr.Case.Alt[]{caseAlt});
           
            SourceModel.Parameter[] parameters = new SourceModel.Parameter[] {SourceModel.Parameter.make("var", false)};
            SourceModel.FunctionDefn functionDefn =
                SourceModel.FunctionDefn.Algebraic.make(DEFAULT_TEST_FUNCTION_NAME, Scope.PRIVATE, parameters, caseExpr);
           
            // Compile our elements to a new module.
            SourceModel.TopLevelSourceElement[] topLevelSourceElements = new SourceModel.TopLevelSourceElement[]{typeConsDefn, functionDefn};
            CompilerMessageLogger logger = compileAndRemoveModuleForTopLevelSourceElements(DEFAULT_TEST_MODULE_NAME, topLevelSourceElements);
           
            // Check that we got a single error message of the expected type.
            List<CompilerMessage> errorList = logger.getCompilerMessages();
            assertFalse("No errors logged.", errorList.isEmpty());
            CompilerMessage error = errorList.get(0);
            assertEquals(MessageKind.Error.DataConstructorPatternGroupArgumentNotTypeable.class, error.getMessageKind().getClass());
        }
    }
   
    /**
     * Helper function to get a source model for a type constructor consisting of a number of data constructors with given fields.
     * Each of the fields will have the Unit type.
     *
     * The definition will be:
     * data private (DEFAULT_DATA_CONS_NAME) =
     *   private (DEFAULT_DATA_CONS_NAME)0
     *       fieldNamesSourceFormArray[0][0] :: ()
     *       fieldNamesSourceFormArray[0][1] :: ()
     *       ...
     *   private (DEFAULT_DATA_CONS_NAME)1
     *       fieldNamesSourceFormArray[1][0] :: ()
     *       fieldNamesSourceFormArray[1][1] :: ()
     *       ...
     *   private (DEFAULT_DATA_CONS_NAME)2
     *       fieldNamesSourceFormArray[2][0] :: ()
     *       fieldNamesSourceFormArray[2][1] :: ()
     *       ...
     *   ...
     *
     * @param fieldNamesSourceFormArray the names of the fields.  These must be valid cal source forms.
     *   The string array at index n will give the names of the fields for data constructor n.
     * @return the source model for the corresponding type constructor.
     */
    private static SourceModel.TypeConstructorDefn getTypeConstructorDefinition(String[][] fieldNamesSourceFormArray) {
       
        // Create an array for corresponding type expr defns.
        int nDCDefs = fieldNamesSourceFormArray.length;
        SourceModel.TypeExprDefn[][] fieldTypesArray = new SourceModel.TypeExprDefn[nDCDefs][];
       
        // Fill the arrays with the unit type.
        for (int i = 0; i < nDCDefs; i++) {
            String[] fieldNamesSourceForm = fieldNamesSourceFormArray[i];
            SourceModel.TypeExprDefn[] typeExprDefnArray = new SourceModel.TypeExprDefn[fieldNamesSourceForm.length];
            fieldTypesArray[i] = typeExprDefnArray;
           
            Arrays.fill(typeExprDefnArray, SourceModel.TypeExprDefn.Unit.make());
        }

        // Defer to the more general method.
        return getTypeConstructorDefinition(fieldNamesSourceFormArray, fieldTypesArray);
    }

    /**
     * Helper function to get a source model for a type constructor consisting of a number of data constructors with given fields and types.
     * The types should not be parametrized -- ie. the resulting data constructor should have no arrows in its type.
     *
     * The definition will be:
     * data private (DEFAULT_DATA_CONS_NAME) =
     *   private (DEFAULT_DATA_CONS_NAME)0
     *       fieldNamesSourceFormArray[0][0] :: fieldTypesArray[0][0]
     *       fieldNamesSourceFormArray[0][1] :: fieldTypesArray[0][1]
     *       ...
     *   private (DEFAULT_DATA_CONS_NAME)1
     *       fieldNamesSourceFormArray[1][0] :: fieldTypesArray[1][0]
     *       fieldNamesSourceFormArray[1][1] :: fieldTypesArray[1][1]
     *       ...
     *   private (DEFAULT_DATA_CONS_NAME)2
     *       fieldNamesSourceFormArray[2][0] :: fieldTypesArray[2][0]
     *       fieldNamesSourceFormArray[2][1] :: fieldTypesArray[2][1]
     *       ...
     *   ...
     *
     * @param fieldNamesSourceFormArray the names of the fields.  These must be valid cal source forms.
     *   The string array at index n will give the names of the fields for data constructor n.
     * @param fieldTypesArray the types of the fields.  This should have the same structure as fieldNamesSourceFormArray.
     *   The argument at fieldTypesArray[i][j] will be the type of the argument corresponding to fieldNamesSourceFormArray[i][j].
     * @return the source model for the corresponding type constructor.
     */
    private static SourceModel.TypeConstructorDefn getTypeConstructorDefinition(
            String[][] fieldNamesSourceFormArray, SourceModel.TypeExprDefn[][] fieldTypesArray) {
       
        int nDCDefs = fieldNamesSourceFormArray.length;
        DataConsDefn[] dcDefs = new DataConsDefn[nDCDefs];
       
        for (int i = 0; i < nDCDefs; i++) {
            String[] fieldNamesSourceForm = fieldNamesSourceFormArray[i];
           
            // Create the field names.
            FieldName[] fieldNames = new FieldName[fieldNamesSourceForm.length];
            for (int j = 0; j < fieldNames.length; j++) {
                fieldNames[j] = FieldName.make(fieldNamesSourceForm[j]);
            }
           
            // Create the type arguments.
            DataConsDefn.TypeArgument[] typeArguments = new DataConsDefn.TypeArgument[fieldNames.length];
            for (int j = 0; j < fieldNames.length; j++) {
                typeArguments[j] = DataConsDefn.TypeArgument.make(SourceModel.Name.Field.make(fieldNames[j]), fieldTypesArray[i][j], false);
            }
           
            // Create the data constructor definition.
            String dcName = DEFAULT_DATA_CONS_NAME + i;
            dcDefs[i] = DataConsDefn.make(dcName, Scope.PRIVATE, typeArguments);
        }

        // Create the type constructor with the data cons definition.
        SourceModel.TypeConstructorDefn typeConsDefn =
            SourceModel.TypeConstructorDefn.AlgebraicType.make(DEFAULT_DATA_CONS_NAME, Scope.PRIVATE, null,
                                                               dcDefs, SourceModel.TypeConstructorDefn.NO_DERIVING_CLAUSE);
       
        return typeConsDefn;
    }
   
    /**
     * Check for an error when a case alt unpack pattern on ints uses the same int.
     */
    public void testIntCaseCollision() {
        {
            String[] outerDefnText = {
                    "testIntCase x =        ",
                    "    case x of          ",
                    "   (1 | 2)     -> " + Prelude + ".True;",
                    "    1          -> " + Prelude + ".True;",
                    "    ;"
            };
           
            checkDefnForExpectedError(outerDefnText, MessageKind.Error.RepeatedPatternValueInCaseExpression.class);
        }
        {
            String[] outerDefnText = {
                    "testIntCase x =        ",
                    "    case x of          ",
                    "   (1 | 1)     -> " + Prelude + ".True;",
                    "    ;"
            };
           
            checkDefnForExpectedError(outerDefnText, MessageKind.Error.RepeatedPatternValueInCaseExpression.class);
        }
        {
            String[] outerDefnText = {
                    "testIntCase x =",
                    "    case x of",
                    "    -0         -> " + Prelude + ".True;",
                    "    0          -> " + Prelude + ".True;",
                    "    ;"
            };

            checkDefnForExpectedError(outerDefnText, MessageKind.Error.RepeatedPatternValueInCaseExpression.class);
        }
    }
   
    /**
     * Check for an error when a case alt unpack pattern on ints uses an integer literal out of range of the Int type.
     */
    public void testIntLiteralOutOfRange() {
        {
            String[] outerDefnText = {
                    "testIntCase x =",
                    "    case x of",
                    "    - 1873468172648912649812764912837643       -> " + Prelude + ".True;",
                    "    ;"
            };
            checkDefnForExpectedError(outerDefnText, MessageKind.Error.IntLiteralOutOfRange.class);
        }
    }
   
    /**
     * Check for an error when a case alt unpack pattern on chars uses the same char.
     */
    public void testCharCaseCollision() {
        {
            String[] outerDefnText = {
                    "testCharCase x =   ",
                    "    case x of      ",
                    "    ('a' | 'b')  -> " + Prelude + ".True;",
                    "     'a'         -> " + Prelude + ".True;",
                    "     ;"
            };
            checkDefnForExpectedError(outerDefnText, MessageKind.Error.RepeatedPatternValueInCaseExpression.class);
        }
        {
            String[] outerDefnText = {
                    "testCharCase x =   ",
                    "    case x of      ",
                    "    ('a' | 'a')  -> " + Prelude + ".True;",
                    "    ; "
            };
            checkDefnForExpectedError(outerDefnText, MessageKind.Error.RepeatedPatternValueInCaseExpression.class);
        }
        {
            String[] outerDefnText = {
                    "testCharCase x =   ",
                    "    case x of      ",
                    "    '\045'       -> " + Prelude + ".True;",
                    "    '%'          -> " + Prelude + ".True;",
                    "    ; "
            };
            checkDefnForExpectedError(outerDefnText, MessageKind.Error.RepeatedPatternValueInCaseExpression.class);
        }
        {
            String[] outerDefnText = {
                    "testCharCase x =   ",
                    "    case x of      ",
                    "    '\u0025'     -> " + Prelude + ".True;",
                    "    '%'          -> " + Prelude + ".True;",
                    "    ; "
            };
            checkDefnForExpectedError(outerDefnText, MessageKind.Error.RepeatedPatternValueInCaseExpression.class);
        }
        {
            String[] outerDefnText = {
                    "testCharCase x =   ",
                    "    case x of      ",
                    "    '\045'       -> " + Prelude + ".True;",
                    "    '\u0025'     -> " + Prelude + ".True;",
                    "    ; "
            };
            checkDefnForExpectedError(outerDefnText, MessageKind.Error.RepeatedPatternValueInCaseExpression.class);
        }
       
    }
}
TOP

Related Classes of org.openquark.cal.compiler.DataConstructorCode_Test

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.