Skip to content

Commit acac4cb

Browse files
authored
Fix callees in specialized functions (#1116)
1 parent 7e7a724 commit acac4cb

File tree

3 files changed

+278
-3
lines changed

3 files changed

+278
-3
lines changed

de.peeeq.wurstscript/settings.gradle

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
pluginManagement {
22
repositories {
3-
maven { url = uri("https://cache-redirector.jetbrains.com/plugins.gradle.org/m2") }
43
gradlePluginPortal()
54
maven { url = uri("https://plugins.gradle.org/m2") }
6-
maven { url = uri("https://cache-redirector.jetbrains.com/maven-central") }
75
mavenCentral()
86
}
97
}
108

119
dependencyResolutionManagement {
1210
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
1311
repositories {
14-
maven { url = uri("https://cache-redirector.jetbrains.com/maven-central") }
1512
mavenCentral()
1613
google()
1714
maven { url 'https://jitpack.io' }

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateGenerics.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,49 @@ private void eliminateGenericUses() {
223223
}
224224
}
225225

226+
private void fixCalleesInSpecializedFunction(ImFunction newF, GenericTypes generics) {
227+
newF.accept(new Element.DefaultVisitor() {
228+
229+
@Override
230+
public void visit(ImFunctionCall fc) {
231+
super.visit(fc);
232+
233+
ImFunction callee = fc.getFunc();
234+
if (callee == null) return;
235+
236+
// Only interesting if callee itself is generic
237+
if (callee.getTypeVariables().isEmpty()) {
238+
return;
239+
}
240+
241+
// Determine which generics to use for the callee
242+
GenericTypes calleeGenerics;
243+
244+
if (!fc.getTypeArguments().isEmpty()) {
245+
// Call carries explicit type args → honor them
246+
calleeGenerics = new GenericTypes(specializeTypeArgs(fc.getTypeArguments()));
247+
} else {
248+
// No explicit type args → use the same generics context as the enclosing function.
249+
// This matches the pattern: destroyArrayList<T>(this: ArrayList<T>) calls ArrayList_onDestroy<T>(this)
250+
calleeGenerics = generics;
251+
}
252+
253+
if (calleeGenerics.containsTypeVariable()) {
254+
// Still not concrete → let the normal pipeline handle it later or fail explicitly if needed
255+
return;
256+
}
257+
258+
ImFunction specializedCallee = specializedFunctions.get(callee, calleeGenerics);
259+
if (specializedCallee == null) {
260+
specializedCallee = specializeFunction(callee, calleeGenerics);
261+
}
262+
263+
fc.setFunc(specializedCallee);
264+
fc.getTypeArguments().removeAll();
265+
}
266+
});
267+
}
268+
226269
/**
227270
* creates a specialized version of this function
228271
*/
@@ -246,7 +289,13 @@ private ImFunction specializeFunction(ImFunction f, GenericTypes generics) {
246289

247290
newF.setName(f.getName() + "⟪" + generics.makeName() + "⟫");
248291
rewriteGenerics(newF, generics, typeVars);
292+
293+
// fix calls inside this specialized function so they also point to specialized callees
294+
fixCalleesInSpecializedFunction(newF, generics);
295+
296+
// Then collect further generic uses inside the now-specialized body
249297
collectGenericUsages(newF);
298+
250299
return newF;
251300
}
252301

de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/GenericsWithTypeclassesTests.java

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1614,5 +1614,234 @@ public void inheritedField_lazyClosure_uses_enclosing_receiver() {
16141614
);
16151615
}
16161616

1617+
@Test
1618+
public void arrayListInClosure() {
1619+
testAssertOkLines(true,
1620+
"package Hello",
1621+
"import NoWurst",
1622+
"",
1623+
"native testSuccess()",
1624+
"",
1625+
"public class ArrayList<T:>",
1626+
" private static T array store",
1627+
" private static int nextFreeIndex = 0",
1628+
"",
1629+
" private static constant int MAX_FREE_SECTIONS = 256",
1630+
" private static int array freeSectionStart",
1631+
" private static int array freeSectionCapacity",
1632+
" private static int freeSectionCount = 0",
1633+
"",
1634+
" private int startIndex",
1635+
" private int capacity",
1636+
" private int size = 0",
1637+
" private static constant int INITIAL_CAPACITY = 16",
1638+
"",
1639+
" static function getNextFreeIndex() returns int",
1640+
" return nextFreeIndex",
1641+
"",
1642+
" construct()",
1643+
" allocateStorage(INITIAL_CAPACITY)",
1644+
"",
1645+
" construct(int initialCapacity)",
1646+
" allocateStorage(initialCapacity)",
1647+
"",
1648+
" private function allocateStorage(int cap)",
1649+
" for i = 0 to freeSectionCount - 1",
1650+
" if freeSectionCapacity[i] >= cap",
1651+
" startIndex = freeSectionStart[i]",
1652+
" capacity = freeSectionCapacity[i]",
1653+
"",
1654+
" for j = i to freeSectionCount - 2",
1655+
" freeSectionStart[j] = freeSectionStart[j + 1]",
1656+
" freeSectionCapacity[j] = freeSectionCapacity[j + 1]",
1657+
" freeSectionCount--",
1658+
" return",
1659+
"",
1660+
" if nextFreeIndex + cap > 10000",
1661+
" compactFreeList()",
1662+
"",
1663+
" if nextFreeIndex + cap > 10000",
1664+
" nextFreeIndex = 0",
1665+
"",
1666+
" startIndex = nextFreeIndex",
1667+
" capacity = cap",
1668+
" nextFreeIndex += cap",
1669+
"",
1670+
" private static function compactFreeList()",
1671+
" if freeSectionCount <= 1",
1672+
" return",
1673+
"",
1674+
" for i = 1 to freeSectionCount - 1",
1675+
" let keyStart = freeSectionStart[i]",
1676+
" let keyCap = freeSectionCapacity[i]",
1677+
" var j = i - 1",
1678+
"",
1679+
" while j >= 0 and freeSectionStart[j] > keyStart",
1680+
" freeSectionStart[j + 1] = freeSectionStart[j]",
1681+
" freeSectionCapacity[j + 1] = freeSectionCapacity[j]",
1682+
" j--",
1683+
"",
1684+
" freeSectionStart[j + 1] = keyStart",
1685+
" freeSectionCapacity[j + 1] = keyCap",
1686+
"",
1687+
" var writeIdx = 0",
1688+
" for readIdx = 0 to freeSectionCount - 1",
1689+
" if writeIdx > 0 and freeSectionStart[writeIdx - 1] + freeSectionCapacity[writeIdx - 1] == freeSectionStart[readIdx]",
1690+
" freeSectionCapacity[writeIdx - 1] += freeSectionCapacity[readIdx]",
1691+
" else",
1692+
" if writeIdx != readIdx",
1693+
" freeSectionStart[writeIdx] = freeSectionStart[readIdx]",
1694+
" freeSectionCapacity[writeIdx] = freeSectionCapacity[readIdx]",
1695+
" writeIdx++",
1696+
"",
1697+
" freeSectionCount = writeIdx",
1698+
"",
1699+
" if freeSectionCount > 0",
1700+
" let lastIdx = freeSectionCount - 1",
1701+
" if freeSectionStart[lastIdx] + freeSectionCapacity[lastIdx] == nextFreeIndex",
1702+
" nextFreeIndex = freeSectionStart[lastIdx]",
1703+
" freeSectionCount--",
1704+
"",
1705+
" private function freeStorage()",
1706+
" if capacity <= 0",
1707+
" return",
1708+
"",
1709+
" if freeSectionCount < MAX_FREE_SECTIONS",
1710+
" freeSectionStart[freeSectionCount] = startIndex",
1711+
" freeSectionCapacity[freeSectionCount] = capacity",
1712+
" freeSectionCount++",
1713+
"",
1714+
" if startIndex + capacity == nextFreeIndex",
1715+
" nextFreeIndex = startIndex",
1716+
" freeSectionCount--",
1717+
" else",
1718+
" compactFreeList()",
1719+
"",
1720+
" if freeSectionCount < MAX_FREE_SECTIONS",
1721+
" freeSectionStart[freeSectionCount] = startIndex",
1722+
" freeSectionCapacity[freeSectionCount] = capacity",
1723+
" freeSectionCount++",
1724+
"",
1725+
" private function grow()",
1726+
" let newCapacity = capacity * 2",
1727+
" let oldStart = startIndex",
1728+
" let oldCapacity = capacity",
1729+
"",
1730+
" allocateStorage(newCapacity)",
1731+
"",
1732+
" for i = 0 to size - 1",
1733+
" store[startIndex + i] = store[oldStart + i]",
1734+
"",
1735+
" let tempStart = startIndex",
1736+
" let tempCap = capacity",
1737+
" startIndex = oldStart",
1738+
" capacity = oldCapacity",
1739+
" freeStorage()",
1740+
" startIndex = tempStart",
1741+
" capacity = tempCap",
1742+
"",
1743+
" ondestroy",
1744+
" for i = 0 to size - 1",
1745+
" store[startIndex + i] = null",
1746+
"",
1747+
" // Return storage to free pool",
1748+
" freeStorage()",
1749+
"",
1750+
" /** Adds one or more elements to the end of the list (amortized O(1)) */",
1751+
" function add(vararg T elems)",
1752+
" for elem in elems",
1753+
" if size >= capacity",
1754+
" grow()",
1755+
" store[startIndex + size] = elem",
1756+
" size++",
1757+
"",
1758+
" /** Returns the element at the specified index (O(1)) */",
1759+
" function get(int index) returns T",
1760+
" if index < 0 or index >= size",
1761+
" return store[startIndex + index]",
1762+
"",
1763+
" /** Removes the element at the given index and returns it (O(n) - shifts elements) */",
1764+
" function removeAt(int index) returns T",
1765+
" if index < 0 or index >= size",
1766+
"",
1767+
" let elem = store[startIndex + index]",
1768+
"",
1769+
" // Shift elements left",
1770+
" for i = index to size - 2",
1771+
" store[startIndex + i] = store[startIndex + i + 1]",
1772+
"",
1773+
" size--",
1774+
" return elem",
1775+
"",
1776+
" /** Returns the size of the list (O(1)) */",
1777+
" function size() returns int",
1778+
" return size",
1779+
"",
1780+
" /** Checks whether this list is empty (O(1)) */",
1781+
" function isEmpty() returns boolean",
1782+
" return size == 0",
1783+
"",
1784+
"",
1785+
"public function lazy<T:>(Lazy<T> l) returns Lazy<T>",
1786+
" return l",
1787+
"",
1788+
"public abstract class Lazy<T:>",
1789+
" T val = null",
1790+
" var wasRetrieved = false",
1791+
"",
1792+
" abstract function retrieve() returns T",
1793+
"",
1794+
" function get() returns T",
1795+
" if not wasRetrieved",
1796+
" val = retrieve()",
1797+
" wasRetrieved = true",
1798+
" return val",
1799+
"",
1800+
"public class CFBuilding",
1801+
" CFBuilding precursor = null",
1802+
" ArrayList<CFBuilding> upgrades = null",
1803+
"",
1804+
" Lazy<boolean> hasAAUpgrade = lazy<boolean>(() -> begin",
1805+
" var result = false",
1806+
" if upgrades != null",
1807+
" result = true",
1808+
" // perform iterative search through \"tree\"",
1809+
" var toCheck = new ArrayList<CFBuilding>()",
1810+
" // toCheck.addAll(upgrades)",
1811+
" while not toCheck.isEmpty()",
1812+
" let b = toCheck.removeAt(0)",
1813+
" if b.isAntiAir",
1814+
" result = true",
1815+
" break",
1816+
" if b.upgrades != null",
1817+
" // toCheck.addAll(b.upgrades)",
1818+
"",
1819+
" destroy toCheck",
1820+
" return result",
1821+
" end)",
1822+
"",
1823+
" var netWorthDiv100 = lazy<real>(() -> begin",
1824+
" var worth = 0",
1825+
" var cur = this",
1826+
" while cur != null",
1827+
" worth += cur.goldCost",
1828+
" cur = cur.precursor",
1829+
" return worth / 200.",
1830+
" end)",
1831+
"",
1832+
" var goldCost = 0",
1833+
" var isAntiAir = false // Is the building an anti air unit",
1834+
"",
1835+
"init",
1836+
" let b = new CFBuilding()",
1837+
" b.upgrades = new ArrayList<CFBuilding>()",
1838+
" let aa = b",
1839+
" .hasAAUpgrade.get()",
1840+
" if aa == true",
1841+
" testSuccess()",
1842+
""
1843+
);
1844+
}
1845+
16171846

16181847
}

0 commit comments

Comments
 (0)