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
20 changes: 13 additions & 7 deletions dev/design/concurrency.md
Original file line number Diff line number Diff line change
Expand Up @@ -467,11 +467,17 @@ Acceptance: no semantic change or native/FFM surprise in supported workloads.
Promotion beyond experimental additionally requires a measured benefit over
platform threads; runtime-clone cost must not be mistaken for scheduler cost.

### Phase 24 — Final core syntax compatibility
### Phase 24 — Final core syntax compatibility (completed 2026-08-12)

Finish the remaining `op/threads.t` postfix create/join expression without a
thread-specific parser shortcut that changes ordinary anonymous-sub precedence.

Implemented by keeping an unknown print-filehandle candidate as an expression
when it is followed by a known indirect-object package. This fixes the general
`print method Package LIST` ambiguity rather than recognizing `threads` or the
specific postfix chain. A system-Perl-validated regression test covers the
generic form.

Acceptance: `op/threads.t` reaches 30/30 on JVM and interpreter backends while
`class/threads.t` remains 4/4 and `threads-dirh.t` continues to exit cleanly.

Expand Down Expand Up @@ -590,7 +596,7 @@ has a measured benefit over a fresh snapshot.

## 7. Progress Tracking

### Current Status: Phase 23 implemented; compatibility hardening begins next
### Current Status: Phase 24 complete; Phase 25 is next

Hints, warnings, filters, and source maps are runtime-owned while compiler-only
scratch remains protected by the global compile lock. The Phase 11 inventory is
Expand Down Expand Up @@ -735,11 +741,11 @@ request history.

### Next Steps

1. After PR 939 merges, implement Phases 24–28 as independent, always-green PRs:
final core syntax, snapshot graph integrity, scalar operator parity, regex
concurrency/debug state, and then general regex parity. For every `_thr.t`
result, record the direct companion suite in the same run and require zero
thread-induced delta rather than comparing aggregate TAP counts alone.
1. Implement Phases 25–28 as independent, always-green PRs: snapshot graph
integrity, scalar operator parity, regex concurrency/debug state, and then
general regex parity. For every `_thr.t` result, record the direct companion
suite in the same run and require zero thread-induced delta rather than
comparing aggregate TAP counts alone.
2. Implement Phases 29–32 independently: truthful API behavior, resource
inheritance and Test2 stress, native callback/handle ownership, and only then
additional shared value categories. Preserve the green anchors after every
Expand Down
31 changes: 31 additions & 0 deletions src/main/java/org/perlonjava/frontend/parser/FileHandle.java
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,33 @@ private static boolean isFollowedByMethodDereference(Parser parser) {
return idx < parser.tokens.size() && "->".equals(parser.tokens.get(idx).text);
}

/**
* An unknown bareword followed by a known package can be Perl's indirect
* object syntax ({@code method Package LIST}). In print's ambiguous
* filehandle position it must remain an expression; autovivifying the
* method name as an IO glob makes the package and arguments look like a
* malformed print list.
*/
private static boolean isFollowedByIndirectObjectPackage(Parser parser) {
int idx = parser.tokenIndex;
while (idx < parser.tokens.size()
&& parser.tokens.get(idx).type == LexerTokenType.WHITESPACE) {
idx++;
}
if (idx >= parser.tokens.size()
|| parser.tokens.get(idx).type != LexerTokenType.IDENTIFIER) {
return false;
}

String packageName = parser.tokens.get(idx).text;
Boolean packageExists = GlobalVariable.packageExistsCache.get(packageName);
if (packageExists == null && !packageName.contains("::")) {
packageExists = GlobalVariable.packageExistsCache.get(
parser.ctx.symbolTable.getCurrentPackage() + "::" + packageName);
}
return Boolean.TRUE.equals(packageExists);
}

private static boolean shouldAutovivifyBarewordHandle(Parser parser, String name, boolean autovivifyUnknownBareword) {
// Do not treat compile-time magic like __PACKAGE__ as print filehandles:
// they match ^[A-Z_][A-Z0-9_]*$ but must fall through to the expression list
Expand All @@ -385,6 +412,10 @@ private static boolean shouldAutovivifyBarewordHandle(Parser parser, String name
return false;
}

if (isFollowedByIndirectObjectPackage(parser)) {
return false;
}

if (ParserTables.CORE_PROTOTYPES.containsKey(name)) {
return false;
}
Expand Down
18 changes: 18 additions & 0 deletions src/test/resources/unit/print_indirect_method_postfix.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use strict;
use warnings;

{
package PrintIndirectFactory;

sub create {
my ($class, $code) = @_;
return bless { code => $code }, $class;
}

sub join { return $_[0]->{code}->() }
}

print "1..1\n";
print create PrintIndirectFactory sub {
return sub { return sub { return "ok 1 - print preserves an indirect postfix call chain\n" } };
}=>->join->()();
Loading