Compare commits

...

2 Commits

Author SHA1 Message Date
87060c7eb7 +inference bugs
+List.ensureCapacity
optimized List.fill
2026-04-20 13:56:47 +03:00
6ba128f7ba fixed some more inference bugs 2026-04-20 12:23:17 +03:00
5 changed files with 64 additions and 12 deletions

View File

@ -175,6 +175,8 @@ List could be sorted in place, just like [Collection] provide sorted copies, in
| `[Range]` | get slice of the array (copy) | Range | | `[Range]` | get slice of the array (copy) | Range |
| `+=` | append element(s) (2) | List or Obj | | `+=` | append element(s) (2) | List or Obj |
| `List.fill(size, block)` | build a new list from indices `0..<size` | Int, Callable | | `List.fill(size, block)` | build a new list from indices `0..<size` | Int, Callable |
| `List.fill(size,capacity,block)` | same, pre-allocating capacity slots | Int, Int, Callable |
| `ensureCapacity(count)` | pre-allocate storage for at least `count` elements without reallocation (5) | Int |
| `sort()` | in-place sort, natural order | void | | `sort()` | in-place sort, natural order | void |
| `sortBy(predicate)` | in-place sort bu `predicate` call result (3) | void | | `sortBy(predicate)` | in-place sort bu `predicate` call result (3) | void |
| `sortWith(comparator)` | in-place sort using `comarator` function (4) | void | | `sortWith(comparator)` | in-place sort using `comarator` function (4) | void |
@ -197,6 +199,11 @@ order, e.g. is same as `list.sortWith { a,b -> predicate(a) <=> predicate(b) }`
positive if first is greater, and zero if they are equal. For example, the equvalent comparator positive if first is greater, and zero if they are equal. For example, the equvalent comparator
for `sort()` will be `sort { a, b -> a <=> b } for `sort()` will be `sort { a, b -> a <=> b }
(5)
: if the current capacity is already ≥ `count`, this is a no-op. Otherwise the internal storage
is reallocated to hold at least `count` elements. Use this before a bulk `+=` loop to avoid
repeated reallocations. `List.fill(size, capacity, block)` calls this automatically.
It inherits from [Iterable] too and thus all iterable methods are applicable to any list. It inherits from [Iterable] too and thus all iterable methods are applicable to any list.
## Observable list hooks ## Observable list hooks

View File

@ -2096,12 +2096,19 @@ class Compiler(
plan.captureOwners[name] = slotLoc plan.captureOwners[name] = slotLoc
plan.captures += capture plan.captures += capture
if (!plan.slotPlan.slots.containsKey(name)) { if (!plan.slotPlan.slots.containsKey(name)) {
val newSlot = plan.slotPlan.nextIndex
plan.slotPlan.slots[name] = SlotEntry( plan.slotPlan.slots[name] = SlotEntry(
plan.slotPlan.nextIndex, newSlot,
isMutable = slotLoc.isMutable, isMutable = slotLoc.isMutable,
isDelegated = slotLoc.isDelegated isDelegated = slotLoc.isDelegated
) )
plan.slotPlan.nextIndex += 1 plan.slotPlan.nextIndex += 1
slotTypeByScopeId[slotLoc.scopeId]?.get(slotLoc.slot)?.let { cls ->
slotTypeByScopeId.getOrPut(plan.slotPlan.id) { mutableMapOf() }[newSlot] = cls
}
slotTypeDeclByScopeId[slotLoc.scopeId]?.get(slotLoc.slot)?.let { decl ->
slotTypeDeclByScopeId.getOrPut(plan.slotPlan.id) { mutableMapOf() }[newSlot] = decl
}
} }
} }
@ -3417,6 +3424,10 @@ class Compiler(
index < expectedParams.size index < expectedParams.size
) { ) {
expectedParams[index] expectedParams[index]
} else if (param.type == TypeDecl.TypeAny && index == 0 && argsDeclaration.params.size == 1 &&
implicitItType != null
) {
implicitItType
} else { } else {
param.type param.type
} }
@ -10346,6 +10357,9 @@ class Compiler(
if (initClass != null) { if (initClass != null) {
classFieldTypesByName.getOrPut(declaringClassNameCaptured) { mutableMapOf() }[name] = initClass classFieldTypesByName.getOrPut(declaringClassNameCaptured) { mutableMapOf() }[name] = initClass
} }
if (!isDelegate && varTypeDecl is TypeDecl.Generic) {
classMemberTypeDeclByName.getOrPut(declaringClassNameCaptured) { mutableMapOf() }[name] = varTypeDecl
}
} }
// Emit MiniValDecl for this declaration (before execution wiring), attach doc if any // Emit MiniValDecl for this declaration (before execution wiring), attach doc if any

View File

@ -528,6 +528,22 @@ open class ObjList(initialList: MutableList<Obj> = mutableListOf()) : Obj() {
} else self.list.removeLast() } else self.list.removeLast()
self self
} }
addFnDoc(
name= "ensureCapacity",
doc = """
ensure the list capacity allows storing specified amount if items without reallocation.
If current capacity is greater or equal to `count`, does nothing. Note that possible reallocation
could be a costly operation,
""".trimIndent(),
params = listOf(ParamDoc("count", type("lyng.Int"))),
moduleName = "lyng.stdlib"
) {
val self = thisAs<ObjList>()
val list = self.list as ArrayList
val count = requireOnlyArg<ObjInt>().value.toInt()
list.ensureCapacity(count)
self
}
addFnDoc( addFnDoc(
name = "removeRange", name = "removeRange",

View File

@ -436,4 +436,12 @@ class StdlibTest {
assertEquals(2, x) assertEquals(2, x)
""".trimIndent()) """.trimIndent())
} }
@Test
fun testListFill() = runTest {
eval("""
val x = List.fill(5) { i -> i * 10 }
assertEquals([0, 10, 20, 30, 40], x)
""".trimIndent())
}
} }

View File

@ -432,11 +432,19 @@ fun List<T>.sort(): void {
sortWith { a, b -> a <=> b } sortWith { a, b -> a <=> b }
} }
/* Build a new list of `size` elements by calling `block(index)` for each index. */ /*
static fun List<T>.fill(size: Int, block: (Int)->T): List<T> { Build a new list of `size` elements by calling `block(index)` for each index.
val result = List<T>() `capacity` less size is ignored (size will be used as capacity).
for( i in 0..<size ) result += block(i) */
result static fun List<T>.fill(size: Int, capacity = -1, block: (Int)->T): List<T> {
require(size >= 0, "size must not be negative")
val capacity = capacity < size ? size : capacity
List<T>().also {
if( size > 0 ) {
it.ensureCapacity(capacity)
for( i in 0..<size ) it += block(i)
}
}
} }
/* Print this exception and its stack trace to standard output. */ /* Print this exception and its stack trace to standard output. */
@ -545,8 +553,7 @@ class StackTraceEntry(
} }
// Private helper: starts one LaunchPool worker coroutine for the given queue. // Private helper: starts one LaunchPool worker coroutine for the given queue.
// Defined outside LaunchPool so the global `launch` is not shadowed by the method. // Defined outside LaunchPool so the global `launch` is not shadowed by the method.
private fun _launchPoolWorker(q) { private fun _launchPoolWorker(ch: Channel): Deferred {
val ch = q as Channel
launch { launch {
var task = ch.receive() var task = ch.receive()
while (task != null) { while (task != null) {
@ -588,7 +595,7 @@ class LaunchPool(maxWorkers, maxQueueSize = Channel.UNLIMITED) {
mu.withLock { mu.withLock {
if (isClosed) throw IllegalStateException("LaunchPool is closed") if (isClosed) throw IllegalStateException("LaunchPool is closed")
} }
val d = CompletableDeferred() val d = CompletableDeferred()
val wrapper = { val wrapper = {
try { d.complete(lambda()) } try { d.complete(lambda()) }
catch(e) { d.completeExceptionally(e) } catch(e) { d.completeExceptionally(e) }
@ -618,7 +625,7 @@ class LaunchPool(maxWorkers, maxQueueSize = Channel.UNLIMITED) {
*/ */
fun cancel() { fun cancel() {
closeQueue() closeQueue()
workers.forEach { (it as Deferred).cancel() } workers.forEach { it.cancel() }
} }
/* /*
@ -628,7 +635,7 @@ class LaunchPool(maxWorkers, maxQueueSize = Channel.UNLIMITED) {
*/ */
fun cancelAndJoin() { fun cancelAndJoin() {
closeQueue() closeQueue()
workers.forEach { (it as Deferred).cancel() } workers.forEach { it.cancel() }
} }
/* /*
@ -639,7 +646,7 @@ class LaunchPool(maxWorkers, maxQueueSize = Channel.UNLIMITED) {
fun closeAndJoin() { fun closeAndJoin() {
closeQueue() closeQueue()
for (w in workers) { for (w in workers) {
(w as Deferred).await() w.await()
} }
} }
} }