SuggestionBuilder needs to send ascribedType of constructor parameters (#6655)

close #6611

Changelog:
- update: run compiler passes on the `ascribedType` field of the constructor arguments
- update: suggestion builder uses the type information attached to `ascribedType`
- feat: resolve qualified names in type signatures
This commit is contained in:
Dmitry Bushev 2023-05-13 19:33:03 +01:00 committed by GitHub
parent 00110b8dec
commit 706791779b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 350 additions and 44 deletions

View File

@ -1,9 +1,11 @@
import project.Any.Any
import project.Data.Text.Text
import project.Error.Error
import project.Meta
import project.Nothing.Nothing
import project.Panic.Panic
import project.System.File.File
import project.System.File_Format.File_Format
polyglot java import java.io.IOException
polyglot java import java.nio.file.AccessDeniedException

View File

@ -132,7 +132,7 @@ type Value_Type
Fallback provided to allow describing types that are not supported by
Enso at this time.
Unsupported_Data_Type type_name:(Text|Nothing)=Nothing (underlying_type:SQL_Type|Nothing=Nothing)
Unsupported_Data_Type type_name:(Text|Nothing)=Nothing (underlying_type:Any|Nothing=Nothing)
## A mix of values can be stored in the Column.

View File

@ -259,7 +259,7 @@ type Invalid_Location
format.
type Invalid_Format
## PRIVATE
Error column:(Text|Nothing) (value_type:Value_Type|Integer|Number|Date|Time|Time_Of_Day|Boolean) (cells:[Text])
Error column:(Text|Nothing) (value_type:Value_Type|Integer|Number|Date|Date_Time|Time_Of_Day|Boolean) (cells:[Text])
## PRIVATE
Pretty print the invalid format error.

View File

@ -30,7 +30,7 @@ prepare_reader_table on_problems result_with_problems =
## PRIVATE
Convert Boolean|Infer to the correct HeaderBehavior
make_java_headers : (Boolean | Infer) -> ExcelHeaders.HeaderBehavior
make_java_headers : (Boolean | Infer) -> Any
make_java_headers headers = case headers of
True -> ExcelHeaders.HeaderBehavior.USE_FIRST_ROW_AS_HEADERS
Infer -> ExcelHeaders.HeaderBehavior.INFER

View File

@ -143,7 +143,7 @@ type Test
## PRIVATE
Reports an unexpected dataflow error has occurred.
fail_match_on_unexpected_error : Error.Error -> Integer -> Nothing
fail_match_on_unexpected_error : Error -> Integer -> Nothing
fail_match_on_unexpected_error error frames_to_skip =
payload = error.catch
loc = Meta.get_source_location 1+frames_to_skip

View File

@ -1,15 +1,16 @@
from Standard.Base import all
import Standard.Database.Data.SQL_Type.SQL_Type
import Standard.Database.Data.Table.Table
import project.Helpers
## PRIVATE
Prepares the query for visualization.
Prepares the database table for visualization.
Arguments:
- x: The query to prepare for visualisation.
- x: The database table to prepare for visualisation.
For each interpolation it provides its value, its actual type name, its
expected SQL type name and if it was possible to infer it, its expected Enso
@ -17,7 +18,7 @@ import project.Helpers
Expected Enso types are inferred based on known SQL types and their mapping
to Enso types.
prepare_visualization : Table.IR.Query -> Text
prepare_visualization : Table -> Text
prepare_visualization x =
prepared = x.to_sql.prepare
code = prepared.first

View File

@ -23,6 +23,7 @@ import scala.collection.mutable
*
* @param source the text source
* @param typeGraph the type hierarchy
* @param compiler the compiler instance
* @tparam A the type of the text source
*/
final class SuggestionBuilder[A: IndexedSource](
@ -91,8 +92,7 @@ final class SuggestionBuilder[A: IndexedSource](
}
val getters = members
.flatMap(_.arguments)
.map(_.name.name)
.distinct
.distinctBy(_.name)
.map(buildGetter(module, tpName.name, _))
val tpSuggestions = tpe +: conses ++: getters
@ -360,8 +360,9 @@ final class SuggestionBuilder[A: IndexedSource](
private def buildGetter(
module: QualifiedName,
typeName: String,
getterName: String
argument: IR.DefinitionArgument
): Suggestion = {
val getterName = argument.name.name
val thisArg = IR.DefinitionArgument.Specified(
name = IR.Name.Self(None),
ascribedType = None,
@ -377,7 +378,7 @@ final class SuggestionBuilder[A: IndexedSource](
isStatic = false,
args = Seq(thisArg),
doc = None,
typeSignature = None
typeSignature = argument.name.getMetadata(TypeSignatures)
)
}
@ -636,14 +637,21 @@ final class SuggestionBuilder[A: IndexedSource](
* @param arg the value argument
* @return the suggestion argument
*/
private def buildArgument(arg: IR.DefinitionArgument): Suggestion.Argument =
private def buildArgument(arg: IR.DefinitionArgument): Suggestion.Argument = {
val signatureOpt = arg.name.getMetadata(TypeSignatures).map { meta =>
buildTypeSignature(meta.signature) match {
case Vector(targ) => buildTypeArgumentName(targ)
case _ => Any
}
}
Suggestion.Argument(
name = arg.name.name,
reprType = Any,
reprType = signatureOpt.getOrElse(Any),
isSuspended = arg.suspended,
hasDefault = arg.defaultValue.isDefined,
defaultValue = arg.defaultValue.flatMap(buildDefaultValue)
)
}
/** Build return type from the type definition.
*
@ -688,6 +696,7 @@ object SuggestionBuilder {
/** Creates the suggestion builder for a module.
*
* @param module the module to index
* @param compiler the compiler instance
* @return the suggestions builder for the module
*/
def apply(
@ -700,6 +709,7 @@ object SuggestionBuilder {
*
* @param source the text source
* @param typeGraph the type hierarchy
* @param compiler the compiler instance
* @tparam A the type of the text source
*/
def apply[A: IndexedSource](
@ -712,6 +722,7 @@ object SuggestionBuilder {
/** Create the suggestion builder.
*
* @param source the text source
* @param compiler the compiler instance
* @tparam A the type of the text source
*/
def apply[A: IndexedSource](

View File

@ -300,7 +300,7 @@ object IR {
* @param diagnostics compiler diagnostics for this node
*/
@SerialVersionUID(
3668L // removes special handling for `enso_project` method
6655L // SuggestionBuilder needs to send ascribedType of constructor parameters
) // prevents reading broken caches, see PR-3692 for details
sealed case class Module(
imports: List[Module.Scope.Import],
@ -3040,7 +3040,6 @@ object IR {
/** A representation of the name `Self`, used to refer to the current type.
*
* @param location the source location that the node corresponds to
* @param synthetic synthetic determines if this `self` was generated by the compiler
* @param passData the pass metadata associated with this node
* @param diagnostics compiler diagnostics for this node
*/
@ -4723,6 +4722,7 @@ object IR {
def mapExpressions(fn: Expression => Expression): Specified = {
copy(
name = name.mapExpressions(fn),
ascribedType = ascribedType.map(fn),
defaultValue = defaultValue.map(fn)
)
}

View File

@ -324,7 +324,7 @@ case object AliasAnalysis extends IRPass {
* instead of creating a new scope
* @return `expression`, potentially with aliasing information attached
*/
def analyseExpression(
private def analyseExpression(
expression: IR.Expression,
graph: Graph,
parentScope: Scope,
@ -467,7 +467,7 @@ case object AliasAnalysis extends IRPass {
* defined
* @return `args`, potentially
*/
def analyseArgumentDefs(
private def analyseArgumentDefs(
args: List[IR.DefinitionArgument],
graph: Graph,
scope: Scope
@ -492,9 +492,13 @@ case object AliasAnalysis extends IRPass {
arg.getExternalId
)
scope.addDefinition(definition)
arg.updateMetadata(
this -->> Info.Occurrence(graph, occurrenceId)
)
arg
.updateMetadata(this -->> Info.Occurrence(graph, occurrenceId))
.copy(
ascribedType =
arg.ascribedType.map(analyseExpression(_, graph, scope))
)
case arg @ IR.DefinitionArgument.Specified(
name,
_,
@ -523,7 +527,11 @@ case object AliasAnalysis extends IRPass {
scope.addDefinition(definition)
arg
.copy(defaultValue = newDefault)
.copy(
defaultValue = newDefault,
ascribedType =
arg.ascribedType.map(analyseExpression(_, graph, scope))
)
.updateMetadata(this -->> Info.Occurrence(graph, occurrenceId))
} else {
throw new CompilerError(

View File

@ -10,7 +10,7 @@ import org.enso.compiler.pass.analyse.BindingAnalysis
import scala.annotation.unused
/** Resolves and desugars referent name occurences in type positions.
/** Resolves and desugars referent name occurrences in type positions.
*/
case object TypeNames extends IRPass {
@ -77,24 +77,34 @@ case object TypeNames extends IRPass {
expression.transformExpressions {
case expr if SuspendedArguments.representsSuspended(expr) => expr
case n: IR.Name.Literal =>
bindingsMap
.resolveName(n.name)
.map(res => n.updateMetadata(this -->> Resolution(res)))
.fold(
error =>
IR.Error.Resolution(n, IR.Error.Resolution.ResolverError(error)),
n =>
n.getMetadata(this).get.target match {
case _: ResolvedModule =>
IR.Error.Resolution(
n,
IR.Error.Resolution.UnexpectedModule("type signature")
)
case _ => n
}
)
processResolvedName(n, bindingsMap.resolveName(n.name))
case n: IR.Name.Qualified =>
processResolvedName(
n,
bindingsMap.resolveQualifiedName(n.parts.map(_.name))
)
}
private def processResolvedName(
name: IR.Name,
resolvedName: Either[BindingsMap.ResolutionError, BindingsMap.ResolvedName]
): IR.Name =
resolvedName
.map(res => name.updateMetadata(this -->> Resolution(res)))
.fold(
error =>
IR.Error.Resolution(name, IR.Error.Resolution.ResolverError(error)),
n =>
n.getMetadata(this).get.target match {
case _: ResolvedModule =>
IR.Error.Resolution(
n,
IR.Error.Resolution.UnexpectedModule("type signature")
)
case _ => n
}
)
/** Executes the pass on the provided `ir`, and returns a possibly transformed
* or annotated version of `ir` in an inline context.
*

View File

@ -130,7 +130,14 @@ case object TypeSignatures extends IRPass {
lastSignature = None
res
case ut: IR.Module.Scope.Definition.Type =>
Some(ut.mapExpressions(resolveExpression))
Some(
ut
.copy(
params = ut.params.map(resolveArgument),
members = ut.members.map(resolveDefinitionData)
)
.mapExpressions(resolveExpression)
)
case err: IR.Error => Some(err)
case ann: IR.Name.GenericAnnotation => Some(ann)
case _: IR.Module.Scope.Definition.SugaredType =>
@ -169,6 +176,36 @@ case object TypeSignatures extends IRPass {
}
}
private def resolveDefinitionData(
data: IR.Module.Scope.Definition.Data
): IR.Module.Scope.Definition.Data = {
data.copy(
arguments = data.arguments.map(resolveArgument)
)
}
private def resolveArgument(
argument: IR.DefinitionArgument
): IR.DefinitionArgument =
argument match {
case specified @ IR.DefinitionArgument.Specified(
_,
Some(ascribedType),
_,
_,
_,
_,
_
) =>
val sig = resolveExpression(ascribedType.duplicate())
specified.copy(
name = specified.name.updateMetadata(this -->> Signature(sig)),
ascribedType =
Some(ascribedType.updateMetadata(this -->> Signature(sig)))
)
case argument => argument
}
/** Resolves type signatures in an ascription.
*
* @param sig the signature to convert

View File

@ -183,7 +183,9 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
"build method with an argument" in {
val code =
"""
"""import Standard.Base.Data.Text.Text
|import Standard.Base.Data.Numbers.Number
|
|foo : Text -> Number
|foo a = 42""".stripMargin
val module = code.preprocessModule
@ -198,10 +200,16 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
name = "foo",
arguments = Seq(
Suggestion.Argument("self", "Unnamed.Test", false, false, None),
Suggestion.Argument("a", "Text", false, false, None)
Suggestion.Argument(
"a",
"Standard.Base.Data.Text.Text",
false,
false,
None
)
),
selfType = "Unnamed.Test",
returnType = "Number",
returnType = "Standard.Base.Data.Numbers.Number",
isStatic = true,
documentation = None
),
@ -2005,6 +2013,234 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
)
}
"build type with ascribed constructor" in {
val code =
"""type X
|
|type T
| A (x : X)
|""".stripMargin
val module = code.preprocessModule
build(code, module) shouldEqual Tree.Root(
Vector(
ModuleNode,
Tree.Node(
Suggestion.Type(
externalId = None,
module = "Unnamed.Test",
name = "X",
params = Seq(),
returnType = "Unnamed.Test.X",
parentType = Some(SuggestionBuilder.Any),
documentation = None
),
Vector()
),
Tree.Node(
Suggestion.Type(
externalId = None,
module = "Unnamed.Test",
name = "T",
params = Seq(),
returnType = "Unnamed.Test.T",
parentType = Some(SuggestionBuilder.Any),
documentation = None
),
Vector()
),
Tree.Node(
Suggestion.Constructor(
externalId = None,
module = "Unnamed.Test",
name = "A",
arguments = Seq(
Suggestion
.Argument("x", "Unnamed.Test.X", false, false, None)
),
returnType = "Unnamed.Test.T",
documentation = None
),
Vector()
),
Tree.Node(
Suggestion.Method(
externalId = None,
module = "Unnamed.Test",
name = "x",
arguments = Seq(
Suggestion
.Argument("self", "Unnamed.Test.T", false, false, None)
),
selfType = "Unnamed.Test.T",
returnType = "Unnamed.Test.X",
isStatic = false,
documentation = None
),
Vector()
)
)
)
}
"build type with qualified ascribed constructor" in {
val code =
"""import Standard.Base.Data.Numbers
|
|type T
| A (x : Numbers.Number)
|""".stripMargin
val module = code.preprocessModule
build(code, module) shouldEqual Tree.Root(
Vector(
ModuleNode,
Tree.Node(
Suggestion.Type(
externalId = None,
module = "Unnamed.Test",
name = "T",
params = Seq(),
returnType = "Unnamed.Test.T",
parentType = Some(SuggestionBuilder.Any),
documentation = None
),
Vector()
),
Tree.Node(
Suggestion.Constructor(
externalId = None,
module = "Unnamed.Test",
name = "A",
arguments = Seq(
Suggestion
.Argument(
"x",
"Standard.Base.Data.Numbers.Number",
false,
false,
None
)
),
returnType = "Unnamed.Test.T",
documentation = None
),
Vector()
),
Tree.Node(
Suggestion.Method(
externalId = None,
module = "Unnamed.Test",
name = "x",
arguments = Seq(
Suggestion
.Argument("self", "Unnamed.Test.T", false, false, None)
),
selfType = "Unnamed.Test.T",
returnType = "Standard.Base.Data.Numbers.Number",
isStatic = false,
documentation = None
),
Vector()
)
)
)
}
"build type with ascribed type parameter in constructor" in {
val code =
"""
|type E a b
| L (x : a)
| R (y : b)
|""".stripMargin
val module = code.preprocessModule
build(code, module) shouldEqual Tree.Root(
Vector(
ModuleNode,
Tree.Node(
Suggestion.Type(
externalId = None,
module = "Unnamed.Test",
name = "E",
params = Seq(
Suggestion
.Argument("a", SuggestionBuilder.Any, false, false, None),
Suggestion
.Argument("b", SuggestionBuilder.Any, false, false, None)
),
returnType = "Unnamed.Test.E",
parentType = Some(SuggestionBuilder.Any),
documentation = None
),
Vector()
),
Tree.Node(
Suggestion.Constructor(
externalId = None,
module = "Unnamed.Test",
name = "L",
arguments = Seq(
Suggestion.Argument("x", "a", false, false, None)
),
returnType = "Unnamed.Test.E",
documentation = None
),
Vector()
),
Tree.Node(
Suggestion.Constructor(
externalId = None,
module = "Unnamed.Test",
name = "R",
arguments = Seq(
Suggestion.Argument("y", "b", false, false, None)
),
returnType = "Unnamed.Test.E",
documentation = None
),
Vector()
),
Tree.Node(
Suggestion.Method(
externalId = None,
module = "Unnamed.Test",
name = "x",
arguments = Seq(
Suggestion
.Argument("self", "Unnamed.Test.E", false, false, None)
),
selfType = "Unnamed.Test.E",
returnType = "a",
isStatic = false,
documentation = None
),
Vector()
),
Tree.Node(
Suggestion.Method(
externalId = None,
module = "Unnamed.Test",
name = "y",
arguments = Seq(
Suggestion
.Argument("self", "Unnamed.Test.E", false, false, None)
),
selfType = "Unnamed.Test.E",
returnType = "b",
isStatic = false,
documentation = None
),
Vector()
)
)
)
}
"build Integer type" in {
val code = "type Integer"

View File

@ -806,6 +806,7 @@ class ProjectManagementApiSpec
}
"return a list of projects even if editions of some of them cannot be resolved" taggedAs Retry in {
pending // flaky
implicit val client: WsTestClient = new WsTestClient(address)
//given
val fooId = createProject("Foo")

View File

@ -2,7 +2,7 @@ from Standard.Base import all
polyglot java import java.util.Random
make_random_vec : Integer -> Vector.Vector
make_random_vec : Integer -> Vector
make_random_vec n =
random_gen = Random.new n
Vector.fill n random_gen.nextLong

View File

@ -6,7 +6,7 @@ import Standard.Test.Extensions
from project.Semantic.Default_Args_Spec.Box import all
type Box
type Foo (v : Bool = True)
Foo (v : Bool = True)
type Bar (a : Integer = 1) (b : Box = (Foo False)) (c : Boolean = b.v)