Skip to content

Commit

Permalink
canAssign: support for implicit cast of function types
Browse files Browse the repository at this point in the history
  • Loading branch information
m0rkeulv committed Oct 15, 2023
1 parent 6351a0c commit 89410d6
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 34 deletions.
80 changes: 56 additions & 24 deletions src/main/java/com/intellij/plugins/haxe/model/HaxeClassModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -274,19 +274,35 @@ public List<HaxeType> getAbstractToList() {
return types;
}

public List<SpecificHaxeClassReference> getImplicitCastToTypesList(SpecificHaxeClassReference sourceType) {
public List<SpecificHaxeClassReference> getImplicitCastToTypesListClassOnly(SpecificHaxeClassReference sourceType ) {
List<SpecificHaxeClassReference> list = new ArrayList<>();
for (SpecificTypeReference reference : getImplicitCastToTypesList(sourceType)) {
if (reference instanceof SpecificHaxeClassReference classReference) {
list.add(classReference);
}
}
return list;

}
public List<SpecificTypeReference> getImplicitCastToTypesList(SpecificHaxeClassReference sourceType ) {
if (!isAbstractType()) return Collections.emptyList();
List<HaxeMethodModel> methodsWithMetadata = getCastToMethods();

List<SpecificHaxeClassReference> list = new ArrayList<>();
List<SpecificTypeReference> list = new ArrayList<>();
for (HaxeMethodModel methodModel : methodsWithMetadata) {
if (castMethodAcceptsSource(sourceType, methodModel)) {
SpecificHaxeClassReference reference = setSpecificsConstraints(methodModel, getReturnType(methodModel),
sourceType.getGenericResolver());
SpecificTypeReference returnType = getReturnType(methodModel);
if (returnType.canBeTypeVariable()) {
ResultHolder resolve = sourceType.getGenericResolver().resolve(((SpecificHaxeClassReference)returnType).getClassName());
if (resolve!= null && !resolve.isUnknown()) {
returnType = resolve.getType();
}
}
SpecificTypeReference reference = setSpecificsConstraints(methodModel, returnType, sourceType.getGenericResolver());
list.add(reference);

if (reference.isNullType()) {
SpecificHaxeClassReference underlyingClass = getUnderlyingClassIfAbstractNull(reference);
SpecificHaxeClassReference underlyingClass = getUnderlyingClassIfAbstractNull((SpecificHaxeClassReference)reference);
list.add(underlyingClass);
}
}
Expand All @@ -300,7 +316,7 @@ private boolean castMethodAcceptsSource(@NotNull SpecificHaxeClassReference refe
//implicit cast methods seems to accept both parameter-less methods and single parameter methods
if (parameter == null) return true; // if no param then "this" is the input and will always be compatible.
HaxeGenericResolver resolver = reference.getGenericResolver().withoutUnknowns();
SpecificHaxeClassReference parameterWithRealRealSpecifics = setSpecificsConstraints(methodModel, parameter, resolver);
SpecificTypeReference parameterWithRealRealSpecifics = setSpecificsConstraints(methodModel, parameter, resolver);

if(reference.isAbstractType()) {
SpecificHaxeClassReference underlying = reference.getHaxeClassModel().getUnderlyingClassReference(reference.getGenericResolver());
Expand All @@ -310,16 +326,28 @@ private boolean castMethodAcceptsSource(@NotNull SpecificHaxeClassReference refe
return canAssignToFrom(parameterWithRealRealSpecifics, reference, false, null,null, null);
}

public List<SpecificHaxeClassReference> getImplicitCastFromTypesList(SpecificHaxeClassReference targetType) {
public List<SpecificHaxeClassReference> getImplicitCastFromTypesListClassOnly(SpecificHaxeClassReference targetType) {
List<SpecificHaxeClassReference> list = new ArrayList<>();
for (SpecificTypeReference reference : getImplicitCastFromTypesList(targetType)) {
if (reference instanceof SpecificHaxeClassReference classReference) {
list.add(classReference);
}
}
return list;
}

public List<SpecificTypeReference> getImplicitCastFromTypesList(SpecificHaxeClassReference targetType) {
if (!isAbstractType()) return Collections.emptyList();
List<HaxeMethodModel> methodsWithMetadata = getCastFromMethods();

HaxeGenericResolver resolver = this.getGenericResolver(null);

// if return types can not be assign to target then skip this castMethod
List<SpecificHaxeClassReference> list = new ArrayList<>();
List<SpecificTypeReference> list = new ArrayList<>();
for (HaxeMethodModel m : methodsWithMetadata) {
// TODO consider applying generics from targetType to be more strict about what methods are supported ?
if (canAssignToFrom(targetType, setSpecificsConstraints(m, getReturnType(m), targetType.getGenericResolver().withoutUnknowns()), false, null, null, null)) {
SpecificHaxeClassReference type = getImplicitCastFromType(m);
SpecificTypeReference type = getImplicitCastFromType(m, resolver);
if (type != null) {
list.add(type);
}
Expand All @@ -329,30 +357,34 @@ public List<SpecificHaxeClassReference> getImplicitCastFromTypesList(SpecificHax
}

@Nullable
private SpecificHaxeClassReference getImplicitCastFromType(@NotNull HaxeMethodModel methodModel) {
private SpecificTypeReference getImplicitCastFromType(@NotNull HaxeMethodModel methodModel, @Nullable HaxeGenericResolver resolver) {
SpecificHaxeClassReference parameter = getTypeOfFirstParameter(methodModel);
if (parameter == null) return null;
return setSpecificsConstraints(methodModel, parameter, null);
return setSpecificsConstraints(methodModel, parameter, resolver);
}

@NotNull
private SpecificHaxeClassReference setSpecificsConstraints(@NotNull HaxeMethodModel methodModel, @NotNull SpecificHaxeClassReference classReference,
private SpecificTypeReference setSpecificsConstraints(@NotNull HaxeMethodModel methodModel, @NotNull SpecificTypeReference classReference,
@Nullable HaxeGenericResolver resolver) {
ResultHolder[] specifics = classReference.getGenericResolver().getSpecifics();
ResultHolder[] newSpecifics = applyConstraintsToSpecifics(methodModel, specifics);

SpecificHaxeClassReference reference = replaceTypeIfGenericParameterName(methodModel, classReference);

if (resolver!= null) {
for (int i = 0; i < specifics.length; i++) {
ResultHolder specific = specifics[i];
ResultHolder resolved = resolver.resolve(specific);
if (!resolved.isUnknown() && canAssignToFrom(newSpecifics[i],resolved )) {
newSpecifics[i] = resolved;
if (classReference instanceof SpecificHaxeClassReference haxeClassReference) {
ResultHolder[] specifics = haxeClassReference.getGenericResolver().getSpecifics();
ResultHolder[] newSpecifics = applyConstraintsToSpecifics(methodModel, specifics);

SpecificHaxeClassReference reference = replaceTypeIfGenericParameterName(methodModel, haxeClassReference);

if (resolver != null) {
for (int i = 0; i < specifics.length; i++) {
ResultHolder specific = specifics[i];
ResultHolder resolved = resolver.resolve(specific);
if (!resolved.isUnknown() && canAssignToFrom(newSpecifics[i], resolved)) {
newSpecifics[i] = resolved;
}
}
}
return SpecificHaxeClassReference.withGenerics(reference.getHaxeClassReference(), newSpecifics);
}else {
return classReference;
}
return SpecificHaxeClassReference.withGenerics(reference.getHaxeClassReference(), newSpecifics);
}

//caching implicit cast method lookup results
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,18 +189,39 @@ static public boolean canAssignToFrom(
if (to.isDynamic() || from.isDynamic()) return true;

// if abstract of function is being compared to a function we map the abstract to its underlying function
if (hasAbstractFunctionTypeCast(to, true) && isFunctionTypeOrReference(from)) {
List<SpecificFunctionReference> functionTypes = getAbstractFunctionTypes((SpecificHaxeClassReference)to, true);
if (isFunctionTypeOrReference(from) && to.isAbstractType()) {
SpecificFunctionReference fromFunctionType = asFunctionReference(from);
for(SpecificFunctionReference functionType : functionTypes) {
if(canAssignToFromFunction(functionType, fromFunctionType, holder)) return true;
if (hasAbstractFunctionTypeCast(to, true)) {
List<SpecificFunctionReference> functionTypes = getAbstractFunctionTypes((SpecificHaxeClassReference)to, true);
for (SpecificFunctionReference functionType : functionTypes) {
if (canAssignToFromFunction(functionType, fromFunctionType, holder)) return true;
}
}
if (to instanceof SpecificHaxeClassReference classReference) {
List<SpecificTypeReference> list = getImplicitCast(classReference, true);
for (SpecificTypeReference reference : list) {
if (reference instanceof SpecificFunctionReference functionType) {
if (canAssignToFromFunction(functionType, fromFunctionType, holder)) return true;
}
}
}

}
if (isFunctionTypeOrReference(to) && hasAbstractFunctionTypeCast(from, false)) {
List<SpecificFunctionReference> functionTypes = getAbstractFunctionTypes((SpecificHaxeClassReference)from, false);
if (isFunctionTypeOrReference(to) && from.isAbstractType()) {
SpecificFunctionReference toFunctionType = asFunctionReference(to);
for(SpecificFunctionReference functionType : functionTypes) {
if(canAssignToFromFunction(toFunctionType, functionType, holder)) return true;
if (hasAbstractFunctionTypeCast(from, false)) {
List<SpecificFunctionReference> functionTypes = getAbstractFunctionTypes((SpecificHaxeClassReference)from, false);
for (SpecificFunctionReference functionType : functionTypes) {
if (canAssignToFromFunction(toFunctionType, functionType, holder)) return true;
}
}
if (from instanceof SpecificHaxeClassReference classReference) {
List<SpecificTypeReference> list = getImplicitCast(classReference, false);
for (SpecificTypeReference reference : list) {
if (reference instanceof SpecificFunctionReference functionType) {
if (canAssignToFromFunction(toFunctionType, functionType, holder)) return true;
}
}
}
}

Expand Down Expand Up @@ -235,6 +256,18 @@ static public boolean canAssignToFrom(
return false;
}

private static List<SpecificTypeReference> getImplicitCast(@NotNull SpecificHaxeClassReference classReference, boolean from) {

HaxeAbstractTypeDeclaration abstractType = (HaxeAbstractTypeDeclaration)classReference.getHaxeClass();
if (abstractType != null && abstractType.getModel() != null) {
HaxeClassModel model = abstractType.getModel();
return from
? model.getImplicitCastFromTypesList(classReference)
: model.getImplicitCastToTypesList(classReference);
}
return List.of();
}

static private boolean canAssignToFromFunction(
@NotNull SpecificFunctionReference to,
@NotNull SpecificFunctionReference from,
Expand Down Expand Up @@ -435,13 +468,13 @@ static private boolean canAssignToFromType(
if (canAssignToFromSpecificType(to, from)) return true;

Set<SpecificHaxeClassReference> compatibleTypes = to.getCompatibleTypes(SpecificHaxeClassReference.Compatibility.ASSIGNABLE_FROM);
if (to.isAbstractType() && includeImplicitCast) compatibleTypes.addAll(to.getHaxeClassModel().getImplicitCastFromTypesList(to));
if (to.isAbstractType() && includeImplicitCast) compatibleTypes.addAll(to.getHaxeClassModel().getImplicitCastFromTypesListClassOnly(to));
for (SpecificHaxeClassReference compatibleType : compatibleTypes) {
if (canAssignToFromSpecificType(compatibleType, from)) return true;
}

compatibleTypes = from.getCompatibleTypes(SpecificHaxeClassReference.Compatibility.ASSIGNABLE_TO);
if (from.isAbstractType() && includeImplicitCast) compatibleTypes.addAll(from.getHaxeClassModel().getImplicitCastToTypesList(from));
if (from.isAbstractType() && includeImplicitCast) compatibleTypes.addAll(from.getHaxeClassModel().getImplicitCastToTypesListClassOnly(from));
for (SpecificHaxeClassReference compatibleType : compatibleTypes) {
if (canAssignToFromSpecificType(to, compatibleType)) return true;
}
Expand Down

0 comments on commit 89410d6

Please sign in to comment.