diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java index 4e9d892fa0d..49b80311b59 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java @@ -17,6 +17,7 @@ public class JetTypeChecker { this.standardLibrary = standardLibrary; } + @NotNull private Map> getConversionMap() { // if (conversionMap.size() == 0) { // addConversion(standardLibrary.getByte(), @@ -62,7 +63,13 @@ public class JetTypeChecker { // conversionMap.put(actual.getTypeConstructor(), new HashSet(Arrays.asList(constructors))); // } // - public JetType commonSupertype(Collection types) { + @NotNull + public JetType commonSupertype(@NotNull JetType... types) { + return commonSupertype(Arrays.asList(types)); + } + + @NotNull + public JetType commonSupertype(@NotNull Collection types) { Collection typeSet = new HashSet(types); assert !typeSet.isEmpty(); boolean nullable = false; @@ -98,7 +105,8 @@ public class JetTypeChecker { return TypeUtils.makeNullableIfNeeded(result, nullable); } - private JetType computeSupertypeProjections(TypeConstructor constructor, Set types) { + @NotNull + private JetType computeSupertypeProjections(@NotNull TypeConstructor constructor, @NotNull Set types) { // we assume that all the given types are applications of the same type constructor assert !types.isEmpty(); @@ -127,7 +135,8 @@ public class JetTypeChecker { return new JetTypeImpl(Collections.emptyList(), constructor, nullable, newProjections, JetStandardClasses.STUB); } - private TypeProjection computeSupertypeProjection(TypeParameterDescriptor parameterDescriptor, Set typeProjections) { + @NotNull + private TypeProjection computeSupertypeProjection(@NotNull TypeParameterDescriptor parameterDescriptor, @NotNull Set typeProjections) { if (typeProjections.size() == 1) { return typeProjections.iterator().next(); } @@ -183,7 +192,8 @@ public class JetTypeChecker { } } - private Map> computeCommonRawSupertypes(Collection types) { + @NotNull + private Map> computeCommonRawSupertypes(@NotNull Collection types) { assert !types.isEmpty(); final Map> constructorToAllInstances = new HashMap>(); @@ -244,19 +254,19 @@ public class JetTypeChecker { return result; } - private void markAll(TypeConstructor typeConstructor, Set markerSet) { + private void markAll(@NotNull TypeConstructor typeConstructor, @NotNull Set markerSet) { markerSet.add(typeConstructor); for (JetType type : typeConstructor.getSupertypes()) { markAll(type.getConstructor(), markerSet); } } - private R dfs(JetType current, Set visited, DfsNodeHandler handler) { + private R dfs(@NotNull JetType current, @NotNull Set visited, @NotNull DfsNodeHandler handler) { doDfs(current, visited, handler); return handler.result(); } - private void doDfs(JetType current, Set visited, DfsNodeHandler handler) { + private void doDfs(@NotNull JetType current, @NotNull Set visited, @NotNull DfsNodeHandler handler) { if (!visited.add(current.getConstructor())) { return; } @@ -307,7 +317,7 @@ public class JetTypeChecker { // This method returns the supertype of the first parameter that has the same constructor // as the second parameter, applying the substitution of type arguments to it @Nullable - private JetType findCorrespondingSupertype(JetType subtype, JetType supertype) { + private JetType findCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) { TypeConstructor constructor = subtype.getConstructor(); if (constructor.equals(supertype.getConstructor())) { return subtype; @@ -321,7 +331,7 @@ public class JetTypeChecker { return null; } - private boolean checkSubtypeForTheSameConstructor(JetType subtype, JetType supertype) { + private boolean checkSubtypeForTheSameConstructor(@NotNull JetType subtype, @NotNull JetType supertype) { TypeConstructor constructor = subtype.getConstructor(); assert constructor.equals(supertype.getConstructor()); diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index 6c74d7bcba2..a5b76e9877c 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -728,6 +728,18 @@ public class JetTypeInferrer { } result = semanticServices.getStandardLibrary().getBooleanType(); } + else if (operationType == JetTokens.ELVIS) { + JetType leftType = getType(scope, left, false); + JetType rightType = right == null ? null : getType(scope, right, false); + if (leftType != null) { + if (!leftType.isNullable()) { + semanticServices.getErrorHandler().genericWarning(left.getNode(), "Elvis operator (?:) is always returns the left operand of non-nullable type " + leftType); + } + if (rightType != null) { + result = TypeUtils.makeNullableAsSpecified(semanticServices.getTypeChecker().commonSupertype(leftType, rightType), rightType.isNullable()); + } + } + } else { semanticServices.getErrorHandler().genericError(operationSign.getNode(), "Unknown operation"); } diff --git a/idea/src/org/jetbrains/jet/lang/types/TypeUtils.java b/idea/src/org/jetbrains/jet/lang/types/TypeUtils.java index 4e83ef9115b..7a11c4babaf 100644 --- a/idea/src/org/jetbrains/jet/lang/types/TypeUtils.java +++ b/idea/src/org/jetbrains/jet/lang/types/TypeUtils.java @@ -12,17 +12,18 @@ import java.util.Set; */ public class TypeUtils { public static JetType makeNullable(JetType type) { - if (type.isNullable()) { - return type; - } - return new JetTypeImpl(type.getAttributes(), type.getConstructor(), true, type.getArguments(), type.getMemberScope()); + return makeNullableAsSpecified(type, true); } public static JetType makeNotNullable(JetType type) { - if (!type.isNullable()) { + return makeNullableAsSpecified(type, false); + } + + public static JetType makeNullableAsSpecified(JetType type, boolean nullable) { + if (type.isNullable() == nullable) { return type; } - return new JetTypeImpl(type.getAttributes(), type.getConstructor(), false, type.getArguments(), type.getMemberScope()); + return new JetTypeImpl(type.getAttributes(), type.getConstructor(), nullable, type.getArguments(), type.getMemberScope()); } @Nullable diff --git a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java index 9671b9309f8..588afd7c0ff 100644 --- a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java +++ b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java @@ -405,6 +405,7 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { assertType("1 !== null", "Boolean"); assertType("true && false", "Boolean"); assertType("true || false", "Boolean"); + assertType("null ?: false", "Boolean"); } private void assertSubtype(String type1, String type2) {