The grammar's #ParameterName alternative is Identifier isnull=QMark?, so the ? is a separate labelled token. ParseToPojo.visitParameterName builds the POJO from ctx.getText(), which includes the marker, so L? produces StringLiteral{nullable=true, value="L?"} — the name and the marker both end up in value().
Every lookup in TypeExpressionEvaluator.visit(ParameterizedType.StringLiteral) keys off that string: the locals map, boundInteger, parseIntegerLiteral and boundType. None can ever match, so a ?-marked reference is unresolvable, and the two stringLiteral.nullable() reads in that method are unreachable for this node.
Measured on main at cf581f4, with a declared argument varchar<L> bound to varchar(10):
varchar<L> -> VarChar{length=10}
varchar<L?> -> UnsupportedOperationException: Unbound type parameter 'L?' in return-type expression
visitAnyType gets this right, reading AnyVar().getText() and the isnull token separately, which is why any1? resolves and a named parameter does not.
The fix is .value(ctx.Identifier().getText()). The practically relevant case is a nullable reference to a named type parameter rather than the integer position shown above, which is what makes the dead nullable() handling in the evaluator matter.
The grammar's
#ParameterNamealternative isIdentifier isnull=QMark?, so the?is a separate labelled token.ParseToPojo.visitParameterNamebuilds the POJO fromctx.getText(), which includes the marker, soL?producesStringLiteral{nullable=true, value="L?"}— the name and the marker both end up invalue().Every lookup in
TypeExpressionEvaluator.visit(ParameterizedType.StringLiteral)keys off that string: the locals map,boundInteger,parseIntegerLiteralandboundType. None can ever match, so a?-marked reference is unresolvable, and the twostringLiteral.nullable()reads in that method are unreachable for this node.Measured on
mainat cf581f4, with a declared argumentvarchar<L>bound tovarchar(10):visitAnyTypegets this right, readingAnyVar().getText()and theisnulltoken separately, which is whyany1?resolves and a named parameter does not.The fix is
.value(ctx.Identifier().getText()). The practically relevant case is a nullable reference to a named type parameter rather than the integer position shown above, which is what makes the deadnullable()handling in the evaluator matter.