Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

named tuple enum variant inference, named tuple parameter info #239

Merged
merged 1 commit into from
Nov 11, 2024
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
28 changes: 19 additions & 9 deletions src/main/kotlin/org/move/ide/hints/CallInfo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ class CallInfo(

companion object {
fun resolve(callExpr: MvCallExpr): CallInfo? {
val fn = callExpr.path.reference?.resolveFollowingAliases() as? MvFunction ?: return null
val msl = callExpr.isMsl()
val callTy = callExpr.inference(msl)?.getCallableType(callExpr) as? TyFunction ?: return null
return buildFunctionParameters(fn, callTy)
val item = callExpr.path.reference?.resolveFollowingAliases() ?: return null
return buildFunctionParameters(item, callTy)
}

fun resolve(assertMacroExpr: MvAssertMacroExpr): CallInfo {
Expand All @@ -43,14 +43,24 @@ class CallInfo(
return buildFunctionParameters(fn, callTy)
}

private fun buildFunctionParameters(function: MvFunction, ty: TyFunction): CallInfo {
val tys = ty.paramTypes.drop(if (function.isMethod) 1 else 0)
val params = function.parameters
.drop(if (function.isMethod) 1 else 0).map { it.name to it.type }
val self = function.selfParam?.let {
ty.paramTypes.firstOrNull()?.let { "self: ${tyToString(it)}" } ?: "_"
private fun buildFunctionParameters(item: MvElement, ty: TyFunction): CallInfo? {
return when (item) {
is MvFunction -> {
val tys = ty.paramTypes.drop(if (item.isMethod) 1 else 0)
val params = item.parameters
.drop(if (item.isMethod) 1 else 0).map { it.name to it.type }
val self = item.selfParam?.let {
ty.paramTypes.firstOrNull()?.let { "self: ${tyToString(it)}" } ?: "_"
}
CallInfo(self, buildParameters(tys, params))
}
is MvStruct, is MvEnumVariant -> {
// tuple struct
val tys = ty.paramTypes
CallInfo(null, tys.map { Parameter(null, null, it) })
}
else -> null
}
return CallInfo(self, buildParameters(tys, params))
}

private fun buildParameters(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,6 @@ class CompositeParameterInfoHandler: ParameterInfoHandler<PsiElement, ParameterI
context.offset,
MvElementTypes.COMMA
)
// if (listElement.valueArgumentList.isEmpty()) {
// 0
// } else {
// }
}
context.setCurrentParameter(currentParameterIndex)
}
Expand Down
17 changes: 11 additions & 6 deletions src/main/kotlin/org/move/lang/core/types/infer/TyLowering.kt
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,11 @@ class TyLowering {
val explicitTypeParams = explicitTypeParamsSubst(methodOrPath, namedItem, msl)
baseTy.substitute(explicitTypeParams)
}
is MvEnumVariant -> lowerPath(methodOrPath, namedItem.enumItem, msl)
is MvEnumVariant -> {
// has to be MvPath of form `ENUM_NAME::ENUM_VARIANT_NAME`
val enumPath = (methodOrPath as? MvPath)?.qualifier ?: return TyUnknown
lowerPath(enumPath, namedItem.enumItem, msl)
}
else -> debugErrorOrFallback(
"${namedItem.elementType} path cannot be inferred into type",
TyUnknown
Expand All @@ -84,16 +88,17 @@ class TyLowering {

fun lowerCallable(
methodOrPath: MvMethodOrPath,
item: MvGenericDeclaration,
namedItem: MvGenericDeclaration,
parameterTypes: List<Ty>,
returnType: Ty,
msl: Boolean
): TyFunction {
val typeParamsSubst = item.typeParamsToTypeParamsSubst
val acqTypes = (item as? MvFunctionLike)?.acquiresPathTypes.orEmpty().map { it.loweredType(msl) }
val baseTy = TyFunction(item, typeParamsSubst, parameterTypes, returnType, acqTypes)
val acqTypes = (namedItem as? MvFunctionLike)?.acquiresPathTypes.orEmpty().map { it.loweredType(msl) }
val typeParamsSubst = namedItem.typeParamsToTypeParamsSubst

val baseTy = TyFunction(namedItem, typeParamsSubst, parameterTypes, returnType, acqTypes)

val explicitTypeParams = explicitTypeParamsSubst(methodOrPath, item, msl)
val explicitTypeParams = explicitTypeParamsSubst(methodOrPath, namedItem, msl)
return baseTy.substitute(explicitTypeParams) as TyFunction
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ class TypeInferenceWalker(
TyUnknown
}
}
is MvEnumVariant -> item.enumItem.declaredType(ctx.msl)
is MvEnumVariant -> TyLowering().lowerPath(pathExpr.path, item, ctx.msl)
is MvModule -> TyUnknown
else -> debugErrorOrFallback(
"Referenced item ${item.elementType} " +
Expand Down Expand Up @@ -403,8 +403,7 @@ class TypeInferenceWalker(
is MvFieldsOwner -> {
val tupleFields = namedItem.tupleFields
if (tupleFields == null) return TyUnknown
val itemTy = instantiateTupleCallable(tupleFields, path, namedItem)
itemTy
instantiateTupleCallable(tupleFields, path, namedItem) ?: return TyUnknown
}
is MvPatBinding -> {
ctx.getBindingType(namedItem) as? TyLambda
Expand Down Expand Up @@ -594,34 +593,24 @@ class TypeInferenceWalker(

private fun instantiateTupleCallable(
tupleFields: MvTupleFields,
methodOrPath: MvMethodOrPath,
path: MvPath,
item: MvFieldsOwner
): TyFunction {
val genericItem = when (item) {
is MvStruct -> item
is MvEnumVariant -> item.enumItem
): TyFunction? {
val (genericItem, genericPath) = when (item) {
is MvStruct -> item to path
is MvEnumVariant -> {
val qualifierPath = path.qualifier ?: return null
item.enumItem to qualifierPath
}
else -> error("exhaustive")
}
//
val parameterTypes = tupleFields.tupleFieldDeclList.map { it.type.loweredType(msl) }
val returnType = genericItem.declaredType(msl)
val baseFuncTy =
TyLowering().lowerCallable(genericPath, genericItem, parameterTypes, returnType, msl)

val funcTy = TyLowering().lowerCallable(methodOrPath, genericItem, parameterTypes, returnType, msl)
//
// val baseTy = TyFunction(
// genericItem,
// genericItem.typeParamsToTypeParamsSubst,
// paramTypes = parameterTypes,
// returnType = returnType,
// acquiresTypes = emptyList(),
// )
//
// val explicitTypeParamsSubst = TyLowering().explicitTypeParamsSubst(methodOrPath, genericItem, msl)
// val funcTy = baseTy.substitute(explicitTypeParamsSubst)

// val baseTy = TyLowering().lowerTupleCallable(methodOrPath, item, tupleFields, msl)
val tyVarsSubst = genericItem.tyVarsSubst
return funcTy.substitute(tyVarsSubst) as TyFunction
return baseFuncTy.substitute(tyVarsSubst) as TyFunction
}

fun inferMacroCallExprTy(macroExpr: MvAssertMacroExpr): Ty {
Expand Down
6 changes: 3 additions & 3 deletions src/main/kotlin/org/move/lang/core/types/ty/TyFunction.kt
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,19 @@ data class TyFunction(
}
}

// do not account for explicit type arguments
// does not account for explicit type arguments
fun MvFunctionLike.functionTy(msl: Boolean): TyFunction = rawFunctionTy(this, msl)

private fun rawFunctionTy(item: MvFunctionLike, msl: Boolean): TyFunction {
val typeParamsSubst = item.typeParamsToTypeParamsSubst
val paramTypes = item.parameters.map { it.type?.loweredType(msl) ?: TyUnknown }
val acquiredTypes = item.acquiresPathTypes.map { it.loweredType(msl) }
val acqTypes = item.acquiresPathTypes.map { it.loweredType(msl) }
val retType = item.returnTypeTy(msl)
return TyFunction(
item,
typeParamsSubst,
paramTypes,
retType,
acquiredTypes,
acqTypes,
)
}
40 changes: 40 additions & 0 deletions src/test/kotlin/org/move/ide/hints/ParameterInfoHandlerTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -205,4 +205,44 @@ class ParameterInfoHandlerTest:
}
""", "_: bool, err: u64", 0
)

@MoveV2
fun `test parameter info for named tuple struct`() = checkByText("""
module 0x1::m {
struct S(u8, u16);
fun main() {
S(/*caret*/);
}
}
""", "u8, u16", 0)

@MoveV2
fun `test parameter info for generic named tuple struct`() = checkByText("""
module 0x1::m {
struct S<T>(T, T);
fun main() {
S<u8>(/*caret*/);
}
}
""", "u8, u8", 0)

@MoveV2
fun `test parameter info for named tuple enum variant`() = checkByText("""
module 0x1::m {
enum S { One(u8, u8) }
fun main() {
S::One(/*caret*/);
}
}
""", "u8, u8", 0)

@MoveV2
fun `test parameter info for generic named tuple enum variant`() = checkByText("""
module 0x1::m {
enum S<T> { One(T, T) }
fun main() {
S<u8>::One(/*caret*/);
}
}
""", "u8, u8", 0)
}
12 changes: 12 additions & 0 deletions src/test/kotlin/org/move/lang/types/CallableTypeTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.move.lang.types

import org.intellij.lang.annotations.Language
import org.move.ide.presentation.text
import org.move.lang.core.psi.MvCallExpr
import org.move.lang.core.psi.MvMethodCall
import org.move.lang.core.psi.ext.MvCallable
import org.move.lang.core.psi.ext.isMsl
Expand Down Expand Up @@ -54,6 +55,17 @@ class CallableTypeTest: TypificationTestCase() {
}
""")

fun `test function with explicit integer parameter callable type`() = testFunctionType("""
module 0x1::m {
fun call<T>(t: T): T { t }
fun main() {
call<u8>(1);
//^ fn(u8) -> u8
}
}
""")

private fun testFunctionType(@Language("Move") code: String) = testCallableType<MvCallExpr>(code)
private fun testMethodType(@Language("Move") code: String) = testCallableType<MvMethodCall>(code)

private inline fun <reified T: MvCallable> testCallableType(@Language("Move") code: String) {
Expand Down
36 changes: 36 additions & 0 deletions src/test/kotlin/org/move/lang/types/ExpressionTypesTest.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.move.lang.types

import org.move.utils.tests.MoveV2
import org.move.utils.tests.NamedAddress
import org.move.utils.tests.types.TypificationTestCase

Expand Down Expand Up @@ -2185,4 +2186,39 @@ module 0x1::main {
}
}
""")

@MoveV2
fun `test generic enum variant`() = testExpr("""
module 0x1::m {
enum S<T> { One }
fun main() {
let a = S<u8>::One;
a;
//^ 0x1::m::S<u8>
}
}
""")

@MoveV2
fun `test generic enum variant with positional fields`() = testExpr("""
module 0x1::m {
enum S<T> { One(T, T) }
fun main() {
let a = S<u8>::One(1, 1);
a;
//^ 0x1::m::S<u8>
}
}
""")

fun `test function with explicit integer parameter return type`() = testExpr("""
module 0x1::m {
fun call<T>(t: T): T { t }
fun main() {
let a = call<u8>(1);
a;
//^ u8
}
}
""")
}