Do not allow inference of type arguments on the rhs if there's no information available

This commit is contained in:
Andrey Breslav
2013-09-12 21:13:11 +02:00
parent 5c86a5bd7c
commit a9134f8eff
20 changed files with 219 additions and 44 deletions
@@ -85,7 +85,7 @@ public interface Errors {
DiagnosticFactory0<JetNullableType> REDUNDANT_NULLABLE = DiagnosticFactory0.create(WARNING, NULLABLE_TYPE);
DiagnosticFactory1<JetNullableType, JetType> BASE_WITH_NULLABLE_UPPER_BOUND = DiagnosticFactory1.create(WARNING, NULLABLE_TYPE);
DiagnosticFactory1<JetElement, Integer> WRONG_NUMBER_OF_TYPE_ARGUMENTS = DiagnosticFactory1.create(ERROR);
DiagnosticFactory2<JetUserType, Integer, String> NO_TYPE_ARGUMENTS_ON_RHS_OF_IS_EXPRESSION = DiagnosticFactory2.create(ERROR);
DiagnosticFactory2<JetTypeReference, Integer, String> NO_TYPE_ARGUMENTS_ON_RHS = DiagnosticFactory2.create(ERROR);
DiagnosticFactory1<JetTypeProjection, ClassifierDescriptor> CONFLICTING_PROJECTION = DiagnosticFactory1.create(ERROR, VARIANCE_IN_PROJECTION);
DiagnosticFactory1<JetTypeProjection, ClassifierDescriptor> REDUNDANT_PROJECTION = DiagnosticFactory1.create(WARNING, VARIANCE_IN_PROJECTION);
@@ -434,7 +434,7 @@ public class DefaultErrorMessages {
MAP.put(TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH, "Type inference failed. Expected type mismatch: found: {1} required: {0}", RENDER_TYPE, RENDER_TYPE);
MAP.put(WRONG_NUMBER_OF_TYPE_ARGUMENTS, "{0,choice,0#No type arguments|1#Type argument|1<{0,number,integer} type arguments} expected", null);
MAP.put(NO_TYPE_ARGUMENTS_ON_RHS_OF_IS_EXPRESSION, "{0,choice,0#No type arguments|1#Type argument|1<{0,number,integer} type arguments} expected. " +
MAP.put(NO_TYPE_ARGUMENTS_ON_RHS, "{0,choice,0#No type arguments|1#Type argument|1<{0,number,integer} type arguments} expected. " +
"Use ''{1}'' if you don''t want to pass type arguments", null, TO_STRING);
MAP.put(DANGLING_FUNCTION_LITERAL_ARGUMENT_SUSPECTED,
@@ -79,17 +79,19 @@ public class PossiblyBareType {
}
@NotNull
public JetType reconstruct(@NotNull JetType subjectType) {
if (!isBare()) return getActualType();
public TypeReconstructionResult reconstruct(@NotNull JetType subjectType) {
if (!isBare()) return new TypeReconstructionResult(getActualType(), true);
JetType type = CastDiagnosticsUtil.findStaticallyKnownSubtype(
TypeReconstructionResult reconstructionResult = CastDiagnosticsUtil.findStaticallyKnownSubtype(
TypeUtils.makeNotNullable(subjectType),
getBareTypeConstructor()
);
JetType type = reconstructionResult.getResultingType();
// No need to make an absent type nullable
if (type == null) return type;
if (type == null) return reconstructionResult;
return TypeUtils.makeNullableAsSpecified(type, isBareTypeNullable());
JetType resultingType = TypeUtils.makeNullableAsSpecified(type, isBareTypeNullable());
return new TypeReconstructionResult(resultingType, reconstructionResult.isAllArgumentsInferred());
}
@Override
@@ -112,7 +112,7 @@ public class CastDiagnosticsUtil {
// NOTE: this does not account for 'as Array<List<T>>'
if (allParametersReified(subtype)) return false;
JetType staticallyKnownSubtype = findStaticallyKnownSubtype(supertype, subtype.getConstructor());
JetType staticallyKnownSubtype = findStaticallyKnownSubtype(supertype, subtype.getConstructor()).getResultingType();
// If the substitution failed, it means that the result is an impossible type, e.g. something like Out<in Foo>
// In this case, we can't guarantee anything, so the cast is considered to be erased
@@ -132,14 +132,14 @@ public class CastDiagnosticsUtil {
* Example 1:
* supertype = Collection<String>
* subtype = List<...>
* result = List<String>
* result = List<String>, all arguments are inferred
*
* Example 2:
* supertype = Any
* subtype = List<...>
* result = List<*>
* result = List<*>, some arguments were not inferred, replaced with '*'
*/
public static JetType findStaticallyKnownSubtype(@NotNull JetType supertype, @NotNull TypeConstructor subtypeConstructor) {
public static TypeReconstructionResult findStaticallyKnownSubtype(@NotNull JetType supertype, @NotNull TypeConstructor subtypeConstructor) {
assert !supertype.isNullable() : "This method only makes sense for non-nullable types";
// Assume we are casting an expression of type Collection<Foo> to List<Bar>
@@ -176,6 +176,7 @@ public class CastDiagnosticsUtil {
// If some of the parameters are not determined by unification, it means that these parameters are lost,
// let's put stars instead, so that we can only cast to something like List<*>, e.g. (a: Any) as List<*>
boolean allArgumentsInferred = true;
for (TypeParameterDescriptor variable : variables) {
TypeProjection value = substitution.get(variable.getTypeConstructor());
if (value == null) {
@@ -183,12 +184,15 @@ public class CastDiagnosticsUtil {
variable.getTypeConstructor(),
SubstitutionUtils.makeStarProjection(variable)
);
allArgumentsInferred = false;
}
}
// At this point we have values for all type parameters of List
// Let's make a type by substituting them: List<T> -> List<Foo>
return TypeSubstitutor.create(substitution).substitute(subtypeWithVariables, Variance.INVARIANT);
JetType substituted = TypeSubstitutor.create(substitution).substitute(subtypeWithVariables, Variance.INVARIANT);
return new TypeReconstructionResult(substituted, allArgumentsInferred);
}
private static boolean allParametersReified(JetType subtype) {
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.lang.types;
import org.jetbrains.annotations.Nullable;
public class TypeReconstructionResult {
private final JetType resultingType;
private final boolean allArgumentsInferred;
public TypeReconstructionResult(@Nullable JetType resultingType, boolean allArgumentsInferred) {
this.resultingType = resultingType;
this.allArgumentsInferred = allArgumentsInferred;
}
@Nullable
public JetType getResultingType() {
return resultingType;
}
public boolean isAllArgumentsInferred() {
return allArgumentsInferred;
}
}
@@ -62,7 +62,9 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.lexer.JetTokens;
import org.jetbrains.jet.utils.ThrowingList;
import java.util.*;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor.NO_RECEIVER_PARAMETER;
import static org.jetbrains.jet.lang.diagnostics.Errors.*;
@@ -77,12 +79,14 @@ import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType;
import static org.jetbrains.jet.lang.types.expressions.ControlStructureTypingUtils.createCallForSpecialConstruction;
import static org.jetbrains.jet.lang.types.expressions.ControlStructureTypingUtils.resolveSpecialConstructionAsCall;
import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.*;
import static org.jetbrains.jet.lexer.JetTokens.*;
import static org.jetbrains.jet.lang.types.expressions.TypeReconstructionUtil.reconstructBareType;
import static org.jetbrains.jet.lexer.JetTokens.AS_KEYWORD;
import static org.jetbrains.jet.lexer.JetTokens.AS_SAFE;
@SuppressWarnings("SuspiciousMethodCalls")
public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
private static final TokenSet BARE_TYPES_ALLOWED = TokenSet.create(AS_KEYWORD, AS_SAFE, IS_KEYWORD, NOT_IS);
private static final TokenSet BARE_TYPES_ALLOWED = TokenSet.create(AS_KEYWORD, AS_SAFE);
private final PlatformToKotlinClassMap platformToKotlinClassMap;
@@ -192,10 +196,9 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
DataFlowInfo dataFlowInfo = context.dataFlowInfo;
JetType subjectType = typeInfo.getType();
JetType targetType;
if (subjectType != null) {
targetType = possiblyBareTarget.reconstruct(subjectType);
JetType targetType = reconstructBareType(right, possiblyBareTarget, subjectType, context.trace);
if (subjectType != null) {
checkBinaryWithTypeRHS(expression, contextWithNoExpectedType, targetType, subjectType);
dataFlowInfo = typeInfo.getDataFlowInfo();
if (operationType == AS_KEYWORD) {
@@ -204,10 +207,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
dataFlowInfo = dataFlowInfo.establishSubtyping(value, targetType);
}
}
else {
// Recovery: let's reconstruct as if we were casting from Any, to get some type there
targetType = possiblyBareTarget.reconstruct(KotlinBuiltIns.getInstance().getAnyType());
}
JetType result = operationType == AS_SAFE ? TypeUtils.makeNullable(targetType) : targetType;
return DataFlowUtils.checkType(result, expression, context, dataFlowInfo);
}
@@ -283,10 +283,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
}
TypeResolutionContext typeResolutionContext = new TypeResolutionContext(context.scope, context.trace, true, /*allowBareTypes=*/ true);
PossiblyBareType possiblyBareTarget = context.expressionTypingServices.getTypeResolver().resolvePossiblyBareType(typeResolutionContext, typeReferenceAfterIs);
JetType type = possiblyBareTarget.reconstruct(subjectType);
if (possiblyBareTarget.isBare()) {
context.trace.record(BindingContext.TYPE, typeReferenceAfterIs, type);
}
JetType type = TypeReconstructionUtil.reconstructBareType(typeReferenceAfterIs, possiblyBareTarget, subjectType, context.trace);
if (!subjectType.isNullable() && type.isNullable()) {
JetTypeElement element = typeReferenceAfterIs.getTypeElement();
assert element instanceof JetNullableType : "element must be instance of " + JetNullableType.class.getName();
@@ -0,0 +1,72 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.lang.types.expressions;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
import org.jetbrains.jet.lang.psi.JetTypeReference;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.PossiblyBareType;
import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import static org.jetbrains.jet.lang.diagnostics.Errors.NO_TYPE_ARGUMENTS_ON_RHS;
public class TypeReconstructionUtil {
@NotNull
public static JetType reconstructBareType(
@NotNull JetTypeReference right,
@NotNull PossiblyBareType possiblyBareTarget,
@Nullable JetType subjectType,
@NotNull BindingTrace trace
) {
if (subjectType == null) {
// Recovery: let's reconstruct as if we were casting from Any, to get some type there
subjectType = KotlinBuiltIns.getInstance().getAnyType();
}
TypeReconstructionResult reconstructionResult = possiblyBareTarget.reconstruct(subjectType);
if (!reconstructionResult.isAllArgumentsInferred()) {
TypeConstructor typeConstructor = possiblyBareTarget.getBareTypeConstructor();
trace.report(NO_TYPE_ARGUMENTS_ON_RHS.on(right,
typeConstructor.getParameters().size(),
allStarProjectionsString(typeConstructor)));
}
JetType targetType = reconstructionResult.getResultingType();
if (targetType != null) {
if (possiblyBareTarget.isBare()) {
trace.record(BindingContext.TYPE, right, targetType);
}
return targetType;
}
return ErrorUtils.createErrorType("Failed to reconstruct type: " + right.getText());
}
@NotNull
private static String allStarProjectionsString(@NotNull TypeConstructor constructor) {
int size = constructor.getParameters().size();
assert size != 0 : "No projections possible for a nilary type constructor" + constructor;
ClassifierDescriptor declarationDescriptor = constructor.getDeclarationDescriptor();
assert declarationDescriptor != null : "No declaration descriptor for type constructor " + constructor;
String name = declarationDescriptor.getName().asString();
return TypeUtils.getTypeNameAndStarProjectionsString(name, size);
}
}
@@ -0,0 +1,8 @@
trait Tr
trait G<T>
fun test(tr: Tr) {
val v = tr as <!NO_TYPE_ARGUMENTS_ON_RHS!>G?<!>
// If v is not nullable, there will be a warning on this line:
v!!: G<*>
}
@@ -0,0 +1,6 @@
class G<T>
fun foo(p: <!UNRESOLVED_REFERENCE!>P<!>) {
val v = <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>p<!> <!USELESS_CAST!>as<!> <!NO_TYPE_ARGUMENTS_ON_RHS!>G?<!>
v!!: G<*>
}
@@ -0,0 +1,7 @@
trait Tr
trait G<T>
fun test(tr: Tr?) {
val v = tr as <!NO_TYPE_ARGUMENTS_ON_RHS!>G<!>
v: G<*>
}
@@ -0,0 +1,8 @@
trait Tr<T>
trait G<T> : Tr<T>
fun test(tr: Tr<String>?) {
val v = tr as G?
// If v is not nullable, there will be a warning on this line:
v!!: G<String>
}
@@ -0,0 +1,7 @@
trait Tr
trait G<T>
fun test(tr: Tr?) {
val v = tr as <!NO_TYPE_ARGUMENTS_ON_RHS!>G?<!>
v!!: G<*>
}
@@ -2,6 +2,6 @@ trait Tr
trait G<T>
fun test(tr: Tr) {
val v = tr as G
val v = tr as <!NO_TYPE_ARGUMENTS_ON_RHS!>G<!>
v: G<*>
}
}
@@ -1,4 +1,4 @@
trait Tr
trait G<T>
fun test(tr: Tr) = tr is G
fun test(tr: Tr) = tr is <!NO_TYPE_ARGUMENTS_ON_RHS!>G<!>
@@ -2,15 +2,13 @@ package p
public fun foo(a: Any) {
a is Map<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><Int><!>
a is <!NO_TYPE_ARGUMENTS_ON_RHS_OF_IS_EXPRESSION!>Map<!>
a is <!NO_TYPE_ARGUMENTS_ON_RHS!>Map<!>
a is Map<out Any?, Any?>
a is Map<*, *>
a is Map<<!SYNTAX!><!>>
a is List<<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>Map<!>>
a is <!NO_TYPE_ARGUMENTS_ON_RHS_OF_IS_EXPRESSION!>List<!>
a is <!NO_TYPE_ARGUMENTS_ON_RHS!>List<!>
a is Int
(a <!USELESS_CAST!>as<!> <!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>Map<!>) is Int
}
(a as <!NO_TYPE_ARGUMENTS_ON_RHS!>Map<!>) is <!INCOMPATIBLE_TYPES!>Int<!>
}
@@ -1,12 +1,12 @@
public fun foo(a: Any, <!UNUSED_PARAMETER!>b<!>: <!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>Map<!>) {
when (a) {
is Map<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><Int><!> -> {}
is <!NO_TYPE_ARGUMENTS_ON_RHS_OF_IS_EXPRESSION!>Map<!> -> {}
is <!NO_TYPE_ARGUMENTS_ON_RHS!>Map<!> -> {}
is Map<out Any?, Any?> -> {}
is Map<*, *> -> {}
is Map<<!SYNTAX!><!>> -> {}
is List<<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>Map<!>> -> {}
is <!NO_TYPE_ARGUMENTS_ON_RHS_OF_IS_EXPRESSION!>List<!> -> {}
is <!NO_TYPE_ARGUMENTS_ON_RHS!>List<!> -> {}
is Int -> {}
else -> {}
}
@@ -1201,6 +1201,11 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
doTest("compiler/testData/diagnostics/tests/cast/bare/AsNullable.kt");
}
@TestMetadata("AsNullableNotEnough.kt")
public void testAsNullableNotEnough() throws Exception {
doTest("compiler/testData/diagnostics/tests/cast/bare/AsNullableNotEnough.kt");
}
@TestMetadata("EitherAs.kt")
public void testEitherAs() throws Exception {
doTest("compiler/testData/diagnostics/tests/cast/bare/EitherAs.kt");
@@ -1231,11 +1236,31 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
doTest("compiler/testData/diagnostics/tests/cast/bare/ErrorsInSubstitution.kt");
}
@TestMetadata("FromErrorType.kt")
public void testFromErrorType() throws Exception {
doTest("compiler/testData/diagnostics/tests/cast/bare/FromErrorType.kt");
}
@TestMetadata("NullableAs.kt")
public void testNullableAs() throws Exception {
doTest("compiler/testData/diagnostics/tests/cast/bare/NullableAs.kt");
}
@TestMetadata("NullableAsNotEnough.kt")
public void testNullableAsNotEnough() throws Exception {
doTest("compiler/testData/diagnostics/tests/cast/bare/NullableAsNotEnough.kt");
}
@TestMetadata("NullableAsNullable.kt")
public void testNullableAsNullable() throws Exception {
doTest("compiler/testData/diagnostics/tests/cast/bare/NullableAsNullable.kt");
}
@TestMetadata("NullableAsNullableNotEnough.kt")
public void testNullableAsNullableNotEnough() throws Exception {
doTest("compiler/testData/diagnostics/tests/cast/bare/NullableAsNullableNotEnough.kt");
}
@TestMetadata("RedundantNullable.kt")
public void testRedundantNullable() throws Exception {
doTest("compiler/testData/diagnostics/tests/cast/bare/RedundantNullable.kt");
@@ -72,14 +72,17 @@ public abstract class AddStarProjectionsFix extends JetIntentionAction<JetUserTy
return new JetSingleIntentionActionFactory() {
@Override
public IntentionAction createAction(Diagnostic diagnostic) {
assert diagnostic.getFactory() == Errors.NO_TYPE_ARGUMENTS_ON_RHS_OF_IS_EXPRESSION;
assert diagnostic.getFactory() == Errors.NO_TYPE_ARGUMENTS_ON_RHS;
@SuppressWarnings("unchecked")
DiagnosticWithParameters2<JetUserType, Integer, String> diagnosticWithParameters =
(DiagnosticWithParameters2<JetUserType, Integer, String>) diagnostic;
JetUserType userType = QuickFixUtil.getParentElementOfType(diagnostic, JetUserType.class);
if (userType == null) return null;
DiagnosticWithParameters2<JetTypeReference, Integer, String> diagnosticWithParameters =
(DiagnosticWithParameters2<JetTypeReference, Integer, String>) diagnostic;
JetTypeElement typeElement = diagnosticWithParameters.getPsiElement().getTypeElement();
while (typeElement instanceof JetNullableType) {
typeElement = ((JetNullableType) typeElement).getInnerType();
}
if (!(typeElement instanceof JetUserType)) return null;
Integer size = diagnosticWithParameters.getA();
return new AddStarProjectionsFix(userType, size) {};
return new AddStarProjectionsFix((JetUserType) typeElement, size) {};
}
};
}
@@ -182,7 +182,7 @@ public class QuickFixes {
factories.put(ELSE_MISPLACED_IN_WHEN, MoveWhenElseBranchFix.createFactory());
factories.put(NO_ELSE_IN_WHEN, AddWhenElseBranchFix.createFactory());
factories.put(NO_TYPE_ARGUMENTS_ON_RHS_OF_IS_EXPRESSION, AddStarProjectionsFix.createFactoryForIsExpression());
factories.put(NO_TYPE_ARGUMENTS_ON_RHS, AddStarProjectionsFix.createFactoryForIsExpression());
factories.put(WRONG_NUMBER_OF_TYPE_ARGUMENTS, AddStarProjectionsFix.createFactoryForJavaClass());
factories.put(TYPE_ARGUMENTS_REDUNDANT_IN_SUPER_QUALIFIER, RemovePsiElementSimpleFix.createRemoveTypeArgumentsFactory());