Skip to content

Fix char-selector parsing when the length is an expression containing a comma#520

Open
perazz wants to merge 2 commits into
stfc:masterfrom
perazz:519_char_selector_comma_len
Open

Fix char-selector parsing when the length is an expression containing a comma#520
perazz wants to merge 2 commits into
stfc:masterfrom
perazz:519_char_selector_comma_len

Conversation

@perazz

@perazz perazz commented Jul 16, 2026

Copy link
Copy Markdown

Summary

Char_Selector.match splits the KIND and LEN parts of a char-selector on the top-level comma of the (placeholder-substituted) selector body. The length-first branch restored the placeholder-hidden sub-expressions with repmap() before building the child nodes, but the kind-first and positional branches did not. A length whose value contains a comma — e.g. an intrinsic call — was therefore left as the internal placeholder max(F2PY_EXPR_TUPLE_n), i.e. a MAX with a single argument.

Reproducer

module m
  implicit none
  integer, parameter :: ck = 1
contains
  subroutine s(n)
    integer, intent(in) :: n
    character(kind=ck, len=max(n, 0)), pointer :: tmp
  end subroutine s
end module m

On master this raises FortranSyntaxError: Intrinsic 'MAX' expects at least 2 args but found 1.

The failure only surfaces when every symbol in scope is resolved (all use … , only:). If the module has a wildcard USE, all_symbols_resolved is False, the intrinsic arg-count check defers instead of raising, and the same mis-parse silently leaks the F2PY_EXPR_TUPLE_n placeholder into the parse tree — so character(len=max(n,0), kind=ck) (length-first) always worked while character(kind=ck, len=max(n,0)) did not.

Fix

Call repmap() on the length and kind sub-expressions in the kind-first and positional branches, matching the existing length-first branch. Adds regression cases to test_char_selector.

Fixes #519.

Char_Selector.match splits the kind and length parts of a char-selector
on the top-level comma of the parenthesised expression that has been
processed by string_replace_map(). The length-first branch restored the
comma-hidden sub-expressions with repmap() before building the child
nodes, but the kind-first and positional branches did not. As a result a
length such as max(len, 0) was left as the placeholder max(F2PY_EXPR_
TUPLE_n), whose single argument makes the intrinsic argument-count check
report "Intrinsic 'MAX' expects at least 2 args but found 1" and reject
otherwise valid declarations, e.g.

    character(kind=CK, len=max(len, 0)), pointer :: tmp

The failure only surfaces once all symbols in scope are resolved; while a
wildcard USE leaves them unresolved the same mis-parse silently leaks the
placeholder into the parse tree instead of raising.

Apply repmap() in the kind-first and positional branches so the length
and kind sub-expressions are restored, matching the length-first branch,
and extend the Char_Selector test with the comma-containing length forms.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

character specifier mis-parses length when it is an expression containing a comma

1 participant