Skip to content

Commit

Permalink
Workaround for issue resolving type for methods with recursive typePa…
Browse files Browse the repository at this point in the history
…rameters constraints.
  • Loading branch information
m0rkeulv committed Jan 13, 2025
1 parent 77f7479 commit 21b3d7a
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -968,8 +968,14 @@ public static ResultHolder tryToFindTypeFromCallExpression(@NotNull HaxeFunction
List<HaxeArgument> arguments = functionType.getArguments();
HaxeArgument argument = arguments.get(index);

ResultHolder resolved = validation.getCallExpressionResolver().withoutUnknowns().resolve(argument.getType());
if (resolved != null && !resolved.isUnknown()) return resolved.getType().createHolder();
ResultHolder argumentType = argument.getType();
ResultHolder resolved = validation.getCallExpressionResolver().withoutUnknowns().resolve(argumentType);
if (resolved != null && !resolved.isUnknown()){
return resolved.getType().createHolder();
}
if (argumentType.isTypeParameter()) {
return argumentType;
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ static public boolean canAssignToFromReference(@Nullable SpecificTypeReference t
if (to == null || from == null) return false;
return canAssignToFromReference(to.createHolder(), from.createHolder());
}
static public boolean canAssignToFromReference(HaxeAssignEvaluation context, @Nullable SpecificTypeReference to, @Nullable SpecificTypeReference from) {
if (to == null || from == null) return false;
return canAssignToFromEvaluation(to.createHolder(), from.createHolder(), false,true, true, context).result;
}

static public boolean canAssignToFromReference(@Nullable SpecificTypeReference to, @Nullable SpecificTypeReference from, boolean checkExplicitCasts, boolean checkImplicitCasts) {
if (to == null || from == null) return false;
Expand Down Expand Up @@ -134,9 +138,11 @@ static public HaxeAssignEvaluation canAssignToFromEvaluation(@NotNull ResultHold
}

evaluation.testBasicAssignRules(strictBasicCheck);
// prevent recrusion in the case of typedef and class hieriarchy loops, casting lopps etc
// prevent recursion in the case of typedef and class hierarchy loops, casting loops etc
if(!evaluation.completed) {
Boolean done = canAssignRecursionGuard.doPreventingRecursion(evaluation.recursionGuardKey(), true, () -> {
// NOTE: memoize can not be used as the context elements does not necessarily represent the type
// (could maybe do some tricks with fully qualified names but recursive typeParameter constraints will be problematic)
Boolean done = canAssignRecursionGuard.doPreventingRecursion(evaluation.recursionGuardKey(), false, () -> {
if (!evaluation.completed) evaluation.testClassAssignRules();
if (!evaluation.completed) evaluation.testEnumAssignRules();
if (!evaluation.completed) evaluation.testFunctionAssignRules();
Expand All @@ -145,8 +151,17 @@ static public HaxeAssignEvaluation canAssignToFromEvaluation(@NotNull ResultHold
if (!evaluation.completed) evaluation.testTypeParameterConstraints(strictBasicCheck, checkExplicitCasts, checkImplicitCasts);
return true;
});
if(done == null) {
evaluation.complete(false, "Stopped by recursion guard");
if (done == null) {
// stopped by recursion guard.

// we allow assign when recursion guard is triggered and the recursive types are typeParameter,
// anything else should fail the assign test. (this might not be the best solution but works for now)

// we allow type parameters as recursive constraints in typeParameters would always fail the assign test.
// Ex. the typeParameter for linkedList sort. T:{prev:T, next:T} (anonymous member check would fail)
// Note: this can probably fail for other complex cases of anonymous member checks as well.
boolean isRecursiveTypeParameter = to.isTypeParameter() || from.isTypeParameter();
evaluation.complete(isRecursiveTypeParameter, "Stopped by recursion guard");
}
}
return evaluation;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ public void testPreview() throws Exception {
public void testUntypedWithGenerics() throws Exception {
doTest(hintsProvider);
}
@Test
public void testUntypedWithRecursiveConstraintGenerics() throws Exception {
doTest(hintsProvider);
}


}
149 changes: 149 additions & 0 deletions src/test/resources/testData/haxe/4.3.6/std/haxe/ds/ListSort.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* Copyright (C)2005-2019 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/

package haxe.ds;

/**
ListSort provides a stable implementation of merge sort through its `sort`
method. It has a O(N.log(N)) complexity and does not require additional memory allocation.
**/
class ListSort {
// Note : we prefer [inline] over [@:generic] here since we want to inline the comparison function as well

/**
Sorts List `lst` according to the comparison function `cmp`, where
`cmp(x,y)` returns 0 if `x == y`, a positive Int if `x > y` and a
negative Int if `x < y`.
This operation modifies List `a` in place and returns its head once modified.
The `prev` of the head is set to the tail of the sorted list.
If `list` or `cmp` are null, the result is unspecified.
**/
public static inline function sort<T:{prev:T, next:T}>(list:T, cmp:T->T->Int):T {
// ported from http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
if (list == null)
return null;
var insize = 1, nmerges, psize = 0, qsize = 0;
var p, q, e, tail:T = null;
while (true) {
p = list;
list = null;
tail = null;
nmerges = 0;
while (p != null) {
nmerges++;
q = p;
psize = 0;
for (i in 0...insize) {
psize++;
q = q.next;
if (q == null)
break;
}
qsize = insize;
while (psize > 0 || (qsize > 0 && q != null)) {
if (psize == 0) {
e = q;
q = q.next;
qsize--;
} else if (qsize == 0 || q == null || cmp(p, q) <= 0) {
e = p;
p = p.next;
psize--;
} else {
e = q;
q = q.next;
qsize--;
}
if (tail != null)
tail.next = e;
else
list = e;
e.prev = tail;
tail = e;
}
p = q;
}
tail.next = null;
if (nmerges <= 1)
break;
insize *= 2;
}
list.prev = tail;
return list;
}

/**
Same as `sort` but on single linked list.
**/
public static inline function sortSingleLinked<T:{next:T}>(list:T, cmp:T->T->Int):T {
if (list == null)
return null;
var insize = 1, nmerges, psize = 0, qsize = 0;
var p, q, e, tail:T;
while (true) {
p = list;
list = null;
tail = null;
nmerges = 0;
while (p != null) {
nmerges++;
q = p;
psize = 0;
for (i in 0...insize) {
psize++;
q = q.next;
if (q == null)
break;
}
qsize = insize;
while (psize > 0 || (qsize > 0 && q != null)) {
if (psize == 0) {
e = q;
q = q.next;
qsize--;
} else if (qsize == 0 || q == null || cmp(p, q) <= 0) {
e = p;
p = p.next;
psize--;
} else {
e = q;
q = q.next;
qsize--;
}
if (tail != null)
tail.next = e;
else
list = e;
tail = e;
}
p = q;
}
tail.next = null;
if (nmerges <= 1)
break;
insize *= 2;
}
return list;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class Untypedgenerics<T> {
function sort(list:TestClass) {
//sort<T:{prev:T, next:T}>(list:T, cmp:T->T->Int):T
haxe.ds.ListSort.sort(list, function(p1/*<# :TestClass #>*/, p2/*<# :TestClass #>*/) return p1.value < p2.value ? 1 : -1);
haxe.ds.ListSort.sort(list, (p1/*<# :TestClass #>*/, p2/*<# :TestClass #>*/) -> p1.value < p2.value ? 1 : -1);
}
}

class TestClass {
public var next:TestClass;
public var prev:TestClass;
public var value:Int;
}

0 comments on commit 21b3d7a

Please sign in to comment.