Add variance-aware type params and generic delegates
This commit is contained in:
1 parent
c9da0b256f
commit
c5bf4e5039
5 files changed
+131
-80
No files matched your search
@@ -23,9 +23,12 @@ sealed class CodeContext {
|
||||
val name: String,
|
||||
val implicitThisMembers: Boolean = false,
|
||||
val implicitThisTypeName: String? = null,
|
||||
val typeParams: Set<String> = emptySet()
|
||||
val typeParams: Set<String> = emptySet(),
|
||||
val typeParamDecls: List<TypeDecl.TypeParam> = emptyList()
|
||||
): CodeContext()
|
||||
class ClassBody(val name: String, val isExtern: Boolean = false): CodeContext() {
|
||||
var typeParams: Set<String> = emptySet()
|
||||
var typeParamDecls: List<TypeDecl.TypeParam> = emptyList()
|
||||
val pendingInitializations = mutableMapOf<String, Pos>()
|
||||
val declaredMembers = mutableSetOf<String>()
|
||||
val memberOverrides = mutableMapOf<String, Boolean>()
|
||||
|
||||
@@ -439,11 +439,56 @@ class Compiler(
|
||||
}
|
||||
|
||||
private fun currentTypeParams(): Set<String> {
|
||||
val result = mutableSetOf<String>()
|
||||
for (ctx in codeContexts.asReversed()) {
|
||||
val fn = ctx as? CodeContext.Function ?: continue
|
||||
if (fn.typeParams.isNotEmpty()) return fn.typeParams
|
||||
when (ctx) {
|
||||
is CodeContext.Function -> result.addAll(ctx.typeParams)
|
||||
is CodeContext.ClassBody -> result.addAll(ctx.typeParams)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
return emptySet()
|
||||
return result
|
||||
}
|
||||
|
||||
private fun parseTypeParamList(): List<TypeDecl.TypeParam> {
|
||||
if (cc.peekNextNonWhitespace().type != Token.Type.LT) return emptyList()
|
||||
val typeParams = mutableListOf<TypeDecl.TypeParam>()
|
||||
cc.nextNonWhitespace()
|
||||
while (true) {
|
||||
val varianceToken = cc.peekNextNonWhitespace()
|
||||
val variance = when (varianceToken.value) {
|
||||
"in" -> {
|
||||
cc.nextNonWhitespace()
|
||||
TypeDecl.Variance.In
|
||||
}
|
||||
"out" -> {
|
||||
cc.nextNonWhitespace()
|
||||
TypeDecl.Variance.Out
|
||||
}
|
||||
else -> TypeDecl.Variance.Invariant
|
||||
}
|
||||
val idTok = cc.requireToken(Token.Type.ID, "type parameter name expected")
|
||||
var bound: TypeDecl? = null
|
||||
var defaultType: TypeDecl? = null
|
||||
if (cc.skipTokenOfType(Token.Type.COLON, isOptional = true)) {
|
||||
bound = parseTypeExpressionWithMini().first
|
||||
}
|
||||
if (cc.skipTokenOfType(Token.Type.ASSIGN, isOptional = true)) {
|
||||
defaultType = parseTypeExpressionWithMini().first
|
||||
}
|
||||
typeParams.add(TypeDecl.TypeParam(idTok.value, variance, bound, defaultType))
|
||||
val sep = cc.nextNonWhitespace()
|
||||
when (sep.type) {
|
||||
Token.Type.COMMA -> continue
|
||||
Token.Type.GT -> break
|
||||
Token.Type.SHR -> {
|
||||
cc.pushPendingGT()
|
||||
break
|
||||
}
|
||||
else -> sep.raiseSyntax("expected ',' or '>' in type parameter list")
|
||||
}
|
||||
}
|
||||
return typeParams
|
||||
}
|
||||
|
||||
private fun lookupSlotLocation(name: String, includeModule: Boolean = true): SlotLocation? {
|
||||
@@ -3702,6 +3747,9 @@ class Compiler(
|
||||
resolutionSink?.declareSymbol(nameToken.value, SymbolKind.CLASS, isMutable = false, pos = nameToken.pos)
|
||||
return inCodeContext(CodeContext.ClassBody(nameToken.value, isExtern = isExtern)) {
|
||||
val classCtx = codeContexts.lastOrNull() as? CodeContext.ClassBody
|
||||
val typeParamDecls = parseTypeParamList()
|
||||
classCtx?.typeParamDecls = typeParamDecls
|
||||
classCtx?.typeParams = typeParamDecls.map { it.name }.toSet()
|
||||
val constructorArgsDeclaration =
|
||||
if (cc.skipTokenOfType(Token.Type.LPAREN, isOptional = true))
|
||||
parseArgsDeclaration(isClassDeclaration = true)
|
||||
@@ -3731,15 +3779,19 @@ class Compiler(
|
||||
val baseSpecs = mutableListOf<BaseSpec>()
|
||||
if (cc.skipTokenOfType(Token.Type.COLON, isOptional = true)) {
|
||||
do {
|
||||
val baseId = cc.requireToken(Token.Type.ID, "base class name expected")
|
||||
resolutionSink?.reference(baseId.value, baseId.pos)
|
||||
val (baseDecl, _) = parseSimpleTypeExpressionWithMini()
|
||||
val baseName = when (baseDecl) {
|
||||
is TypeDecl.Simple -> baseDecl.name
|
||||
is TypeDecl.Generic -> baseDecl.name
|
||||
else -> throw ScriptError(cc.currentPos(), "base class name expected")
|
||||
}
|
||||
var argsList: List<ParsedArgument>? = null
|
||||
// Optional constructor args of the base — parse and ignore for now (MVP), just to consume tokens
|
||||
if (cc.skipTokenOfType(Token.Type.LPAREN, isOptional = true)) {
|
||||
// Parse args without consuming any following block so that a class body can follow safely
|
||||
argsList = parseArgsNoTailBlock()
|
||||
}
|
||||
baseSpecs += BaseSpec(baseId.value, argsList)
|
||||
baseSpecs += BaseSpec(baseName, argsList)
|
||||
} while (cc.skipTokenOfType(Token.Type.COMMA, isOptional = true))
|
||||
}
|
||||
|
||||
@@ -4360,24 +4412,8 @@ class Compiler(
|
||||
declareLocalName(extensionWrapperName, isMutable = false)
|
||||
}
|
||||
|
||||
val typeParams = mutableSetOf<String>()
|
||||
if (cc.peekNextNonWhitespace().type == Token.Type.LT) {
|
||||
cc.nextNonWhitespace()
|
||||
while (true) {
|
||||
val idTok = cc.requireToken(Token.Type.ID, "type parameter name expected")
|
||||
typeParams.add(idTok.value)
|
||||
val sep = cc.nextNonWhitespace()
|
||||
when (sep.type) {
|
||||
Token.Type.COMMA -> continue
|
||||
Token.Type.GT -> break
|
||||
Token.Type.SHR -> {
|
||||
cc.pushPendingGT()
|
||||
break
|
||||
}
|
||||
else -> sep.raiseSyntax("expected ',' or '>' in type parameter list")
|
||||
}
|
||||
}
|
||||
}
|
||||
val typeParamDecls = parseTypeParamList()
|
||||
val typeParams = typeParamDecls.map { it.name }.toSet()
|
||||
|
||||
val argsDeclaration: ArgsDeclaration =
|
||||
if (cc.peekNextNonWhitespace().type == Token.Type.LPAREN) {
|
||||
@@ -4441,7 +4477,8 @@ class Compiler(
|
||||
name,
|
||||
implicitThisMembers = implicitThisMembers,
|
||||
implicitThisTypeName = extTypeName,
|
||||
typeParams = typeParams
|
||||
typeParams = typeParams,
|
||||
typeParamDecls = typeParamDecls
|
||||
)
|
||||
) {
|
||||
cc.labels.add(name)
|
||||
|
||||
@@ -22,6 +22,7 @@ package net.sergeych.lyng
|
||||
// this is highly experimental and subject to complete redesign
|
||||
// very soon
|
||||
sealed class TypeDecl(val isNullable:Boolean = false) {
|
||||
enum class Variance { In, Out, Invariant }
|
||||
// ??
|
||||
data class Function(
|
||||
val receiver: TypeDecl?,
|
||||
@@ -30,6 +31,12 @@ sealed class TypeDecl(val isNullable:Boolean = false) {
|
||||
val nullable: Boolean = false
|
||||
) : TypeDecl(nullable)
|
||||
data class TypeVar(val name: String, val nullable: Boolean = false) : TypeDecl(nullable)
|
||||
data class TypeParam(
|
||||
val name: String,
|
||||
val variance: Variance = Variance.Invariant,
|
||||
val bound: TypeDecl? = null,
|
||||
val defaultType: TypeDecl? = null
|
||||
)
|
||||
object TypeAny : TypeDecl()
|
||||
object TypeNullableAny : TypeDecl(true)
|
||||
|
||||
|
||||
Reference in new issue
Block a user