mirror of
https://github.com/enso-org/enso.git
synced 2024-11-26 08:52:58 +03:00
Handle autoscoped constructor args with no UUID (#11354)
* Handle autoscoped constructor args with no UUID An application involving >1 autoscoped atom constructor arguments with no ID would lead to a silent type error in GUI. It was silent because once IdMap gets updated, the original type error disappears and users are left with a No_Such_Method on a Panic. The type error may occur because the compiler was inferring the same UUID for autoscoped constructors. Args with UUID are cached therefore a type confict might occur on the second (or later) argument. Added a unit test case demonstrating the problem (previously it would fail). The search path is now a bit more careful when inferring arguments. * One more test
This commit is contained in:
parent
4d4a2990a0
commit
c6ec5c5399
@ -7472,6 +7472,199 @@ class RuntimeServerTest
|
||||
context.consumeOut shouldEqual List("16")
|
||||
}
|
||||
|
||||
it should "resolve multiple autoscoped atomconstructor with no IDs initially" in {
|
||||
val contextId = UUID.randomUUID()
|
||||
val requestId = UUID.randomUUID()
|
||||
val moduleName = "Enso_Test.Test.Main"
|
||||
val moduleNameLib = "Enso_Test.Test.Lib"
|
||||
val moduleNameTypes = "Enso_Test.Test.Types"
|
||||
val metadata = new Metadata
|
||||
|
||||
val idS = UUID.randomUUID()
|
||||
val idX = UUID.randomUUID()
|
||||
val idAArg = UUID.randomUUID()
|
||||
val idBArg = UUID.randomUUID()
|
||||
val idRes = UUID.randomUUID()
|
||||
|
||||
val typesMetadata = new Metadata
|
||||
val codeTypes = typesMetadata.appendToCode(
|
||||
"""type Foo
|
||||
| A
|
||||
|
|
||||
|type Bar
|
||||
| B
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
)
|
||||
val typesFile = context.writeInSrcDir("Types", codeTypes)
|
||||
|
||||
val libMetadata = new Metadata
|
||||
val codeLib = libMetadata.appendToCode(
|
||||
"""from project.Types import Foo, Bar
|
||||
|from Standard.Base import all
|
||||
|
|
||||
|type Singleton
|
||||
| S value
|
||||
|
|
||||
| test : Foo -> Bar -> Number
|
||||
| test self (x : Foo = ..A) (y : Bar = ..B) =
|
||||
| Singleton.from_test x y
|
||||
|
|
||||
| from_test : Foo -> Bar -> Number
|
||||
| from_test (x : Foo = ..A) (y : Bar = ..B) =
|
||||
| _ = x
|
||||
| _ = y
|
||||
| 42
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
)
|
||||
|
||||
val libFile = context.writeInSrcDir("Lib", codeLib)
|
||||
|
||||
val code =
|
||||
"""from project.Lib import Singleton
|
||||
|
|
||||
|main =
|
||||
| s = Singleton.S 1
|
||||
| x = s.test ..A ..B
|
||||
| x
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val contents = metadata.appendToCode(code)
|
||||
val mainFile = context.writeMain(contents)
|
||||
|
||||
// create context
|
||||
context.send(Api.Request(requestId, Api.CreateContextRequest(contextId)))
|
||||
context.receive shouldEqual Some(
|
||||
Api.Response(requestId, Api.CreateContextResponse(contextId))
|
||||
)
|
||||
|
||||
// Open files
|
||||
context.send(
|
||||
Api.Request(requestId, Api.OpenFileRequest(typesFile, codeTypes))
|
||||
)
|
||||
context.receive shouldEqual Some(
|
||||
Api.Response(Some(requestId), Api.OpenFileResponse)
|
||||
)
|
||||
context.send(
|
||||
Api.Request(requestId, Api.OpenFileRequest(libFile, codeLib))
|
||||
)
|
||||
context.receive shouldEqual Some(
|
||||
Api.Response(Some(requestId), Api.OpenFileResponse)
|
||||
)
|
||||
context.send(
|
||||
Api.Request(requestId, Api.OpenFileRequest(mainFile, contents))
|
||||
)
|
||||
context.receive shouldEqual Some(
|
||||
Api.Response(Some(requestId), Api.OpenFileResponse)
|
||||
)
|
||||
|
||||
// push main
|
||||
val item1 = Api.StackItem.ExplicitCall(
|
||||
Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"),
|
||||
None,
|
||||
Vector()
|
||||
)
|
||||
context.send(
|
||||
Api.Request(requestId, Api.PushContextRequest(contextId, item1))
|
||||
)
|
||||
context.receiveNIgnorePendingExpressionUpdates(
|
||||
2
|
||||
) should contain theSameElementsAs Seq(
|
||||
Api.Response(requestId, Api.PushContextResponse(contextId)),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
|
||||
context.send(
|
||||
Api.Request(
|
||||
Api.EditFileNotification(
|
||||
mainFile,
|
||||
Seq(),
|
||||
execute = true,
|
||||
idMap = Some(
|
||||
model.IdMap(
|
||||
Vector(
|
||||
model.Span(50, 63) -> idS,
|
||||
model.Span(72, 86) -> idX,
|
||||
model.Span(79, 82) -> idAArg,
|
||||
model.Span(83, 86) -> idBArg,
|
||||
model.Span(91, 92) -> idRes
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
val afterIdMapUpdate = context.receiveN(6)
|
||||
|
||||
afterIdMapUpdate shouldEqual Seq(
|
||||
TestMessages.update(
|
||||
contextId,
|
||||
idS,
|
||||
s"$moduleNameLib.Singleton",
|
||||
methodCall = Some(
|
||||
Api.MethodCall(
|
||||
Api
|
||||
.MethodPointer(moduleNameLib, s"$moduleNameLib.Singleton", "S")
|
||||
)
|
||||
),
|
||||
payload = Api.ExpressionUpdate.Payload.Value(None)
|
||||
),
|
||||
TestMessages.update(
|
||||
contextId,
|
||||
idAArg,
|
||||
s"$moduleNameTypes.Foo",
|
||||
methodCall = Some(
|
||||
Api.MethodCall(
|
||||
Api
|
||||
.MethodPointer(
|
||||
moduleNameTypes,
|
||||
s"$moduleNameTypes.Foo",
|
||||
"A"
|
||||
)
|
||||
)
|
||||
),
|
||||
payload = Api.ExpressionUpdate.Payload.Value(None)
|
||||
),
|
||||
TestMessages.update(
|
||||
contextId,
|
||||
idBArg,
|
||||
s"$moduleNameTypes.Bar",
|
||||
methodCall = Some(
|
||||
Api.MethodCall(
|
||||
Api
|
||||
.MethodPointer(
|
||||
moduleNameTypes,
|
||||
s"$moduleNameTypes.Bar",
|
||||
"B"
|
||||
)
|
||||
)
|
||||
),
|
||||
payload = Api.ExpressionUpdate.Payload.Value(None)
|
||||
),
|
||||
TestMessages.update(
|
||||
contextId,
|
||||
idX,
|
||||
s"Standard.Base.Data.Numbers.Integer",
|
||||
methodCall = Some(
|
||||
Api.MethodCall(
|
||||
Api
|
||||
.MethodPointer(
|
||||
moduleNameLib,
|
||||
s"$moduleNameLib.Singleton",
|
||||
"test"
|
||||
)
|
||||
)
|
||||
),
|
||||
payload = Api.ExpressionUpdate.Payload.Value(None)
|
||||
),
|
||||
TestMessages.update(
|
||||
contextId,
|
||||
idRes,
|
||||
s"Standard.Base.Data.Numbers.Integer",
|
||||
payload = Api.ExpressionUpdate.Payload.Value(None)
|
||||
),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
object RuntimeServerTest {
|
||||
|
||||
|
@ -2,6 +2,7 @@ package org.enso.interpreter.test.instrument
|
||||
|
||||
import org.enso.interpreter.runtime.`type`.ConstantsGen
|
||||
import org.enso.interpreter.test.Metadata
|
||||
|
||||
import org.enso.pkg.QualifiedName
|
||||
import org.enso.common.RuntimeOptions
|
||||
import org.enso.polyglot._
|
||||
@ -4074,6 +4075,237 @@ class RuntimeVisualizationsTest extends AnyFlatSpec with Matchers {
|
||||
new String(data, StandardCharsets.UTF_8) shouldEqual "(Mk_Newtype 42)"
|
||||
}
|
||||
|
||||
it should "resolve multiple autoscoped atomconstructor" in withContext() {
|
||||
context =>
|
||||
val contextId = UUID.randomUUID()
|
||||
val requestId = UUID.randomUUID()
|
||||
val visualizationId = UUID.randomUUID()
|
||||
val moduleName = "Enso_Test.Test.Main"
|
||||
val moduleNameLib = "Enso_Test.Test.Lib"
|
||||
val metadata = new Metadata
|
||||
|
||||
val idS = metadata.addItem(50, 13, "eeee")
|
||||
val idX = metadata.addItem(72, 14, "aaaa")
|
||||
val idAArg = UUID.randomUUID()
|
||||
val idBArg = UUID.randomUUID()
|
||||
val idRes = metadata.addItem(91, 1, "dddd")
|
||||
|
||||
val typesMetadata = new Metadata
|
||||
val codeTypes = typesMetadata.appendToCode(
|
||||
"""type Foo
|
||||
| A
|
||||
|
|
||||
|type Bar
|
||||
| B
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
)
|
||||
val typesFile = context.writeInSrcDir("Types", codeTypes)
|
||||
|
||||
val libMetadata = new Metadata
|
||||
val codeLib = libMetadata.appendToCode(
|
||||
"""from project.Types import Foo, Bar
|
||||
|from Standard.Base import all
|
||||
|
|
||||
|type Singleton
|
||||
| S value
|
||||
|
|
||||
| test : Foo -> Bar -> Number
|
||||
| test self (x : Foo = ..A) (y : Bar = ..B) =
|
||||
| Singleton.from_test x y
|
||||
|
|
||||
| from_test : Foo -> Bar -> Number
|
||||
| from_test (x : Foo = ..A) (y : Bar = ..B) =
|
||||
| _ = x
|
||||
| _ = y
|
||||
| 42
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
)
|
||||
|
||||
val libFile = context.writeInSrcDir("Lib", codeLib)
|
||||
|
||||
val code =
|
||||
"""from project.Lib import Singleton
|
||||
|
|
||||
|main =
|
||||
| s = Singleton.S 1
|
||||
| x = s.test ..A ..B
|
||||
| x
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val contents = metadata.appendToCode(code)
|
||||
val mainFile = context.writeMain(contents)
|
||||
|
||||
metadata.assertInCode(idS, code, "Singleton.S 1")
|
||||
metadata.assertInCode(idX, code, "s.test ..A ..B")
|
||||
metadata.assertInCode(idRes, code, "x")
|
||||
|
||||
// create context
|
||||
context.send(Api.Request(requestId, Api.CreateContextRequest(contextId)))
|
||||
context.receive shouldEqual Some(
|
||||
Api.Response(requestId, Api.CreateContextResponse(contextId))
|
||||
)
|
||||
|
||||
// Open files
|
||||
context.send(
|
||||
Api.Request(requestId, Api.OpenFileRequest(typesFile, codeTypes))
|
||||
)
|
||||
context.receive shouldEqual Some(
|
||||
Api.Response(Some(requestId), Api.OpenFileResponse)
|
||||
)
|
||||
context.send(
|
||||
Api.Request(requestId, Api.OpenFileRequest(libFile, codeLib))
|
||||
)
|
||||
context.receive shouldEqual Some(
|
||||
Api.Response(Some(requestId), Api.OpenFileResponse)
|
||||
)
|
||||
context.send(
|
||||
Api.Request(requestId, Api.OpenFileRequest(mainFile, contents))
|
||||
)
|
||||
context.receive shouldEqual Some(
|
||||
Api.Response(Some(requestId), Api.OpenFileResponse)
|
||||
)
|
||||
|
||||
// push main
|
||||
val item1 = Api.StackItem.ExplicitCall(
|
||||
Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"),
|
||||
None,
|
||||
Vector()
|
||||
)
|
||||
context.send(
|
||||
Api.Request(requestId, Api.PushContextRequest(contextId, item1))
|
||||
)
|
||||
context.receiveNIgnorePendingExpressionUpdates(
|
||||
5
|
||||
) should contain theSameElementsAs Seq(
|
||||
Api.Response(requestId, Api.PushContextResponse(contextId)),
|
||||
TestMessages.update(
|
||||
contextId,
|
||||
idS,
|
||||
s"$moduleNameLib.Singleton",
|
||||
methodCall = Some(
|
||||
Api.MethodCall(
|
||||
Api
|
||||
.MethodPointer(moduleNameLib, s"$moduleNameLib.Singleton", "S")
|
||||
)
|
||||
),
|
||||
payload = Api.ExpressionUpdate.Payload.Value(None)
|
||||
),
|
||||
TestMessages.update(
|
||||
contextId,
|
||||
idX,
|
||||
s"Standard.Base.Data.Numbers.Integer",
|
||||
methodCall = Some(
|
||||
Api.MethodCall(
|
||||
Api
|
||||
.MethodPointer(
|
||||
moduleNameLib,
|
||||
s"$moduleNameLib.Singleton",
|
||||
"test"
|
||||
)
|
||||
)
|
||||
),
|
||||
payload = Api.ExpressionUpdate.Payload.Value(None)
|
||||
),
|
||||
TestMessages.update(
|
||||
contextId,
|
||||
idRes,
|
||||
s"Standard.Base.Data.Numbers.Integer",
|
||||
payload = Api.ExpressionUpdate.Payload.Value(None)
|
||||
),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
|
||||
// attach visualization
|
||||
context.send(
|
||||
Api.Request(
|
||||
requestId,
|
||||
Api.AttachVisualization(
|
||||
visualizationId,
|
||||
idRes,
|
||||
Api.VisualizationConfiguration(
|
||||
contextId,
|
||||
Api.VisualizationExpression.Text(
|
||||
"Enso_Test.Test.Main",
|
||||
"x -> x.to_text",
|
||||
Vector()
|
||||
),
|
||||
"Enso_Test.Test.Main"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val attachVisualizationResponses =
|
||||
context.receiveNIgnoreExpressionUpdates(3)
|
||||
attachVisualizationResponses should contain allOf (
|
||||
Api.Response(requestId, Api.VisualizationAttached()),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
val Some(data) = attachVisualizationResponses.collectFirst {
|
||||
case Api.Response(
|
||||
None,
|
||||
Api.VisualizationUpdate(
|
||||
Api.VisualizationContext(
|
||||
`visualizationId`,
|
||||
`contextId`,
|
||||
`idRes`
|
||||
),
|
||||
data
|
||||
)
|
||||
) =>
|
||||
data
|
||||
}
|
||||
new String(data, StandardCharsets.UTF_8) shouldEqual "42"
|
||||
|
||||
context.send(
|
||||
Api.Request(
|
||||
Api.EditFileNotification(
|
||||
mainFile,
|
||||
Seq(),
|
||||
execute = true,
|
||||
idMap = Some(
|
||||
model.IdMap(
|
||||
Vector(
|
||||
model.Span(79, 82) -> idAArg,
|
||||
model.Span(83, 86) -> idBArg
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
val afterIdMapUpdate = context.receiveN(3)
|
||||
|
||||
// Can't do comparison directly because of Arrays https://github.com/scalatest/scalatest/issues/491
|
||||
afterIdMapUpdate should contain allOf (
|
||||
TestMessages.update(
|
||||
contextId,
|
||||
idRes,
|
||||
s"Standard.Base.Data.Numbers.Integer",
|
||||
typeChanged = false,
|
||||
payload = Api.ExpressionUpdate.Payload.Value(None)
|
||||
),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
|
||||
val Some(data2) = afterIdMapUpdate.collectFirst {
|
||||
case Api.Response(
|
||||
None,
|
||||
Api.VisualizationUpdate(
|
||||
Api.VisualizationContext(
|
||||
`visualizationId`,
|
||||
`contextId`,
|
||||
`idRes`
|
||||
),
|
||||
data
|
||||
)
|
||||
) =>
|
||||
data
|
||||
}
|
||||
|
||||
new String(data2, StandardCharsets.UTF_8) shouldEqual "42"
|
||||
|
||||
}
|
||||
|
||||
it should "emit visualization update for the target of a method call (subexpression)" in withContext() {
|
||||
context =>
|
||||
val contextId = UUID.randomUUID()
|
||||
|
@ -429,4 +429,9 @@ public abstract class InvokeCallableNode extends BaseNode {
|
||||
childDispatch.setId(id);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns expression ID of this node. */
|
||||
public UUID getId() {
|
||||
return invokeFunctionNode.getId();
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import org.enso.interpreter.node.ClosureRootNode;
|
||||
import org.enso.interpreter.node.EnsoRootNode;
|
||||
import org.enso.interpreter.node.ExpressionNode;
|
||||
import org.enso.interpreter.node.callable.ApplicationNode;
|
||||
import org.enso.interpreter.node.callable.InvokeCallableNode;
|
||||
import org.enso.interpreter.node.callable.InvokeCallableNode.DefaultsExecutionMode;
|
||||
import org.enso.interpreter.node.callable.argument.ReadArgumentNode;
|
||||
import org.enso.interpreter.node.callable.function.BlockNode;
|
||||
@ -177,10 +178,14 @@ public final class UnresolvedConstructor implements EnsoObject {
|
||||
prototype.where.getRootNode() instanceof EnsoRootNode root ? root.getModuleScope() : null;
|
||||
for (var where = prototype.where; where != null; where = where.getParent()) {
|
||||
if (where instanceof ExpressionNode withId && withId.getId() != null) {
|
||||
if (!(where instanceof ApplicationNode)) {
|
||||
id = withId.getId();
|
||||
}
|
||||
section = withId.getSourceSection();
|
||||
scope = withId.getRootNode() instanceof EnsoRootNode root ? root.getModuleScope() : null;
|
||||
break;
|
||||
} else if (where instanceof InvokeCallableNode callable && callable.getId() != null) {
|
||||
id = callable.getId();
|
||||
}
|
||||
}
|
||||
var fn = ReadArgumentNode.build(0, null, null);
|
||||
@ -191,7 +196,9 @@ public final class UnresolvedConstructor implements EnsoObject {
|
||||
prototype.descs[i].getName(), ReadArgumentNode.build(1 + i, null, null));
|
||||
}
|
||||
var expr = ApplicationNode.build(fn, args, DefaultsExecutionMode.EXECUTE);
|
||||
if (id != null) {
|
||||
expr.setId(id);
|
||||
}
|
||||
if (section != null) {
|
||||
expr.setSourceLocation(section.getCharIndex(), section.getCharLength());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user