Assertions on approximation of platform types to non-null types

- for most expressions (ExpressionCodegen.genQualified)
This commit is contained in:
Andrey Breslav
2014-10-05 22:18:15 +04:00
parent 3d5d3e00e3
commit f1c66fa6b0
11 changed files with 193 additions and 24 deletions
@@ -37,7 +37,9 @@ import org.jetbrains.jet.lang.resolve.java.*;
import org.jetbrains.jet.lang.resolve.java.descriptor.JavaCallableMemberDescriptor;
import org.jetbrains.jet.lang.resolve.kotlin.PackagePartClassUtils;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.types.Approximation;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypesPackage;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.lexer.JetTokens;
import org.jetbrains.org.objectweb.asm.*;
@@ -608,12 +610,15 @@ public class AsmUtil {
@NotNull CallableDescriptor descriptor,
@NotNull String assertMethodToCall
) {
// Assertions are generated elsewhere for platform types
if (JavaPackage.getPLATFORM_TYPES()) return;
if (!state.isCallAssertionsEnabled()) return;
if (!isDeclaredInJava(descriptor)) return;
JetType type = descriptor.getReturnType();
if (type == null || isNullableType(type)) return;
if (type == null || isNullableType(TypesPackage.lowerIfFlexible(type))) return;
Type asmType = state.getTypeMapper().mapReturnType(descriptor);
if (asmType.getSort() == Type.OBJECT || asmType.getSort() == Type.ARRAY) {
@@ -625,6 +630,30 @@ public class AsmUtil {
}
}
@NotNull
public static StackValue genNotNullAssertions(
@NotNull GenerationState state,
@NotNull final StackValue stackValue,
@Nullable final Approximation.Info approximationInfo
) {
if (!state.isCallAssertionsEnabled()) return stackValue;
if (approximationInfo == null || !TypesPackage.assertNotNull(approximationInfo)) return stackValue;
return new StackValue(stackValue.type) {
@Override
public void put(Type type, InstructionAdapter v) {
stackValue.put(type, v);
if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
v.dup();
v.visitLdcInsn(approximationInfo.getMessage());
v.invokestatic("kotlin/jvm/internal/Intrinsics", "checkExpressionValueIsNotNull",
"(Ljava/lang/Object;Ljava/lang/String;)V", false);
}
}
};
}
private static boolean isDeclaredInJava(@NotNull CallableDescriptor callableDescriptor) {
CallableDescriptor descriptor = callableDescriptor;
while (true) {
@@ -61,6 +61,7 @@ import org.jetbrains.jet.lang.resolve.java.descriptor.SamConstructorDescriptor;
import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodSignature;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.resolve.scopes.receivers.*;
import org.jetbrains.jet.lang.types.Approximation;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeUtils;
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
@@ -242,7 +243,14 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
}
}
return selector.accept(visitor, receiver);
StackValue stackValue = selector.accept(visitor, receiver);
Approximation.Info approximationInfo = null;
if (selector instanceof JetExpression) {
approximationInfo = bindingContext.get(BindingContext.EXPRESSION_RESULT_APPROXIMATION, (JetExpression) selector);
}
return genNotNullAssertions(state, stackValue, approximationInfo);
}
catch (ProcessCanceledException e) {
throw e;
@@ -35,6 +35,7 @@ import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.resolve.scopes.receivers.Qualifier;
import org.jetbrains.jet.lang.types.Approximation;
import org.jetbrains.jet.lang.types.DeferredType;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.expressions.CaptureKind;
@@ -83,6 +84,7 @@ public interface BindingContext {
WritableSlice<JetExpression, JetType> EXPRESSION_TYPE = new BasicWritableSlice<JetExpression, JetType>(DO_NOTHING);
WritableSlice<JetExpression, JetType> EXPECTED_EXPRESSION_TYPE = new BasicWritableSlice<JetExpression, JetType>(DO_NOTHING);
WritableSlice<JetExpression, DataFlowInfo> EXPRESSION_DATA_FLOW_INFO = new BasicWritableSlice<JetExpression, DataFlowInfo>(DO_NOTHING);
WritableSlice<JetExpression, Approximation.Info> EXPRESSION_RESULT_APPROXIMATION = new BasicWritableSlice<JetExpression, Approximation.Info>(DO_NOTHING);
WritableSlice<JetExpression, DataFlowInfo> DATAFLOW_INFO_AFTER_CONDITION = Slices.createSimpleSlice();
/**
@@ -17,6 +17,7 @@
package org.jetbrains.jet.lang.types.expressions;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -33,13 +34,13 @@ import org.jetbrains.jet.lang.resolve.calls.smartcasts.SmartCastUtils;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstantChecker;
import org.jetbrains.jet.lang.resolve.constants.IntegerValueTypeConstant;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.JetTypeInfo;
import org.jetbrains.jet.lang.types.TypeUtils;
import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.Set;
import static org.jetbrains.jet.lang.diagnostics.Errors.*;
import static org.jetbrains.jet.lang.resolve.calls.context.ContextDependency.INDEPENDENT;
import static org.jetbrains.jet.lang.types.TypeUtils.*;
@@ -157,14 +158,47 @@ public class DataFlowUtils {
}
@Nullable
public static JetType checkType(@Nullable JetType expressionType, @NotNull JetExpression expressionToCheck,
@NotNull JetType expectedType, @NotNull DataFlowInfo dataFlowInfo, @NotNull BindingTrace trace
public static JetType checkType(@Nullable final JetType expressionType, @NotNull JetExpression expressionToCheck,
@NotNull JetType expectedType, @NotNull final DataFlowInfo dataFlowInfo, @NotNull final BindingTrace trace
) {
JetExpression expression = JetPsiUtil.safeDeparenthesize(expressionToCheck, false);
final JetExpression expression = JetPsiUtil.safeDeparenthesize(expressionToCheck, false);
recordExpectedType(trace, expression, expectedType);
if (expressionType == null || noExpectedType(expectedType) || !expectedType.getConstructor().isDenotable() ||
if (expressionType == null) return null;
if (noExpectedType(expectedType) || !expectedType.getConstructor().isDenotable() ||
JetTypeChecker.DEFAULT.isSubtypeOf(expressionType, expectedType)) {
if (!noExpectedType(expectedType)) {
Approximation.Info approximationInfo = TypesPackage.getApproximationTo(expressionType, expectedType,
new Approximation.DataFlowExtras() {
private DataFlowValue getDataFlowValue() {
return DataFlowValueFactory.createDataFlowValue(expression, expressionType, trace.getBindingContext());
}
@Override
public boolean getCanBeNull() {
return dataFlowInfo.getNullability(getDataFlowValue()).canBeNull();
}
@NotNull
@Override
public Set<JetType> getPossibleTypes() {
return dataFlowInfo.getPossibleTypes(getDataFlowValue());
}
@NotNull
@Override
public String getPresentableText() {
return StringUtil.trimMiddle(expression.getText(), 50);
}
}
);
if (approximationInfo != null) {
trace.record(BindingContext.EXPRESSION_RESULT_APPROXIMATION, expression, approximationInfo);
}
}
return expressionType;
}
@@ -178,6 +212,7 @@ public class DataFlowUtils {
}
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, trace.getBindingContext());
for (JetType possibleType : dataFlowInfo.getPossibleTypes(dataFlowValue)) {
if (JetTypeChecker.DEFAULT.isSubtypeOf(possibleType, expectedType)) {
SmartCastUtils.recordCastOrError(expression, possibleType, trace, dataFlowValue.isStableIdentifier(), false);
@@ -32,4 +32,14 @@ public class A {
public Object get(Object o) {
return null;
}
public A a() { return this; }
public static class B {
public static B b() { return null; }
}
public static class C {
public static C c() { return null; }
}
}
@@ -1,5 +1,5 @@
class AssertionChecker(val illegalStateExpected: Boolean) {
fun invoke(name: String, f: () -> Unit) {
fun invoke(name: String, f: () -> Any) {
try {
f()
} catch (e: IllegalStateException) {
@@ -44,15 +44,32 @@ fun checkAssertions(illegalStateExpected: Boolean) {
// binary expression
check("plus") { A() + A() }
// postfix expression
check("inc") { var a = A(); a++ }
// prefix expression
check("inc") { var a = A(); ++a }
// field
check("NULL") { val a = A().NULL }
check("NULL") { A().NULL }
// static field
check("STATIC_NULL") { val a = A.STATIC_NULL }
check("STATIC_NULL") { A.STATIC_NULL }
// postfix expression
// TODO:
// check("inc") { var a = A().a(); a++ }
// prefix expression
check("inc-b") { var a = A.B.b(); a++ }
// prefix expression
check("inc-c") { var a = A.C.c(); a++ }
// prefix expression
check("inc") { var a = A().a(); ++a }
// prefix expression
check("inc-b") { var a = A.B.b(); ++a }
// prefix expression
// TODO:
// check("inc-c") { var a = A.C.c(); ++a }
}
fun A.C.inc(): A.C = A.C()
fun <T> T.inc(): T = null as T
@@ -1,8 +1,8 @@
import java.util.ArrayList
fun foo() {
fun foo(): Any {
val a = ArrayList<String>()
a.get(0)
return a.get(0)
}
fun bar(a: ArrayList<String>) {
@@ -120,7 +120,7 @@ public class GenerateNotNullAssertionsTest extends CodegenTestCase {
loadFile("notNullAssertions/arrayListGet.kt");
String text = generateToText();
assertTrue(text.contains("checkReturnedValueIsNotNull"));
assertTrue(text.contains("checkExpressionValueIsNotNull"));
assertTrue(text.contains("checkParameterIsNotNull"));
}
@@ -131,7 +131,7 @@ public class GenerateNotNullAssertionsTest extends CodegenTestCase {
loadFile("notNullAssertions/javaMultipleSubstitutions.kt");
String text = generateToText();
assertEquals(3, StringUtil.getOccurrenceCount(text, "checkReturnedValueIsNotNull"));
assertEquals(3, StringUtil.getOccurrenceCount(text, "checkExpressionValueIsNotNull"));
assertEquals(3, StringUtil.getOccurrenceCount(text, "checkParameterIsNotNull"));
}