Working on the typechecker: inferring common supertypes taking projections and bounds into account
This commit is contained in:
@@ -9,6 +9,7 @@ import org.jetbrains.jet.lang.types.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
@@ -35,11 +36,12 @@ public class TypeResolver {
|
||||
public void visitUserType(JetUserType type) {
|
||||
ClassDescriptor classDescriptor = resolveClass(scope, type);
|
||||
if (classDescriptor != null) {
|
||||
TypeConstructor typeConstructor = classDescriptor.getTypeConstructor();
|
||||
result[0] = new TypeImpl(
|
||||
attributes,
|
||||
classDescriptor.getTypeConstructor(),
|
||||
typeConstructor,
|
||||
nullable,
|
||||
resolveTypeProjections(scope, type.getTypeArguments()),
|
||||
resolveTypeProjections(scope, typeConstructor, type.getTypeArguments()),
|
||||
JetStandardClasses.STUB
|
||||
);
|
||||
}
|
||||
@@ -97,18 +99,22 @@ public class TypeResolver {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<TypeProjection> resolveTypeProjections(JetScope scope, List<JetTypeProjection> argumentElements) {
|
||||
private List<TypeProjection> resolveTypeProjections(JetScope scope, TypeConstructor constructor, List<JetTypeProjection> argumentElements) {
|
||||
final List<TypeProjection> arguments = new ArrayList<TypeProjection>();
|
||||
for (JetTypeProjection argumentElement : argumentElements) {
|
||||
for (int i = 0, argumentElementsSize = argumentElements.size(); i < argumentElementsSize; i++) {
|
||||
JetTypeProjection argumentElement = argumentElements.get(i);
|
||||
|
||||
ProjectionKind projectionKind = argumentElement.getProjectionKind();
|
||||
Type type;
|
||||
if (projectionKind == ProjectionKind.NEITHER_OUT_NOR_IN) {
|
||||
type = null;
|
||||
} else {
|
||||
type = resolveType(scope, argumentElement.getTypeReference());
|
||||
Set<Type> upperBounds = constructor.getParameters().get(i).getUpperBounds();
|
||||
arguments.add(new TypeProjection(ProjectionKind.OUT_ONLY, TypeUtils.intersect(upperBounds)));
|
||||
}
|
||||
else {
|
||||
// TODO : handle the Foo<in *> case
|
||||
type = resolveType(scope, argumentElement.getTypeReference());
|
||||
arguments.add(new TypeProjection(projectionKind, type));
|
||||
}
|
||||
TypeProjection typeProjection = new TypeProjection(projectionKind, type);
|
||||
arguments.add(typeProjection);
|
||||
}
|
||||
return arguments;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.jetbrains.jet.lang.types;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
public class DfsNodeHandler<R> {
|
||||
|
||||
public void beforeChildren(Type current) {
|
||||
|
||||
}
|
||||
|
||||
public void afterChildren(Type current) {
|
||||
|
||||
}
|
||||
|
||||
public R result() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@ public class JetStandardClasses {
|
||||
);
|
||||
|
||||
private static final Type ANY_TYPE = new TypeImpl(ANY.getTypeConstructor(), TypeMemberDomain.EMPTY);
|
||||
private static final Type NULLABLE_ANY_TYPE = TypeUtils.makeNullable(ANY_TYPE);
|
||||
|
||||
private static final ClassDescriptor BYTE = new ClassDescriptor("Byte");
|
||||
private static final ClassDescriptor CHAR = new ClassDescriptor("Char");
|
||||
@@ -104,6 +105,10 @@ public class JetStandardClasses {
|
||||
return ANY_TYPE;
|
||||
}
|
||||
|
||||
public static Type getNullableAnyType() {
|
||||
return NULLABLE_ANY_TYPE;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ClassDescriptor getByte() {
|
||||
return BYTE;
|
||||
|
||||
@@ -127,7 +127,7 @@ public class JetTypeChecker {
|
||||
} else {
|
||||
Type thenType = getType(scope, expression.getThen());
|
||||
Type elseType = getType(scope, elseBranch);
|
||||
result[0] = commonSupertype(thenType, elseType);
|
||||
result[0] = commonSupertype(Arrays.asList(thenType, elseType));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,53 +150,213 @@ public class JetTypeChecker {
|
||||
return result[0];
|
||||
}
|
||||
|
||||
private Type commonSupertype(Type thenType, Type elseType) {
|
||||
if (JetStandardClasses.isNothing(thenType)) {
|
||||
return elseType;
|
||||
}
|
||||
if (JetStandardClasses.isNothing(elseType)) {
|
||||
return thenType;
|
||||
private Type commonSupertype(Collection<Type> types) {
|
||||
Collection<Type> typeSet = new HashSet<Type>(types);
|
||||
assert !typeSet.isEmpty();
|
||||
boolean nullable = false;
|
||||
for (Iterator<Type> iterator = typeSet.iterator(); iterator.hasNext();) {
|
||||
Type type = iterator.next();
|
||||
if (JetStandardClasses.isNothing(type)) {
|
||||
iterator.remove();
|
||||
}
|
||||
nullable |= type.isNullable();
|
||||
}
|
||||
|
||||
List<Type> thenOrder = new LinkedList<Type>(); // adding to the beginning
|
||||
Map<TypeConstructor, Type> visited = new HashMap<TypeConstructor, Type>();
|
||||
topologicallySort(thenType, visited, thenOrder, null);
|
||||
|
||||
List<Type> elseOrder = new LinkedList<Type>(); // adding to the beginning
|
||||
topologicallySort(elseType, new HashMap<TypeConstructor, Type>(), elseOrder, visited);
|
||||
|
||||
assert !elseOrder.isEmpty() : "No common supertype";
|
||||
// TODO: support multiple common supertypes
|
||||
|
||||
Type result = elseOrder.get(0);
|
||||
|
||||
if (thenType.isNullable() || elseType.isNullable()) {
|
||||
return TypeUtils.makeNullable(result);
|
||||
if (typeSet.isEmpty()) {
|
||||
// TODO : attributes
|
||||
return nullable ? JetStandardClasses.getNullableNothingType() : JetStandardClasses.getNothingType();
|
||||
}
|
||||
assert !result.isNullable();
|
||||
|
||||
if (typeSet.size() == 1) {
|
||||
return TypeUtils.makeNullableIfNeeded(typeSet.iterator().next(), nullable);
|
||||
}
|
||||
|
||||
Map<TypeConstructor, Set<Type>> commonSupertypes = computeCommonRawSupertypes(typeSet);
|
||||
while (commonSupertypes.size() > 1) {
|
||||
HashSet<Type> merge = new HashSet<Type>();
|
||||
for (Set<Type> supertypes : commonSupertypes.values()) {
|
||||
merge.addAll(supertypes);
|
||||
}
|
||||
commonSupertypes = computeCommonRawSupertypes(merge);
|
||||
}
|
||||
assert !commonSupertypes.isEmpty();
|
||||
Map.Entry<TypeConstructor, Set<Type>> entry = commonSupertypes.entrySet().iterator().next();
|
||||
Type result = computeSupertypeProjections(entry.getKey(), entry.getValue());
|
||||
|
||||
return TypeUtils.makeNullableIfNeeded(result, nullable);
|
||||
}
|
||||
|
||||
private Type computeSupertypeProjections(TypeConstructor constructor, Set<Type> types) {
|
||||
// we assume that all the given types are applications of the same type constructor
|
||||
|
||||
assert !types.isEmpty();
|
||||
|
||||
if (types.size() == 1) {
|
||||
return types.iterator().next();
|
||||
}
|
||||
|
||||
List<TypeParameterDescriptor> parameters = constructor.getParameters();
|
||||
List<TypeProjection> newProjections = new ArrayList<TypeProjection>();
|
||||
for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) {
|
||||
TypeParameterDescriptor parameterDescriptor = parameters.get(i);
|
||||
Set<TypeProjection> typeProjections = new HashSet<TypeProjection>();
|
||||
for (Type type : types) {
|
||||
typeProjections.add(type.getArguments().get(i));
|
||||
}
|
||||
newProjections.add(computeSupertypeProjection(parameterDescriptor, typeProjections));
|
||||
}
|
||||
|
||||
boolean nullable = false;
|
||||
for (Type type : types) {
|
||||
nullable |= type.isNullable();
|
||||
}
|
||||
|
||||
// TODO : attributes?
|
||||
return new TypeImpl(Collections.<Attribute>emptyList(), constructor, nullable, newProjections, JetStandardClasses.STUB);
|
||||
}
|
||||
|
||||
private TypeProjection computeSupertypeProjection(TypeParameterDescriptor parameterDescriptor, Set<TypeProjection> typeProjections) {
|
||||
// TODO: equals/hashCode for projections
|
||||
if (typeProjections.size() == 1) {
|
||||
return typeProjections.iterator().next();
|
||||
}
|
||||
|
||||
Set<Type> ins = new HashSet<Type>();
|
||||
Set<Type> outs = new HashSet<Type>();
|
||||
|
||||
Variance variance = parameterDescriptor.getVariance();
|
||||
switch (variance) {
|
||||
case INVARIANT:
|
||||
// Nothing
|
||||
break;
|
||||
case IN_VARIANCE:
|
||||
outs = null;
|
||||
break;
|
||||
case OUT_VARIANCE:
|
||||
ins = null;
|
||||
break;
|
||||
}
|
||||
|
||||
for (TypeProjection projection : typeProjections) {
|
||||
ProjectionKind projectionKind = projection.getProjectionKind();
|
||||
if (projectionKind.allowsInCalls()) {
|
||||
if (ins != null) {
|
||||
ins.add(projection.getType());
|
||||
}
|
||||
} else {
|
||||
ins = null;
|
||||
}
|
||||
|
||||
if (projectionKind.allowsOutCalls()) {
|
||||
if (outs != null) {
|
||||
outs.add(projection.getType());
|
||||
}
|
||||
} else {
|
||||
outs = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (ins != null) {
|
||||
ProjectionKind projectionKind = variance == Variance.IN_VARIANCE ? ProjectionKind.NO_PROJECTION : ProjectionKind.IN_ONLY;
|
||||
return new TypeProjection(projectionKind, TypeUtils.intersect(ins));
|
||||
} else if (outs != null) {
|
||||
ProjectionKind projectionKind = variance == Variance.OUT_VARIANCE ? ProjectionKind.NO_PROJECTION : ProjectionKind.OUT_ONLY;
|
||||
return new TypeProjection(projectionKind, commonSupertype(outs));
|
||||
} else {
|
||||
ProjectionKind projectionKind = variance == Variance.OUT_VARIANCE ? ProjectionKind.NO_PROJECTION : ProjectionKind.OUT_ONLY;
|
||||
return new TypeProjection(projectionKind, commonSupertype(parameterDescriptor.getUpperBounds()));
|
||||
}
|
||||
}
|
||||
|
||||
private Map<TypeConstructor, Set<Type>> computeCommonRawSupertypes(Collection<Type> types) {
|
||||
assert !types.isEmpty();
|
||||
|
||||
final Map<TypeConstructor, Set<Type>> constructorToAllInstances = new HashMap<TypeConstructor, Set<Type>>();
|
||||
Set<TypeConstructor> commonSuperclasses = null;
|
||||
|
||||
List<TypeConstructor> order = null;
|
||||
for (Iterator<Type> iterator = types.iterator(); iterator.hasNext();) {
|
||||
Type type = iterator.next();
|
||||
|
||||
Set<TypeConstructor> visited = new HashSet<TypeConstructor>();
|
||||
|
||||
order = dfs(type, visited, new DfsNodeHandler<List<TypeConstructor>>() {
|
||||
public LinkedList<TypeConstructor> list = new LinkedList<TypeConstructor>();
|
||||
|
||||
@Override
|
||||
public void beforeChildren(Type current) {
|
||||
TypeConstructor constructor = current.getConstructor();
|
||||
|
||||
Set<Type> instances = constructorToAllInstances.get(constructor);
|
||||
if (instances == null) {
|
||||
instances = new HashSet<Type>();
|
||||
constructorToAllInstances.put(constructor, instances);
|
||||
}
|
||||
instances.add(current);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterChildren(Type current) {
|
||||
list.addFirst(current.getConstructor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TypeConstructor> result() {
|
||||
return list;
|
||||
}
|
||||
});
|
||||
|
||||
if (commonSuperclasses == null) {
|
||||
commonSuperclasses = visited;
|
||||
} else {
|
||||
commonSuperclasses.retainAll(visited);
|
||||
}
|
||||
}
|
||||
assert order != null;
|
||||
|
||||
Set<TypeConstructor> notSource = new HashSet<TypeConstructor>();
|
||||
Map<TypeConstructor, Set<Type>> result = new HashMap<TypeConstructor, Set<Type>>();
|
||||
for (TypeConstructor superConstructor : order) {
|
||||
if (!commonSuperclasses.contains(superConstructor)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!notSource.contains(superConstructor)) {
|
||||
result.put(superConstructor, constructorToAllInstances.get(superConstructor));
|
||||
markAll(superConstructor, notSource);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void topologicallySort(Type current, Map<TypeConstructor, Type> visited, List<Type> topologicalOrder, @Nullable Map<TypeConstructor, Type> filter) {
|
||||
Type visitedOccurrence = visited.put(current.getConstructor(), current);
|
||||
if (visitedOccurrence != null) {
|
||||
assert equalTypes(visitedOccurrence, current);
|
||||
private void markAll(TypeConstructor typeConstructor, Set<TypeConstructor> markerSet) {
|
||||
markerSet.add(typeConstructor);
|
||||
for (Type type : typeConstructor.getSupertypes()) {
|
||||
markAll(type.getConstructor(), markerSet);
|
||||
}
|
||||
}
|
||||
|
||||
private <R> R dfs(Type current, Set<TypeConstructor> visited, DfsNodeHandler<R> handler) {
|
||||
doDfs(current, visited, handler);
|
||||
return handler.result();
|
||||
}
|
||||
|
||||
private void doDfs(Type current, Set<TypeConstructor> visited, DfsNodeHandler<?> handler) {
|
||||
if (!visited.add(current.getConstructor())) {
|
||||
return;
|
||||
}
|
||||
handler.beforeChildren(current);
|
||||
Map<TypeConstructor, TypeProjection> substitutionContext = getSubstitutionContext(current);
|
||||
for (Type supertype : current.getConstructor().getSupertypes()) {
|
||||
TypeConstructor supertypeConstructor = supertype.getConstructor();
|
||||
if (visited.containsKey(supertypeConstructor)) {
|
||||
assert equalTypes(visited.get(supertypeConstructor), supertype);
|
||||
if (visited.contains(supertypeConstructor)) {
|
||||
continue;
|
||||
}
|
||||
Type substitutedSupertype = substituteInType(substitutionContext, supertype);
|
||||
topologicallySort(substitutedSupertype, visited, topologicalOrder, filter);
|
||||
}
|
||||
if (filter != null && filter.containsKey(current.getConstructor())) {
|
||||
assert equalTypes(filter.get(current.getConstructor()), current) : filter.get(current.getConstructor()) + " != " + current;
|
||||
topologicalOrder.add(0, current);
|
||||
dfs(substitutedSupertype, visited, handler);
|
||||
}
|
||||
handler.afterChildren(current);
|
||||
}
|
||||
|
||||
public boolean isConvertibleTo(JetExpression expression, Type type) {
|
||||
@@ -358,27 +518,7 @@ public class JetTypeChecker {
|
||||
}
|
||||
|
||||
public boolean equalTypes(@NotNull Type type1, @NotNull Type type2) {
|
||||
if (!type1.getConstructor().equals(type2.getConstructor())) {
|
||||
return false;
|
||||
}
|
||||
List<TypeProjection> type1Arguments = type1.getArguments();
|
||||
List<TypeProjection> type2Arguments = type2.getArguments();
|
||||
if (type1Arguments.size() != type2Arguments.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < type1Arguments.size(); i++) {
|
||||
TypeProjection typeProjection1 = type1Arguments.get(i);
|
||||
TypeProjection typeProjection2 = type2Arguments.get(i);
|
||||
if (typeProjection1.getProjectionKind() != typeProjection2.getProjectionKind()) {
|
||||
return false;
|
||||
}
|
||||
if (typeProjection1.getProjectionKind() != ProjectionKind.NEITHER_OUT_NOR_IN) {
|
||||
if (!equalTypes(typeProjection1.getType(), typeProjection2.getType())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return TypeImpl.equalTypes(type1, type2);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,6 +13,4 @@ public interface Type extends Annotated {
|
||||
boolean isNullable();
|
||||
|
||||
@NotNull TypeMemberDomain getMemberDomain();
|
||||
|
||||
@NotNull Type getNullableVersion();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.jetbrains.jet.lang.types;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -26,15 +28,6 @@ public final class TypeImpl extends AnnotatedImpl implements Type {
|
||||
this(Collections.<Attribute>emptyList(), constructor, false, Collections.<TypeProjection>emptyList(), memberDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getNullableVersion() {
|
||||
if (isNullable()) {
|
||||
return this;
|
||||
}
|
||||
// TODO: cache these?
|
||||
return new TypeImpl(getAttributes(), constructor, true, arguments, memberDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeConstructor getConstructor() {
|
||||
return constructor;
|
||||
@@ -72,4 +65,58 @@ public final class TypeImpl extends AnnotatedImpl implements Type {
|
||||
return stringBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
TypeImpl type = (TypeImpl) o;
|
||||
|
||||
// TODO
|
||||
return equalTypes(this, type);
|
||||
// if (nullable != type.nullable) return false;
|
||||
// if (arguments != null ? !arguments.equals(type.arguments) : type.arguments != null) return false;
|
||||
// if (constructor != null ? !constructor.equals(type.constructor) : type.constructor != null) return false;
|
||||
// if (memberDomain != null ? !memberDomain.equals(type.memberDomain) : type.memberDomain != null) return false;
|
||||
|
||||
// return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = constructor != null ? constructor.hashCode() : 0;
|
||||
result = 31 * result + (arguments != null ? arguments.hashCode() : 0);
|
||||
result = 31 * result + (nullable ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public static boolean equalTypes(@NotNull Type type1, @NotNull Type type2) {
|
||||
if (type1.isNullable() != type2.isNullable()) {
|
||||
return false;
|
||||
}
|
||||
if (!type1.getConstructor().equals(type2.getConstructor())) {
|
||||
return false;
|
||||
}
|
||||
List<TypeProjection> type1Arguments = type1.getArguments();
|
||||
List<TypeProjection> type2Arguments = type2.getArguments();
|
||||
if (type1Arguments.size() != type2Arguments.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < type1Arguments.size(); i++) {
|
||||
TypeProjection typeProjection1 = type1Arguments.get(i);
|
||||
TypeProjection typeProjection2 = type2Arguments.get(i);
|
||||
if (typeProjection1.getProjectionKind() != typeProjection2.getProjectionKind()) {
|
||||
return false;
|
||||
}
|
||||
if (typeProjection1.getProjectionKind() != ProjectionKind.NEITHER_OUT_NOR_IN) {
|
||||
if (!equalTypes(typeProjection1.getType(), typeProjection2.getType())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
package org.jetbrains.jet.lang.types;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
public class TypeParameterDescriptor extends NamedAnnotatedImpl {
|
||||
private final Variance variance;
|
||||
private final Collection<Type> upperBounds;
|
||||
private final Set<Type> upperBounds;
|
||||
private final TypeConstructor typeConstructor;
|
||||
|
||||
public TypeParameterDescriptor(List<Attribute> attributes, Variance variance, String name, Collection<Type> upperBounds) {
|
||||
public TypeParameterDescriptor(List<Attribute> attributes, Variance variance, String name, Set<Type> upperBounds) {
|
||||
super(attributes, name);
|
||||
this.variance = variance;
|
||||
this.upperBounds = upperBounds;
|
||||
@@ -24,11 +24,15 @@ public class TypeParameterDescriptor extends NamedAnnotatedImpl {
|
||||
upperBounds);
|
||||
}
|
||||
|
||||
public TypeParameterDescriptor(List<Attribute> attributes, Variance variance, String name) {
|
||||
this(attributes, variance, name, Collections.singleton(JetStandardClasses.getNullableAnyType()));
|
||||
}
|
||||
|
||||
public Variance getVariance() {
|
||||
return variance;
|
||||
}
|
||||
|
||||
public Collection<Type> getUpperBounds() {
|
||||
public Set<Type> getUpperBounds() {
|
||||
return upperBounds;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package org.jetbrains.jet.lang.types;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
@@ -10,4 +15,41 @@ public class TypeUtils {
|
||||
}
|
||||
return new TypeImpl(type.getAttributes(), type.getConstructor(), true, type.getArguments(), type.getMemberDomain());
|
||||
}
|
||||
|
||||
public static Type intersect(Set<Type> types) {
|
||||
assert !types.isEmpty();
|
||||
|
||||
if (types.size() == 1) {
|
||||
return types.iterator().next();
|
||||
}
|
||||
|
||||
StringBuilder debugName = new StringBuilder();
|
||||
boolean nullable = false;
|
||||
for (Iterator<Type> iterator = types.iterator(); iterator.hasNext();) {
|
||||
Type type = iterator.next();
|
||||
|
||||
nullable |= type.isNullable();
|
||||
|
||||
debugName.append(type.toString());
|
||||
if (iterator.hasNext()) {
|
||||
debugName.append(" & ");
|
||||
}
|
||||
}
|
||||
|
||||
List<Attribute> noAttributes = Collections.<Attribute>emptyList();
|
||||
TypeConstructor constructor = new TypeConstructor(noAttributes, debugName.toString(), Collections.<TypeParameterDescriptor>emptyList(), types);
|
||||
return new TypeImpl(
|
||||
noAttributes,
|
||||
constructor,
|
||||
nullable,
|
||||
Collections.<TypeProjection>emptyList(),
|
||||
JetStandardClasses.STUB);
|
||||
}
|
||||
|
||||
public static Type makeNullableIfNeeded(Type type, boolean nullable) {
|
||||
if (nullable) {
|
||||
return makeNullable(type);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user