Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package tigerpython.utilities.types

/**
* The `BuiltinMethod` represents special methods such as `list.pop()` where we need to access the type information
* of the underlying receiver in order to get accurate types.
*/
class BuiltinMethod(var parent: PrimitiveType,
val name: String,
val params: Array[String],
val defaultReturnType: DataType) extends FunctionType {

override def getFullName: String = if (parent != null) parent.getFullName + "." + name else name

override def getParamsString: String = params.mkString(", ")

override def getReturnType: DataType =
if (parent != null)
parent.getMethodType(name)
else
defaultReturnType

// `parent` is bound once, when the field is registered on its declaring type (e.g. `<mutable-seq>`),
// so it never reflects the receiver's own type (e.g. `list[Actor]` rather than plain `list`). Every
// attribute access must therefore call `boundTo` with the actual receiver type so `getMethodType` sees it.
def boundTo(receiver: PrimitiveType): BuiltinMethod = {
val result = new BuiltinMethod(receiver, name, params, defaultReturnType)
result.docString = docString
result
}

override def toString: String = "%s(%s)".format(name, getParamsString)
}
object BuiltinMethod {

def apply(parent: PrimitiveType, name: String, params: Array[String], docString: String,
defaultReturnType: DataType = BuiltinTypes.ANY_TYPE): BuiltinMethod = {
val result = new BuiltinMethod(parent, name, params, defaultReturnType)
result.docString = docString
result
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,13 @@ object BuiltinTypes {

val MUTABLE_SEQ = PrimitiveType("<mutable-seq>", SEQ_TYPE)
MUTABLE_SEQ.addFields(
BuiltinFunction("append", Array("x"), MUTABLE_SEQ, null),
BuiltinFunction("extend", Array("x"), MUTABLE_SEQ, null),
BuiltinFunction("insert", Array(), MUTABLE_SEQ, null),
BuiltinFunction("pop", Array(), MUTABLE_SEQ, null),
BuiltinFunction("remove", Array("x"), MUTABLE_SEQ, null),
BuiltinFunction("reverse", Array(), MUTABLE_SEQ, "reverses the items of the sequence in place"),
BuiltinFunction("sort", Array(), MUTABLE_SEQ, "sort the items of the sequence in place")
BuiltinFunction("append", Array("x"), NONE_TYPE, null),
BuiltinFunction("extend", Array("x"), NONE_TYPE, null),
BuiltinFunction("insert", Array(), NONE_TYPE, null),
BuiltinMethod(null, "pop", Array(), null),
BuiltinFunction("remove", Array("x"), NONE_TYPE, null),
BuiltinFunction("reverse", Array(), NONE_TYPE, "reverses the items of the sequence in place"),
BuiltinFunction("sort", Array(), NONE_TYPE, "sort the items of the sequence in place")
)
val LIST_TYPE = PrimitiveType("list", MUTABLE_SEQ)
val TUPLE_TYPE = PrimitiveType("tuple", SEQ_TYPE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ class ListType(val itemType: DataType) extends
//override def getFields: Map[String, DataType] = super.fields

override def getItemType: DataType = itemType

protected[types]
override def getMethodType(methodName: String): DataType =
methodName match {
case "pop" =>
itemType
case _ =>
BuiltinTypes.ANY_TYPE
}
}
object ListType {
private val listTypes = collection.mutable.Map[DataType, ListType]()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ class PrimitiveType(val name: String,

def getInstanceFields: Map[String, DataType] = fields

/**
* This method is used by `BuiltinMethod` to retrieve contextual type information.
*/
protected[types]
def getMethodType(methodName: String): DataType = BuiltinTypes.ANY_TYPE

override def isCallable: Boolean = true

def isSubclassOf(base: DataType): Boolean =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,35 @@ class TypeAstWalker {
}

protected def getTypeOfAttr(attr: AstNode.Attribute): DataType = {
getType(attr.base).findField(attr.attr.name) match {
val baseType = getType(attr.base)
baseType.findField(attr.attr.name) match {
case Some(method: BuiltinMethod) =>
getPrimitiveType(baseType) match {
case Some(receiver) =>
method.boundTo(receiver)
case None =>
method
}
case Some(result) =>
validateDataType(result)
case None =>
ANY_TYPE
}
}

// A `BuiltinMethod` field is shared by every instance of its declaring type (e.g. all lists share
// the same "pop" field object), so it can't carry the receiver's own type (e.g. `list[Actor]` rather
// than plain `list`) - that has to be bound in at each attribute access instead, from here.
private def getPrimitiveType(dataType: DataType): Option[PrimitiveType] =
dataType match {
case instance: Instance =>
getPrimitiveType(instance.baseType)
case primitive: PrimitiveType =>
Some(primitive)
case _ =>
None
}

protected def getTypeOfBinaryOp(binOp: AstNode.BinaryOp): DataType = {
val left = getType(binOp.left)
val right = getType(binOp.right)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# pyi:issue1003.actors
# class Actor:
# def remove(self) -> None: ...
# def get_actors() -> list[Actor]: ...
# 50
#
from issue1003.actors import *
get_actors().pop().remove()
Loading