Skip to content

Commit 12b901c

Browse files
lahodajbiboudis
andauthored
First round of instance patterns. No patterns with separate instance and match candidate yet. (openjdk#20)
* First round of instance patterns. No patterns with separate instance and match candidate yet. * A very (VERY!) basic support for instance pattern with split receiver and match candidate. * Align calling convention between instance patterns and deconstructors * Fix adjustment of `syntheticPattern` computation --------- Co-authored-by: Angelos Bimpoudis <angelos.bimpoudis@oracle.com>
1 parent 13bde79 commit 12b901c

File tree

11 files changed

+461
-88
lines changed

11 files changed

+461
-88
lines changed

src/java.base/share/classes/java/lang/runtime/PatternBootstraps.java

Lines changed: 43 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@
5454
*/
5555
public class PatternBootstraps {
5656

57-
private PatternBootstraps() {}
57+
private PatternBootstraps() {
58+
}
5859

5960
private static final Object SENTINEL = new Object();
6061
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
@@ -67,8 +68,7 @@ private static class StaticHolders {
6768
try {
6869
SYNTHETIC_PATTERN = LOOKUP.findStatic(PatternBootstraps.class, "syntheticPattern",
6970
MethodType.methodType(Object.class, Method[].class, MethodHandle.class, Object.class));
70-
}
71-
catch (ReflectiveOperationException e) {
71+
} catch (ReflectiveOperationException e) {
7272
throw new ExceptionInInitializerError(e);
7373
}
7474
}
@@ -97,13 +97,10 @@ private static class StaticHolders {
9797
* @param invocationType The invocation type of the {@code CallSite} with one parameter,
9898
* a reference type, and an {@code Object} as a return type.
9999
* @param mangledName The mangled name of the method declaration that will act as a pattern.
100-
*
101100
* @return a {@code CallSite} returning the first matching element as described above
102-
*
103101
* @throws NullPointerException if any argument is {@code null}
104102
* @throws IllegalArgumentException if the invocation type is not a method type of first parameter of a reference type,
105103
* and with {@code Object} as its return type,
106-
*
107104
* @jvms 4.4.6 The CONSTANT_NameAndType_info Structure
108105
* @jvms 4.4.10 The CONSTANT_Dynamic_info and CONSTANT_InvokeDynamic_info Structures
109106
*/
@@ -113,69 +110,64 @@ public static CallSite invokePattern(MethodHandles.Lookup lookup,
113110
String mangledName) {
114111
if (invocationType.parameterCount() == 2) {
115112
Class<?> receiverType = invocationType.parameterType(0);
116-
Class<?> selectorType = invocationType.parameterType(1);
113+
Class<?> matchCandidateType = invocationType.parameterType(1);
117114
if ((!invocationType.returnType().equals(Object.class)))
118115
throw new IllegalArgumentException("Illegal invocation type " + invocationType);
119116

120117
MethodHandle target = null;
121118
try {
122119
// Attempt 1: discover the pattern declaration
123-
target = lookup.findStatic(receiverType, mangledName, MethodType.methodType(Object.class, receiverType, selectorType));
120+
target = lookup.findStatic(receiverType, mangledName, MethodType.methodType(Object.class, receiverType, matchCandidateType));
124121

125122
return new ConstantCallSite(target);
126123
} catch (Throwable t) {
127-
throw new IllegalArgumentException("Cannot find a pattern");
128-
}
129-
}
124+
// Attempt 2: synthesize the pattern declaration from the record components
125+
if (!matchCandidateType.isRecord() || !receiverType.equals(matchCandidateType)) {
126+
throw new IllegalArgumentException("Implicit pattern invocation with erroneous match-candidate type or received type");
127+
}
130128

131-
Class<?> selectorType = invocationType.parameterType(0);
132-
if (invocationType.parameterCount() != 1
133-
|| (!invocationType.returnType().equals(Object.class)))
134-
throw new IllegalArgumentException("Illegal invocation type " + invocationType);
135-
136-
MethodHandle target = null;
137-
try {
138-
// Attempt 1: discover the pattern declaration
139-
target = lookup.findStatic(selectorType, mangledName, MethodType.methodType(Object.class, selectorType));
140-
141-
return new ConstantCallSite(target);
142-
} catch (Throwable t) {
143-
// Attempt 2: synthesize the pattern declaration from the record components
144-
if (!selectorType.isRecord()) {
145-
throw new IllegalArgumentException("Cannot find a pattern");
146-
}
147-
148-
String expectedMangledName = PatternBytecodeName.mangle(selectorType,
149-
Arrays.stream(selectorType.getRecordComponents())
150-
.map(RecordComponent::getType)
151-
.toArray(Class<?>[]::new));
129+
String expectedMangledName = PatternBytecodeName.mangle(matchCandidateType,
130+
Arrays.stream(matchCandidateType.getRecordComponents())
131+
.map(RecordComponent::getType)
132+
.toArray(Class<?>[]::new));
152133

153-
if (!expectedMangledName.equals(mangledName)) {
154-
throw new IllegalArgumentException("\nUnexpected pattern at use site: " + mangledName + "\nWas expecting: " + expectedMangledName);
155-
}
134+
if (!expectedMangledName.equals(mangledName)) {
135+
throw new IllegalArgumentException("Unexpected pattern at use site: " + mangledName + "(was expecting: " + expectedMangledName + ")");
136+
}
156137

157-
@SuppressWarnings("removal")
158-
final RecordComponent[] components = AccessController.doPrivileged(
159-
(PrivilegedAction<RecordComponent[]>) selectorType::getRecordComponents);
138+
@SuppressWarnings("removal")
139+
final RecordComponent[] components = AccessController.doPrivileged(
140+
(PrivilegedAction<RecordComponent[]>) matchCandidateType::getRecordComponents);
160141

161-
Method[] accessors = Arrays.stream(components).map(c -> {
162-
Method accessor = c.getAccessor();
163-
accessor.setAccessible(true);
164-
return accessor;
165-
}).toArray(Method[]::new);
142+
Method[] accessors = Arrays.stream(components).map(c -> {
143+
Method accessor = c.getAccessor();
144+
accessor.setAccessible(true);
145+
return accessor;
146+
}).toArray(Method[]::new);
166147

167-
Class<?>[] ctypes = Arrays.stream(components).map(c -> c.getType()).toArray(Class<?>[]::new);
148+
Class<?>[] ctypes = Arrays.stream(components).map(c -> c.getType()).toArray(Class<?>[]::new);
168149

169-
Carriers.CarrierElements carrierElements = Carriers.CarrierFactory.of(ctypes);
150+
Carriers.CarrierElements carrierElements = Carriers.CarrierFactory.of(ctypes);
170151

171-
MethodHandle initializingConstructor = carrierElements.initializingConstructor();
152+
MethodHandle initializingConstructor = carrierElements.initializingConstructor();
172153

173-
MethodHandle carrierCreator = initializingConstructor.asSpreader(Object[].class, ctypes.length);
154+
MethodHandle carrierCreator = initializingConstructor.asSpreader(Object[].class, ctypes.length);
174155

175-
target = MethodHandles.insertArguments(StaticHolders.SYNTHETIC_PATTERN, 0, accessors, carrierCreator).asType(invocationType);
156+
target =
157+
MethodHandles.dropArguments(
158+
MethodHandles.insertArguments(StaticHolders.SYNTHETIC_PATTERN,
159+
0,
160+
accessors,
161+
carrierCreator),
162+
1,
163+
Object.class).asType(invocationType);
176164

177-
return new ConstantCallSite(target);
165+
return new ConstantCallSite(target);
166+
}
178167
}
168+
169+
// todo: static patterns will expect one parameter
170+
throw new IllegalStateException("Pattern Invocation Illegal State");
179171
}
180172

181173
/**
@@ -184,9 +176,8 @@ public static CallSite invokePattern(MethodHandles.Lookup lookup,
184176
*
185177
* @param matchCandidateInstance the receiver of a pattern
186178
* @param matchCandidateType the type of the match candidate
187-
* @return initialized carrier object
188-
*
189-
* @throws Throwable throws if invocation of synthetic pattern fails
179+
* @return initialized carrier object
180+
* @throws Throwable throws if invocation of synthetic pattern fails
190181
*/
191182
private static Object syntheticPattern(Method[] accessors, MethodHandle carrierCreator, Object matchCandidateInstance) throws Throwable {
192183
Object[] extracted = Arrays.stream(accessors).map(accessor -> {

src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,10 @@ public Type externalType(Types types) {
366366
t.getThrownTypes(),
367367
t.tsym);
368368
} else if ((flags() & PATTERN) != 0) {
369-
return new MethodType(List.of(owner.erasure(types)), types.syms.objectType, List.nil(), t.tsym);
369+
MethodSymbol thisAsMethod = (MethodSymbol) this;
370+
List<Type> parameterTypes = thisAsMethod.getParameters().map(p -> types.erasure(p.type));
371+
372+
return new MethodType(parameterTypes, types.syms.objectType, List.nil(), t.tsym);
370373
} else {
371374
return t;
372375
}
@@ -480,6 +483,11 @@ public boolean isDeconstructor() {
480483
return isPattern() && name == owner.name;
481484
}
482485

486+
public boolean isTotalPattern() {
487+
//TODO: some non-deconstructor patterns can also be total, to be implemented.
488+
return isDeconstructor() && (flags() & PARTIAL) == 0;
489+
}
490+
483491
public boolean isDynamic() {
484492
return false;
485493
}
@@ -2102,7 +2110,7 @@ private Name mangledBytecodePatternName(Types types) {
21022110

21032111
String postFix = String.join(":", parts);
21042112

2105-
return name.table.names.fromString(owner.name.toString() + ":" + postFix);
2113+
return name.table.names.fromString((isDeconstructor() ? owner.name.toString() : name) + ":" + postFix);
21062114
}
21072115

21082116
static class UnSharedSignatureGenerator extends Types.SignatureGenerator {

src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1970,7 +1970,7 @@ public String toString() {
19701970
StringBuilder sb = new StringBuilder();
19711971
appendAnnotationsString(sb);
19721972
sb.append("(out ");
1973-
sb.append(bindingtypes.stream().map(String::valueOf).collect(Collectors.joining(", out")));
1973+
sb.append(bindingtypes.stream().map(String::valueOf).collect(Collectors.joining(", out ")));
19741974
sb.append(')');
19751975
return sb.toString();
19761976
}

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,6 +1208,7 @@ public void visitMethodDef(JCMethodDecl tree) {
12081208
}
12091209

12101210
if (m.isDeconstructor()) {
1211+
//TODO: for instance/named patterns
12111212
m.patternFlags.add(PatternFlags.DECONSTRUCTOR);
12121213
if ((tree.mods.flags & Flags.PARTIAL) == 0) {
12131214
m.patternFlags.add(PatternFlags.TOTAL);
@@ -2472,7 +2473,7 @@ public void visitMatch(JCMatch tree) {
24722473

24732474
@Override
24742475
public void visitMatchFail(JCMatchFail tree) {
2475-
if ((env.enclMethod.sym.flags_field & Flags.PARTIAL) == 0) {
2476+
if (env.enclMethod.sym.isTotalPattern()) {
24762477
log.error(tree.pos(), Errors.UnmarkedPartialDeconstructor);
24772478
}
24782479
result = null;
@@ -4349,31 +4350,51 @@ public void visitBindingPattern(JCBindingPattern tree) {
43494350
@Override
43504351
public void visitRecordPattern(JCRecordPattern tree) {
43514352
Type site;
4353+
Type uncapturedSite;
4354+
Name deconstructorName;
43524355

43534356
if (tree.deconstructor == null) {
43544357
log.error(tree.pos(), Errors.DeconstructionPatternVarNotAllowed);
43554358
tree.record = syms.errSymbol;
4356-
site = tree.type = types.createErrorType(tree.record.type);
4359+
uncapturedSite = site = tree.type = types.createErrorType(tree.record.type);
4360+
deconstructorName = names.empty;
43574361
} else {
4358-
Type type = attribType(tree.deconstructor, env);
4359-
if (type.isRaw() && type.tsym.getTypeParameters().nonEmpty()) {
4360-
Type inferred = infer.instantiatePatternType(resultInfo.pt, type.tsym);
4361-
if (inferred == null) {
4362-
log.error(tree.pos(), Errors.PatternTypeCannotInfer);
4363-
} else {
4364-
type = inferred;
4362+
//TODO: if there's a deconstructor and instance pattern with the same name, which one prevails?
4363+
if (deferredAttr.attribSpeculative(tree.deconstructor, env, new ResultInfo(KindSelector.TYP, Type.noType)).type.hasTag(TypeTag.CLASS)) {
4364+
Type type = attribType(tree.deconstructor, env);
4365+
if (type.isRaw() && type.tsym.getTypeParameters().nonEmpty()) {
4366+
Type inferred = infer.instantiatePatternType(resultInfo.pt, type.tsym);
4367+
if (inferred == null) {
4368+
log.error(tree.pos(), Errors.PatternTypeCannotInfer);
4369+
} else {
4370+
type = inferred;
4371+
}
43654372
}
4373+
uncapturedSite = type;
4374+
site = types.capture(type);
4375+
deconstructorName = TreeInfo.name(tree.deconstructor);
4376+
} else if (tree.deconstructor instanceof JCFieldAccess acc) {
4377+
Type type = attribTree(acc.selected, env, new ResultInfo(KindSelector.VAL_TYP, Type.noType));
4378+
site = type; //TODO: capture?
4379+
uncapturedSite = type;
4380+
deconstructorName = acc.name;
4381+
} else if (tree.deconstructor instanceof JCIdent ident) {
4382+
Type type = pt();
4383+
site = type; //TODO: capture?
4384+
uncapturedSite = type;
4385+
deconstructorName = ident.name;
4386+
} else {
4387+
//TODO: error recovery
4388+
throw Assert.error("Not handled.");
43664389
}
4367-
tree.type = tree.deconstructor.type = type;
4368-
site = types.capture(tree.type);
43694390
}
43704391

43714392
List<Type> nestedPatternsTargetTypes = null;
43724393

43734394
if (site.tsym.kind == Kind.TYP) {
43744395
int nestedPatternCount = tree.nested.size();
43754396

4376-
List<MethodSymbol> candidates = candidatesWithArity(site, nestedPatternCount);
4397+
List<MethodSymbol> candidates = candidatesWithArity(site, deconstructorName, nestedPatternCount);
43774398

43784399
if (candidates.size() >= 1) {
43794400
List<Type> notionalTypes = calculateNotionalTypes(tree);
@@ -4427,6 +4448,12 @@ public void visitRecordPattern(JCRecordPattern tree) {
44274448
} finally {
44284449
localEnv.info.scope.leave();
44294450
}
4451+
//TODO: are these types sensible?
4452+
if (tree.patternDeclaration != null && tree.patternDeclaration.getParameters().size() == 1) { // TODO: improve error recovery
4453+
tree.type = tree.deconstructor.type = tree.patternDeclaration.getParameters().head.type;
4454+
} else {
4455+
tree.type = tree.deconstructor.type = uncapturedSite;
4456+
}
44304457
chk.validate(tree.deconstructor, env, true);
44314458
result = tree.type;
44324459
matchBindings = new MatchBindings(outBindings.toList(), List.nil());
@@ -4648,9 +4675,9 @@ private boolean isApplicable(List<Type> patternDeclarationBindingType, List<Type
46484675
* @param nestedPatternCount the number of nested patterns
46494676
* @return a list of MethodSymbols
46504677
*/
4651-
private List<MethodSymbol> candidatesWithArity(Type site, int nestedPatternCount) {
4678+
private List<MethodSymbol> candidatesWithArity(Type site, Name deconstructorName, int nestedPatternCount) {
46524679
var matchersIt = site.tsym.members()
4653-
.getSymbols(sym -> sym.isPattern() && sym.type.getBindingTypes().size() == nestedPatternCount)
4680+
.getSymbolsByName(deconstructorName, sym -> sym.isPattern() && sym.type.getBindingTypes().size() == nestedPatternCount)
46544681
.iterator();
46554682

46564683
List<MethodSymbol> patternDeclarations = Stream.generate(() -> null)
@@ -4674,11 +4701,11 @@ private List<MethodSymbol> candidatesWithArity(Type site, int nestedPatternCount
46744701
.collect(List.collector());
46754702
PatternType pt = new PatternType(recordComponents, erasedComponents, syms.voidType, syms.methodClass);
46764703

4677-
MethodSymbol synthetized = new MethodSymbol(PUBLIC | SYNTHETIC | PATTERN, ((ClassSymbol) site.tsym).name, pt, site.tsym);
4704+
MethodSymbol synthesized = new MethodSymbol(PUBLIC | SYNTHETIC | PATTERN, ((ClassSymbol) site.tsym).name, pt, site.tsym);
46784705

4679-
synthetized.patternFlags.add(PatternFlags.DECONSTRUCTOR);
4680-
synthetized.patternFlags.add(PatternFlags.TOTAL);
4681-
patternDeclarations = patternDeclarations.prepend(synthetized);
4706+
synthesized.patternFlags.add(PatternFlags.DECONSTRUCTOR);
4707+
synthesized.patternFlags.add(PatternFlags.TOTAL);
4708+
patternDeclarations = patternDeclarations.prepend(synthesized);
46824709
}
46834710
}
46844711

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4878,6 +4878,10 @@ private boolean patternDominated(JCPattern existingPattern, JCPattern currentPat
48784878
if (existingNested.size() != currentNested.size()) {
48794879
return false;
48804880
}
4881+
if (!existingRecordPattern.patternDeclaration.isTotalPattern()) {
4882+
//partial patterns cannot dominate(?)
4883+
return false;
4884+
}
48814885
while (existingNested.nonEmpty()) {
48824886
if (!patternDominated(existingNested.head, currentNested.head)) {
48834887
return false;

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,8 @@ Type signature(MethodSymbol msym,
157157
thrownbuf.append(exc);
158158
}
159159
if (msym.isPattern()) {
160-
Assert.check(params.isEmpty());
160+
//TODO: anything to do with the params?
161+
//Assert.check(params.isEmpty());
161162
var erasedBindingTypes = bindingsbuf.toList()
162163
.stream()
163164
.map(b -> types.erasure(b))

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ private UnrolledRecordPattern unrollRecordPattern(JCRecordPattern recordPattern)
534534
return new UnrolledRecordPattern((JCBindingPattern) make.BindingPattern(recordBindingVar).setType(recordBinding.type), guard);
535535
}
536536

537-
private JCMethodInvocation generatePatternCall(JCRecordPattern recordPattern, BindingSymbol tempBind) {
537+
private JCMethodInvocation generatePatternCall(JCRecordPattern recordPattern, BindingSymbol matchCandidate) {
538538
List<Type> staticArgTypes = List.of(syms.methodHandleLookupType,
539539
syms.stringType,
540540
syms.methodTypeType,
@@ -544,8 +544,26 @@ private JCMethodInvocation generatePatternCall(JCRecordPattern recordPattern, Bi
544544
recordPattern.pos(), env, syms.patternBootstrapsType,
545545
names.invokePattern, staticArgTypes, List.nil());
546546

547+
List<JCExpression> invocationParams = List.of(make.Ident(matchCandidate));
548+
List<Type> invocationParamTypes;
549+
550+
if (true /*is instance pattern*/) {
551+
if (recordPattern.deconstructor instanceof JCFieldAccess acc &&
552+
!TreeInfo.isStaticSelector(acc.selected, names)) {
553+
invocationParamTypes = List.of(/*receiver:*/acc.selected.type,
554+
/*match candidate:*/recordPattern.type);
555+
invocationParams = invocationParams.prepend(acc.selected);
556+
} else {
557+
invocationParamTypes = List.of(/*receiver:*/recordPattern.type,
558+
/*match candidate:*/recordPattern.type);
559+
invocationParams = invocationParams.prepend(make.Ident(matchCandidate));
560+
}
561+
}
562+
// else { // this will be needed for static patterns later
563+
// invocationParamTypes = List.of(recordPattern.type);
564+
// }
547565
MethodType indyType = new MethodType(
548-
List.of(recordPattern.type),
566+
invocationParamTypes,
549567
syms.objectType,
550568
List.nil(),
551569
syms.methodClass
@@ -567,7 +585,7 @@ private JCMethodInvocation generatePatternCall(JCRecordPattern recordPattern, Bi
567585
qualifier.type = syms.objectType;
568586
return make.Apply(List.nil(),
569587
qualifier,
570-
List.of(make.Ident(tempBind)))
588+
invocationParams)
571589
.setType(syms.objectType);
572590
}
573591

0 commit comments

Comments
 (0)