This is an automated email from the ASF dual-hosted git repository.

xiazcy pushed a commit to branch steps-taking-traversal-poc
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit a9d1f642170da171bdc91b1a3ccb232f1e829d72
Author: Yang Xia <[email protected]>
AuthorDate: Wed May 20 16:54:19 2026 -0700

     allow V()/E() start steps to take traversals by generating a false 
traverser like mergeV/mergeE
---
 .../grammar/TraversalSourceSpawnMethodVisitor.java | 22 ++++++++++------------
 .../traversal/dsl/graph/GraphTraversalSource.java  | 10 ++++++----
 .../process/traversal/step/map/GraphStep.java      | 15 ++++++++++-----
 gremlin-language/src/main/antlr4/Gremlin.g4        |  2 ++
 .../gremlin/test/features/map/VETraversal.feature  | 10 ++++++++--
 5 files changed, 36 insertions(+), 23 deletions(-)

diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java
index f2ca8b7299..bed196c29e 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java
@@ -99,13 +99,12 @@ public class TraversalSourceSpawnMethodVisitor extends 
DefaultGremlinBaseVisitor
      */
     @Override
     public GraphTraversal visitTraversalSourceSpawnMethod_E(final 
GremlinParser.TraversalSourceSpawnMethod_EContext ctx) {
+        if (ctx.nestedTraversal() != null) {
+            return this.traversalSource.E((Traversal<?, ?>) 
anonymousVisitor.visitNestedTraversal(ctx.nestedTraversal()));
+        }
         final Object[] args = 
antlr.argumentVisitor.parseObjectVarargs(ctx.genericArgumentVarargs());
-        for (final Object arg : args) {
-            if (arg instanceof Traversal) {
-                throw new IllegalArgumentException(
-                        "E(traversal) cannot be used as a start step because 
there is no Traverser context " +
-                        "available to evaluate the child traversal. Use 
E(traversal) as a mid-traversal step instead.");
-            }
+        if (args.length == 1 && args[0] instanceof Traversal) {
+            return this.traversalSource.E((Traversal<?, ?>) args[0]);
         }
         return this.traversalSource.E(args);
     }
@@ -115,13 +114,12 @@ public class TraversalSourceSpawnMethodVisitor extends 
DefaultGremlinBaseVisitor
      */
     @Override
     public GraphTraversal visitTraversalSourceSpawnMethod_V(final 
GremlinParser.TraversalSourceSpawnMethod_VContext ctx) {
+        if (ctx.nestedTraversal() != null) {
+            return this.traversalSource.V((Traversal<?, ?>) 
anonymousVisitor.visitNestedTraversal(ctx.nestedTraversal()));
+        }
         final Object[] args = 
antlr.argumentVisitor.parseObjectVarargs(ctx.genericArgumentVarargs());
-        for (final Object arg : args) {
-            if (arg instanceof Traversal) {
-                throw new IllegalArgumentException(
-                        "V(traversal) cannot be used as a start step because 
there is no Traverser context " +
-                        "available to evaluate the child traversal. Use 
V(traversal) as a mid-traversal step instead.");
-            }
+        if (args.length == 1 && args[0] instanceof Traversal) {
+            return this.traversalSource.V((Traversal<?, ?>) args[0]);
         }
         return this.traversalSource.V(args);
     }
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java
index 78ab16e68e..b0991a67b9 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java
@@ -539,8 +539,9 @@ public class GraphTraversalSource implements 
TraversalSource {
 
     /**
      * Spawns a {@link GraphTraversal} starting with vertices whose IDs are 
resolved from a child traversal.
-     * This form will throw an {@link IllegalStateException} at runtime 
because there is no traverser context
-     * available to evaluate the child traversal as a start step.
+     * As a start step, a synthetic traverser is generated to seed the child 
traversal evaluation,
+     * consistent with how {@code mergeV(traversal)} handles start steps. The 
child traversal should
+     * be self-contained (e.g., {@code __.V(1).id()}) rather than dependent on 
an incoming traverser.
      *
      * @param traversal the child traversal that produces vertex IDs
      * @since 4.0.0
@@ -576,8 +577,9 @@ public class GraphTraversalSource implements 
TraversalSource {
 
     /**
      * Spawns a {@link GraphTraversal} starting with edges whose IDs are 
resolved from a child traversal.
-     * This form will throw an {@link IllegalStateException} at runtime 
because there is no traverser context
-     * available to evaluate the child traversal as a start step.
+     * As a start step, a synthetic traverser is generated to seed the child 
traversal evaluation,
+     * consistent with how {@code mergeE(traversal)} handles start steps. The 
child traversal should
+     * be self-contained (e.g., {@code __.V(1).outE().id()}) rather than 
dependent on an incoming traverser.
      *
      * @param traversal the child traversal that produces edge IDs
      * @since 4.0.0
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java
index 5abc13ecc9..043c99c99f 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java
@@ -199,12 +199,17 @@ public class GraphStep<S, E extends Element> extends 
AbstractStep<S, E> implemen
                     if (this.done)
                         throw FastNoSuchElementException.instance();
                     if (this.idTraversal != null) {
-                        throw new IllegalStateException(
-                                "V(traversal)/E(traversal) cannot be used as a 
start step: " +
-                                "no Traverser context available to evaluate 
the child traversal");
+                        // Start step with idTraversal: generate a synthetic 
traverser to seed
+                        // the child traversal, consistent with how 
mergeV/addV handle start steps.
+                        this.done = true;
+                        final Traverser.Admin<S> syntheticTraverser = 
(Traverser.Admin<S>)
+                                
this.getTraversal().getTraverserGenerator().generate(false, (Step) this, 1L);
+                        final Object[] resolvedIds = 
resolveTraversalIds(syntheticTraverser);
+                        this.iterator = lookupElements(resolvedIds);
+                    } else {
+                        this.done = true;
+                        this.iterator = null == this.iteratorSupplier ? 
EmptyIterator.instance() : this.iteratorSupplier.get();
                     }
-                    this.done = true;
-                    this.iterator = null == this.iteratorSupplier ? 
EmptyIterator.instance() : this.iteratorSupplier.get();
                 } else {
                     this.head = this.starts.next();
                     if (this.idTraversal != null) {
diff --git a/gremlin-language/src/main/antlr4/Gremlin.g4 
b/gremlin-language/src/main/antlr4/Gremlin.g4
index b161a271d3..822cb59481 100644
--- a/gremlin-language/src/main/antlr4/Gremlin.g4
+++ b/gremlin-language/src/main/antlr4/Gremlin.g4
@@ -123,10 +123,12 @@ traversalSourceSpawnMethod_addV
 
 traversalSourceSpawnMethod_E
     : K_E LPAREN genericArgumentVarargs RPAREN
+    | K_E LPAREN nestedTraversal RPAREN
     ;
 
 traversalSourceSpawnMethod_V
     : K_V LPAREN genericArgumentVarargs RPAREN
+    | K_V LPAREN nestedTraversal RPAREN
     ;
 
 traversalSourceSpawnMethod_inject
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/VETraversal.feature
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/VETraversal.feature
index 7bddadb221..c8535d6a74 100644
--- 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/VETraversal.feature
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/VETraversal.feature
@@ -130,7 +130,9 @@ Feature: Step - V(traversal) and E(traversal)
       g.V(__.V(vid1).id()).values("name")
       """
     When iterated to list
-    Then the traversal will raise an error
+    Then the result should be unordered
+      | result |
+      | marko |
 
   Scenario: g_EXVXvid1X_outE_idX
     Given the modern graph
@@ -140,4 +142,8 @@ Feature: Step - V(traversal) and E(traversal)
       g.E(__.V(vid1).outE().id())
       """
     When iterated to list
-    Then the traversal will raise an error
+    Then the result should be unordered
+      | result |
+      | e[marko-created->lop] |
+      | e[marko-knows->josh] |
+      | e[marko-knows->vadas] |

Reply via email to