/**
* Copyright (c) 2009-2011, The HATS Consortium. All rights reserved.
* This file is licensed under the terms of the Modified BSD License.
*/
package abs.frontend.typesystem;
import static org.junit.Assert.*;
import java.io.*;
import org.junit.Test;
import abs.common.FileUtils;
import abs.frontend.FrontendTest;
import abs.frontend.analyser.SemanticConditionList;
import abs.frontend.ast.ClassDecl;
import abs.frontend.ast.Model;
import abs.frontend.ast.VarDeclStmt;
import abs.frontend.typechecker.locationtypes.LocationType;
import abs.frontend.typechecker.locationtypes.LocationTypeCheckerException;
import abs.frontend.typechecker.locationtypes.LocationTypeExtension;
import abs.frontend.typechecker.locationtypes.infer.InferMain;
import abs.frontend.typechecker.locationtypes.infer.LocationTypeInferrerExtension;
import abs.frontend.typechecker.locationtypes.infer.LocationTypeInferrerExtension.LocationTypingPrecision;
import static abs.ABSTest.Config.*;
public class LocationTypeTests extends FrontendTest {
@Test
public void fieldDecl() {
Model m = assertParse("interface I { } class C { [Far] I i; }",WITH_STD_LIB);
ClassDecl decl = getFirstClassDecl(m);
LocationType ft = LocationTypeExtension.getLocationTypeFromAnnotations(decl.getField(0).getType());
assertEquals(LocationType.FAR,ft);
}
@Test
public void varDecl() {
Model m = assertParse("interface I { } { [Somewhere] I i; [Near] I jloc; i = jloc; }",WITH_STD_LIB);
m.typeCheck();
assertEquals(LocationType.NEAR,LocationTypeExtension.getLocationTypeFromAnnotations(getTypeOfFirstAssignment(m)));
}
private static String INT = "interface I { [Near] I m(); [Far] I n([Near] I i); Unit farM([Far] I i);}" +
" class C([Somewhere] I f) implements I { " +
" [Far] I farField; " +
" [Near] I nearField; " +
" [Near] I m() { [Near] I i; i = this; return nearField; } " +
" [Far] I n([Near] I i) { return farField; }" +
" Unit farM([Far] I i) { }}" +
" interface J { } class E implements J { }";
@Test
public void syncCall() {
assertLocationTypeOk("{ [Near] I i; i = i.m(); }");
}
@Test
public void asyncCall() {
assertLocationTypeOk("{ [Far] I i; Fut<[Far] I> f; f = i!m(); }");
}
@Test
public void syncCallOnThis() {
assertLocationTypeOk("class D { Unit m() { this.m(); } }");
}
@Test
public void nullLit() {
assertLocationTypeOk("{ [Near] I i; i = null; [Far] I j; j = null; }");
}
@Test
public void syncCallParam() {
assertLocationTypeOk("{ [Near] I i; [Far] I j; j = i.n(i); }");
}
@Test
public void newCog() {
assertLocationTypeOk("{ [Far] I i; i = new C(i); }");
}
@Test
public void newObject() {
assertLocationTypeOk("{ [Near] J i; i = new local E(); }");
assertLocationTypeOk("{ [Somewhere] J i; i = new local E(); }");
}
@Test
public void typeMaybe() {
assertLocationTypeOk("{ [Near] I i; Maybe<[Near] I> m = Just(i); }");
}
@Test
public void syncCallOnMaybeThis() {
String s = "def X id<X>(X x) = x; interface K { Unit m(Maybe<[Near] K> p); } " +
"class D implements K { [Near] K getThis() { return this; } Unit m(Maybe<[Near] K> p)";
assertLocationTypeOk(s+ "{ this.m(Just(this)); } }");
assertLocationTypeOk(s+ "{ [Near] K k; k = this; this.m(Just(k)); } }");
assertLocationTypeOk(s+ "{ [Near] K k; this.m(case Just(k) { Just(x) => Just(x); }); } }");
assertLocationTypeOk(s+ "{ [Near] K k; this.m(Just(id(k))); } }");
}
@Test
public void typeParamInference() {
assertLocationTypeOk("{ [Near] I i; Maybe<Maybe<Bool>> m = Nothing; }");
}
@Test
public void defaultTyping() {
assertLocationTypeOk("{ I i; [Far] I f; i = new local C(f); }");
}
@Test
public void simpleAssignInfer() {
assertInferOk("interface I {} { I i; [Far] I i2; i = i2; }", LocationType.FAR);
}
@Test
public void futureTyping() {
assertLocationTypeOk("{ I i; [Far] I f; Fut<I> fut; i = new local C(f); fut = i!m(); }");
}
@Test
public void syncCallInfer() {
assertInferOk("interface I { Unit m(); } { I i; i.m(); }", LocationType.NEAR);
}
@Test
public void asyncCallInfer() {
assertInferOk("interface I { [Near] I m(); } { I j; [Far] I i; Fut<I> f; f = i!m(); j = f.get; }", LocationType.FAR);
}
@Test
public void testListConstruct() {
assertLocationTypeErrorOnly("interface K {} { List<[Near] K> res = Nil; [Far] K j; res = Cons(j,Nil); }");
}
@Test
public void patternMatch() {
assertLocationTypeErrorOnly("interface K {} { Maybe<[Near] K> m = Nothing; [Far] K k = case m { Nothing => null; Just(x) => x; }; }");
}
@Test
public void caseExp() {
assertLocationTypeError("interface K {} { Maybe<[Far] K> m = Nothing; [Near] K knear;" +
"[Far] K k = case m { Nothing => knear; Just(x) => x; }; }");
}
@Test
public void function() {
assertLocationTypeErrorOnly("interface K {} def [Near] K f([Somewhere] K k) = k;");
}
@Test
public void fnapp() {
assertLocationTypeErrorOnly("interface K {} def Unit f([Near] K k) = Unit; { [Far] K k; Unit u = f(k);}");
}
@Test
public void dataExp() {
assertLocationTypeErrorOnly("interface K {} data Foo = Bar([Far] K); { [Near] K k; Foo f = Bar(k);}");
}
/*
@Test
public void callParamInfer() {
Model m = assertInferOk("interface I { Unit m(I i); } class C implements I { Unit m(I i) { I j; j = new local C(); i.m(j); } } { }");
ClassDecl cd = (ClassDecl) m.getCompilationUnit(1).getModuleDecl(0).getDecl(1);
Type t = cd.getMethod(0).getMethodSig().getParam(0).getType();
LocationType lt = m.getLocationTypeInferenceResult().get(LocationTypeInferrerExtension.getLV(t));
assertEquals(LocationType.NEAR, lt);
}*/
@Test
public void newCOGInfer() {
assertInferOk("interface I { } class C implements I {} { I i; i = new C(); }", LocationType.FAR);
}
@Test
public void newCOGInferAnn() {
assertInferOk("interface I { } class C implements I {} { [Infer] I i; i = new C(); }", LocationType.FAR);
}
@Test
public void newObjectInfer() {
assertInferOk("interface I { } class C implements I {} { I i; i = new local C(); }", LocationType.NEAR);
}
@Test
public void newObjectInfer2() {
assertInferOk("interface I { I getI(); } class C implements I { I i; { i = new local C(); } I getI() { return i; } } " +
"{ I i; I j; j = new local C(); i = j.getI(); }", LocationType.NEAR);
}
@Test
public void newObjectInfer3() {
assertInferOk("interface I { } class C implements I {} { I i; i = new local C(); i = new C(); }", LocationType.SOMEWHERE);
}
@Test
public void annotatedlocaVarInfer() {
assertInferOk("interface I { } class C implements I {} { I i; [Near] I j; i = j; }", LocationType.NEAR);
}
@Test
public void overrideOK() {
assertInferOk("interface I { [Somewhere] I m([Far] I i); } class C implements I { [Near] I m([Somewhere] I i) { return null; } } { }");
}
@Test
public void overrideminimal() {
assertInferOk("interface I { I m([Far] I i); } class C implements I { [Near] I m([Somewhere] I i) { return null; } } { I i; [Near] I k; Fut<I> j; j = k!m(null); i = j.get; }", LocationType.NEAR);
}
@Test
public void callNullParam() {
assertTypeOkOnly("interface I2 { Unit m([Near] I2 i); } { [Far] I2 i; i!m(null); }");
}
@Test
public void callNullParam2() {
assertLocationTypeOk("interface I2 { Unit m([Near] I2 i); } { [Somewhere] I2 i; i!m(null); }");
}
@Test
public void callReturn() {
assertInferOk("interface I2 { [Near] I2 m(); } { I2 i2; [Far] I2 i; Fut<I2> f; f = i!m(); i2 = f.get; }", LocationType.FAR);
}
@Test
public void callReturn2() {
assertInferOk("module M.S1; export *; interface I { [Near] I m(); } class C implements I { [Near] I m() { return null; } } " +
"module M.S2; import * from M.S1; { I i; i = new C(); i!m(); }", LocationType.FAR);
}
@Test
public void typeSyn() {
assertInferOk("interface I {} type I2 = [Somewhere] I; { I2 i; i = null; }", LocationType.SOMEWHERE);
}
@Test
public void typeImprovedInfer() {
assertInferOk("interface I { Unit m([Far] I i); } class C implements I { Unit m([Far] I i) { } } { I i1; I i2; i1 = new C(); i2 = new C(); i1!m(i2); }");
}
@Test
public void fieldTypeImprovedInfer() {
assertInferOk("interface I { Unit m([Far] I i); } " +
"class C implements I { " +
" I i1; I i2; " +
" Unit m([Far] I i) { i1 = new C(); i2 = new C(); i1!m(i2); } } { }");
}
@Test
public void writeBackTest() throws Exception {
String s = writeBackSolutions("module M; interface I { Unit m([Far] I i); } class C implements I { Unit m([Far] I i) { } } { I i1; I i2; i1 = new C(); i2 = new C(); i1!m(i2); }");
//System.out.println(s);
// TODO: Do something later (2010+)
assertTypeOK(s);
assertEquals("module M; interface I { Unit m([Far] I i); } class C implements I { Unit m([Far] I i) { } } { [Far] I i1; [Far] I i2; i1 = new C(); i2 = new C(); i1!m(i2); }",s);
}
// negative tests:
@Test
public void typeSynFail() {
assertInferFails("interface I {} type I2 = [Somewhere] I; { [Far] I2 i; i = null; }");
}
@Test
public void typeMaybeError() {
assertLocationTypeErrorOnly("interface I { } { [Far] I i; Maybe<[Near] I> m = Just(i); }");
}
@Test
public void typeListError() {
assertLocationTypeErrorOnly("interface I {} { List<[Far] I> list = Nil; [Near] I i; list = Cons(i,list); }");
}
@Test
public void callWrongParam() {
assertLocationTypeErrorOnly("interface I { Unit m([Near] I i); } { [Far] I i; i!m(i); }");
}
@Test
public void assignWrong() {
assertLocationTypeError("{ [Far] I i; [Near] I j; i = j; }");
}
@Test
public void illegalSyncCall() {
assertLocationTypeError("{ [Far] I i; i.m(); }");
}
@Test
public void illegalAsyncCall() {
assertLocationTypeError("{ [Far] I i; i!farM(i); }");
}
@Test
public void syncCallWrongParam() {
assertLocationTypeError("{ [Near] I i; [Far] I j; j = i.n(j); }");
}
@Test(expected=LocationTypeCheckerException.class)
public void multipleError() {
Model m = assertParse("interface I { } class C { [Far] [Near] I i; }",WITH_STD_LIB);
ClassDecl decl = getFirstClassDecl(m);
LocationTypeExtension.getLocationTypeFromAnnotations(decl.getField(0).getType());
}
@Test
public void overrideReturnFailed() {
assertInferFails("interface I { [Near] I m(I i); } class C implements I { [Somewhere] I m(I i) { return null; } } { }");
}
@Test
public void overrideParameterFailed() {
assertInferFails("interface I { I m([Somewhere] I i); } class C implements I { I m([Near] I i) { return null; } } { }");
}
@Test
public void testInferenceRetypeChecking() {
String code = "interface I { Unit m(); } { [Far] I i; I j; i = j; j.m(); }";
Model m = assertParseOkStdLib(code);
LocationTypeExtension te = new LocationTypeExtension(m);
m.registerTypeSystemExtension(te);
SemanticConditionList e = m.typeCheck();
m.typeCheck(new SemanticConditionList());
}
@Test
public void testAwaitFail() {
LocationType lt = LocationType.INFER;
Model m = assertParseOkStdLib("interface T { Unit foo(); } class C { T t = null; Unit bar() { await t!foo(); }}");
assertFalse(m.hasErrors()); // This line is essential to trigger the NPE!
LocationTypeInferrerExtension ltie = new LocationTypeInferrerExtension(m);
ltie.setDefaultType(lt);
ltie.setLocationTypingPrecision(LocationTypingPrecision.CLASS_LOCAL_FAR);
m.registerTypeSystemExtension(ltie);
m.getErrors();
SemanticConditionList e = m.typeCheck();
}
@Test
public void testAwaitFailRewriteOff() {
LocationType lt = LocationType.INFER;
Model.doAACrewrite = false;
Model m = assertParseOkStdLib("interface T { Unit foo(); } class C { T t = null; Unit bar() { await t!foo(); }}");
assertFalse(m.hasErrors()); // This line is essential to trigger the NPE!
LocationTypeInferrerExtension ltie = new LocationTypeInferrerExtension(m);
ltie.setDefaultType(lt);
ltie.setLocationTypingPrecision(LocationTypingPrecision.CLASS_LOCAL_FAR);
m.registerTypeSystemExtension(ltie);
m.getErrors();
SemanticConditionList e = m.typeCheck();
Model.doAACrewrite = true;
}
private void assertLocationTypeError(String code) {
assertLocationTypeErrorOnly(INT+code);
}
private void assertLocationTypeErrorOnly(String code) {
Model m = assertParse(code,WITH_STD_LIB);
LocationTypeExtension te = new LocationTypeExtension(m);
m.registerTypeSystemExtension(te);
SemanticConditionList e = m.typeCheck();
assertFalse("No type error occurred", !e.containsErrors());
assertInferFails(code);
}
private void assertLocationTypeOk(String code) {
assertTypeOkOnly(INT+code);
}
private void assertTypeOkOnly(String code) {
Model m = assertParse(code,WITH_STD_LIB);
LocationTypeExtension te = new LocationTypeExtension(m);
m.registerTypeSystemExtension(te);
m.getErrors();
SemanticConditionList e = m.typeCheck();
assertTrue(!e.containsErrors() ? "" : "Found error "+e.getFirstError().getMessage(),!e.containsErrors());
assertInferOk(code);
}
private Model assertInfer(String code, LocationType expected, boolean fails) {
Model m = assertParse(code,WITH_STD_LIB);
//m.setLocationTypingEnabled(true);
LocationTypeInferrerExtension ltie = new LocationTypeInferrerExtension(m);
m.registerTypeSystemExtension(ltie);
SemanticConditionList e = m.typeCheck();
//System.out.println(ltie.getConstraints());
assertEquals(!e.containsErrors() ? "" : "Found error: "+e.getFirstError().getMessage(), fails, e.containsErrors());
//assertTrue("Inference failed", generated != null);
//assertEquals(fails, generated == null);
if (expected != null) {
VarDeclStmt vds = ((VarDeclStmt)m.getMainBlock().getStmt(0));
LocationType t = ltie.getResults().get(LocationTypeInferrerExtension.getLV(vds.getVarDecl().getType()));
assertTrue(t.toString(), expected == LocationType.FAR ? t == LocationType.FAR || t.isParametricFar() : expected == t);
}
return m;
}
private String writeBackSolutions(String code) throws Exception {
File f = File.createTempFile("test", ".abs");
f.deleteOnExit();
FileWriter fw = new FileWriter(f);
fw.write(code);
fw.close();
Model m = assertParseFileOk(f.getAbsolutePath(), Config.WITH_STD_LIB);
LocationTypeInferrerExtension ltie = new LocationTypeInferrerExtension(m);
m.registerTypeSystemExtension(ltie);
SemanticConditionList e = m.typeCheck();
assertEquals(!e.containsErrors() ? "" : "Found error: "+e.getFirstError().getMessage(), false, e.containsErrors());
new InferMain().writeInferenceResultsBack(ltie.getResults());
String res = FileUtils.fileToStringBuilder(f).toString();
f.delete();
return res;
}
private Model assertInferOk(String string, LocationType expected) {
return assertInfer(string, expected, false);
}
private Model assertInferOk(String string) {
return assertInfer(string, null, false);
}
private void assertInferFails(String string) {
assertInfer(string, null, true);
}
}