/******************************************************************************* * Copyright (c) 2007 IBM Corporation, 2008 CWI * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Robert Fuhrer (rfuhrer@watson.ibm.com) - initial API and implementation * Jurgen Vinju (jurgen@vinju.org) * Anya Helene Bagge *******************************************************************************/ package org.rascalmpl.value; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.rascalmpl.value.exceptions.FactTypeDeclarationException; import org.rascalmpl.value.exceptions.FactTypeUseException; import org.rascalmpl.value.random.RandomTypeGenerator; import org.rascalmpl.value.type.Type; import org.rascalmpl.value.type.TypeFactory; import org.rascalmpl.value.type.TypeStore; import junit.framework.TestCase; public class TestType extends TestCase { private static final int COMBINATION_UPPERBOUND = 5; private static TypeFactory ft = TypeFactory.getInstance(); private static TypeStore ts = new TypeStore(); private static List<Type> basic = new LinkedList<>(); private static List<Type> allTypes = new LinkedList<>(); static { try { basic.add(ft.integerType()); basic.add(ft.realType()); basic.add(ft.sourceLocationType()); basic.add(ft.stringType()); basic.add(ft.nodeType()); allTypes.add(ft.valueType()); allTypes.add(ft.numberType()); allTypes.addAll(basic); for (int i = 0; i < 2; i++) { recombine(); } RandomTypeGenerator rg = new RandomTypeGenerator(); for (int i = 0; i < 1000; i++) { allTypes.add(rg.next(10)); } } catch (FactTypeUseException e) { throw new RuntimeException("fact type error in setup", e); } } private static void recombine() throws FactTypeUseException { List<Type> newTypes = new LinkedList<>(); int max1 = COMBINATION_UPPERBOUND; for (Type t1 : allTypes) { newTypes.add(ft.tupleType(t1)); newTypes.add(ft.relType(t1)); newTypes.add(ft.setType(t1)); newTypes.add(ft.aliasType(ts, "type_" + allTypes.size() + newTypes.size(), t1)); Type adt = ft.abstractDataType(ts, "adt_" + newTypes.size()); newTypes.add(ft.constructor(ts, adt, "cons_" + newTypes.size())); newTypes.add(adt); int max2 = COMBINATION_UPPERBOUND; for (Type t2 : allTypes) { newTypes.add(ft.tupleType(t1, t2)); newTypes.add(ft.tupleType(t1, "a" + newTypes.size(), t2, "b" + newTypes.size())); newTypes.add(ft.relType(t1, t2)); newTypes.add(ft.mapType(t1, t2)); newTypes.add(ft.mapType(t1, "a" + newTypes.size(), t2, "b" + newTypes.size())); newTypes.add(ft.constructor(ts, adt, "cons_" + newTypes.size(), t1, "a" + newTypes.size(), t2, "b" + newTypes.size())); int max3 = COMBINATION_UPPERBOUND; for (Type t3 : allTypes) { newTypes.add(ft.tupleType(t1, t2, t3)); newTypes.add(ft.relType(t1, t2, t3)); newTypes.add(ft.constructor(ts, adt, "cons_" + newTypes.size(), t1, "a" + newTypes.size(), t2, "b"+ newTypes.size(), t3, "c" + newTypes.size())); if (max3-- == 0) { break; } } if (max2-- == 0) { break; } } if (max1-- == 0) { break; } } allTypes.addAll(newTypes); } public void testRelations() { for (Type t : allTypes) { if (t.isSet() && t.getElementType().isTuple() && !t.isRelation()) { fail("Sets of tuples should be relations"); } if (t.isRelation() && !t.getElementType().isTuple()) { fail("Relations should contain tuples"); } } } public void testParameterizedAlias() { Type T = ft.parameterType("T"); TypeStore ts = new TypeStore(); // DiGraph[&T] = rel[&T from ,&T to] Type DiGraph = ft.aliasType(ts, "DiGraph", ft.relType(T, "from", T, "to"), T); Type IntInstance = ft.relType(ft.integerType(), ft.integerType()); Type ValueInstance = ft.relType(ft.valueType(), ft.valueType()); // before instantiation, the parameterized type rel[&T, &T] is a // sub-type of rel[value, value] assertTrue(IntInstance.isSubtypeOf(DiGraph)); assertFalse(DiGraph.isSubtypeOf(IntInstance)); assertTrue(DiGraph.isSubtypeOf(ValueInstance)); Map<Type, Type> bindings = new HashMap<>(); DiGraph.match(IntInstance, bindings); assertTrue(bindings.get(T) == ft.integerType()); // after instantiation, the parameterized type is an alias for rel[int, // int] Type ComputedInstance = DiGraph.instantiate(bindings); // DiGraph[int] assertTrue(ComputedInstance.equivalent(IntInstance)); assertFalse(ValueInstance.isSubtypeOf(ComputedInstance)); // and sub-typing remains co-variant: assertTrue(IntInstance.isSubtypeOf(ValueInstance)); assertTrue(ComputedInstance.isSubtypeOf(ValueInstance)); try { ft.aliasType(ts, "DiGraph", ft.setType(T), T); fail("should not be able to redefine alias"); } catch (FactTypeDeclarationException e) { // this should happen } } public void testADT() { Type E = ft.abstractDataType(ts, "E"); assertTrue( "Abstract data-types are composed of constructors which are tree nodes", E.isSubtypeOf(ft.nodeType())); assertTrue(E.isSubtypeOf(ft.valueType())); assertTrue(E.isSubtypeOf(ft.nodeType())); assertTrue(E.lub(ft.nodeType()).isNode()); assertTrue(ft.nodeType().lub(E).isNode()); Type f = ft.constructor(ts, E, "f", ft.integerType(), "i"); Type g = ft.constructor(ts, E, "g", ft.integerType(), "j"); assertTrue(f.isSubtypeOf(ft.nodeType())); assertTrue(f.lub(ft.nodeType()).isNode()); assertTrue(ft.nodeType().lub(f).isNode()); Type a = ft.aliasType(ts, "a", ft.integerType()); assertFalse(f.isSubtypeOf(ft.integerType()) || f.isSubtypeOf(ft.stringType()) || f.isSubtypeOf(a)); assertFalse(g.isSubtypeOf(ft.integerType()) || g.isSubtypeOf(ft.stringType()) || g.isSubtypeOf(a)); assertFalse("constructors are subtypes of the adt", !f.isSubtypeOf(E) || !g.isSubtypeOf(E)); assertFalse("alternative constructors should be incomparable", f .isSubtypeOf(g) || g.isSubtypeOf(f)); assertTrue("A constructor should be a node", f.isSubtypeOf(ft .nodeType())); assertTrue("A constructor should be a node", g.isSubtypeOf(ft .nodeType())); } public void testVoid() { for (Type t : allTypes) { if(t.isSubtypeOf(ft.voidType())) { assertFalse(true); } } } public void testVoidProblem1() { assertFalse(ft.listType(ft.voidType()).isSubtypeOf(ft.voidType())); assertFalse(ft.setType(ft.voidType()).isSubtypeOf(ft.voidType())); assertFalse(ft.relType(ft.voidType()).isSubtypeOf(ft.voidType())); assertFalse(ft.tupleType(ft.voidType()).isSubtypeOf(ft.voidType())); assertFalse(ft.mapType(ft.voidType(),ft.voidType()).isSubtypeOf(ft.voidType())); } public void testIsSubtypeOf() { for (Type t : allTypes) { if (!t.isSubtypeOf(t)) { fail("any type should be a subtype of itself: " + t); } if (t.isSet() && t.getElementType().isTuple() && !t.isRelation()) { fail("Sets of tuples should be relations"); } } for (Type t1 : allTypes) { for (Type t2 : allTypes) { assertEquals(t1.equivalent(t2), t1.isSubtypeOf(t2) && t2.isSubtypeOf(t1)); } } for (Type t1 : allTypes) { for (Type t2 : allTypes) { if (t1.isSubtypeOf(t2)) { for (Type t3 : allTypes) { if (t2.isSubtypeOf(t3)) { if (!t1.isSubtypeOf(t3)) { System.err.println("FAILURE"); System.err.println("\t" + t1 + " <= " + t2 + " <= " + t3); System.err.println("\t" + t1 + " !<= " + t3); fail("subtype should be transitive: " + t1 + ", " + t2 + ", " + t3); } } } } } } } public void testEquiv() { for (Type t : allTypes) { if (!t.equals(t)) { fail("any type should be equal to itself: " + t); } if (!t.equivalent(t)) { fail("any type should be equivalent to itself: " + t); } } for (Type t1 : allTypes) { for (Type t2 : allTypes) { if (t1.equals(t2) && !t2.equals(t1)) { fail("equals() should be symmetric: " + t1 + ", " + t2); } if (t1.equivalent(t2) && !t2.equivalent(t1)) { fail("equivalent() should be symmetric: " + t1 + ", " + t2); } } } for (Type t1 : allTypes) { for (Type t2 : allTypes) { if (t1.equals(t2) || t1.equivalent(t2)) { for (Type t3 : allTypes) { if (t1.equals(t2) && t2.equals(t3)) { if (!t1.equals(t3)) { fail("equals() should be transitive: " + t1 + ", " + t2 + ", " + t3); } } if (t1.equivalent(t2) && t2.equivalent(t3)) { if (!t1.equivalent(t3)) { fail("equivalent() should be transitive: " + t1 + ", " + t2 + ", " + t3); } } } } } } } public void testLub() { for (Type t : allTypes) { if (t.lub(t) != t) { fail("lub should be idempotent: " + t + " != " + t.lub(t)); } } for (Type t1 : allTypes) { for (Type t2 : allTypes) { Type lub1 = t1.lub(t2); Type lub2 = t2.lub(t1); if (lub1 != lub2) { System.err.println("Failure:"); System.err.println(t1 + ".lub(" + t2 + ") = " + lub1); System.err.println(t2 + ".lub(" + t1 + ") = " + lub2); fail("lub should be commutative"); } if (t1.comparable(t2)) { if (t1.isSubtypeOf(t2)) { assertTrue(t1.lub(t2).equivalent(t2)); } if (t2.isSubtypeOf(t1)) { assertTrue(t1.lub(t2).equivalent(t1)); } } } } for (Type t1 : allTypes) { if (!t1.isAliased() && t1.lub(TypeFactory.getInstance().voidType()) != t1) { System.err.println(t1 + " lub void is not " + t1 + "? its "+ t1.lub(TypeFactory.getInstance().voidType())); fail("void should be bottom: " + t1 + ".lub = " + t1.lub(TypeFactory.getInstance().voidType())); } if (t1.isAliased() && t1.lub(TypeFactory.getInstance().voidType()) != t1.getAliased()) { fail("void should be bottom:" + t1); } if (t1.lub(TypeFactory.getInstance().valueType()) != TypeFactory.getInstance().valueType()) { System.err.println(t1 + " lub value is not value?"); fail("value should be top:" + t1); } } } public void testGlb() { for (Type t : allTypes) { if (t.glb(t) != t) { fail("glb should be idempotent: " + t + " != " + t.glb(t)); } } for (Type t1 : allTypes) { for (Type t2 : allTypes) { Type glb1 = t1.glb(t2); Type glb2 = t2.glb(t1); if (glb1 != glb2) { System.err.println("Failure:"); System.err.println(t1 + ".glb(" + t2 + ") = " + glb1); System.err.println(t2 + ".glb(" + t1 + ") = " + glb2); fail("glb should be commutative"); } if (t1.comparable(t2)) { if (t1.isSubtypeOf(t2)) { assertTrue(t1.glb(t2).equivalent(t1)); } if (t2.isSubtypeOf(t1)) { assertTrue(t1.glb(t2).equivalent(t2)); } } } } for (Type t1 : allTypes) { if (!t1.isAliased() && t1.glb(TypeFactory.getInstance().valueType()) != t1) { System.err.println(t1 + " glb value is not " + t1 + "? its "+ t1.glb(TypeFactory.getInstance().valueType())); fail("value should be top: " + t1 + ".lub = " + t1.lub(TypeFactory.getInstance().valueType())); } if (t1.isAliased() && t1.glb(TypeFactory.getInstance().valueType()) != t1.getAliased()) { fail("value should be top:" + t1); } if (t1.glb(TypeFactory.getInstance().voidType()) != TypeFactory.getInstance().voidType()) { System.err.println(t1 + " glb void is not void?"); fail("void should be bottom:" + t1); } } } public void testGetTypeDescriptor() { int count = 0; for (Type t1 : allTypes) { for (Type t2 : allTypes) { if (t1.toString().equals(t2.toString())) { if (t1 != t2) { System.err .println("Type descriptors should be canonical:" + t1.toString() + " == " + t2.toString()); } } if (count++ > 10000) { return; } } } } public void testMatchAndInstantiate() { Type X = ft.parameterType("X"); Map<Type, Type> bindings = new HashMap<>(); Type subject = ft.integerType(); X.match(subject, bindings); if (!bindings.get(X).equals(subject)) { fail("simple match failed"); } if (!X.instantiate(bindings).equals(subject)) { fail("instantiate failed"); } Type relXX = ft.relType(X, X); bindings.clear(); subject = ft.relType(ft.integerType(), ft.integerType()); relXX.match(subject, bindings); if (!bindings.get(X).equals(ft.integerType())) { fail("relation match failed"); } if (!relXX.instantiate(bindings).equals(subject)) { fail("instantiate failed"); } bindings.clear(); subject = ft.relType(ft.integerType(), ft.realType()); relXX.match(subject, bindings); Type lub = ft.integerType().lub(ft.realType()); if (!bindings.get(X).equals(lub)) { fail("lubbing during matching failed"); } if (!relXX.instantiate(bindings).equals(ft.relType(lub, lub))) { fail("instantiate failed"); } } public void testAlias() { Type alias = ft.aliasType(new TypeStore(), "myValue", ft.valueType()); assertTrue(alias.isSubtypeOf(ft.valueType())); assertTrue(ft.valueType().isSubtypeOf(alias)); } }