RFR: 8324651: Compiler Implementation for Derived Record Creation (Preview) [v5]

ExE Boss duke at openjdk.org
Mon Apr 29 13:26:08 UTC 2024


On Wed, 3 Apr 2024 07:13:13 GMT, Jan Lahoda <jlahoda at openjdk.org> wrote:

>> src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java line 1415:
>> 
>>> 1413:                                                      canonicalConstructorTypes,
>>> 1414:                                                      List.nil());
>>> 1415:         createNew.constructor = init;
>> 
>> Maybe instead of hardcoding the constructor that is canonical at compile time (in case new components get added), it might be better to go through some sort of `indy` callsite like (at least when not in the same compilation unit):
>> 
>> DynamicCallSiteDesc[java.lang.runtime.RecordSupport::derivedConstructor(modified component names...):(T extends Record, Class<?>... /* modified component types */)T]
>> 
>> 
>> with the `derivedConstructor` bootstrap method:
>> 
>> public static MethodHandle derivedConstructor(MethodHandles.Lookup lookup, String unused, MethodType type, String... modifiedComponents) throws ReflectiveOperationException {
>> 	requireNonNull(lookup);
>> 	requireNonNull(type);
>> 	// implicit null-check:
>> 	List<String> modifiedComponentNames = List.of(modifiedComponents);
>> 
>> 	Class<?> rtype = type.returnType();
>> 	if (
>> 		!rtype.isRecord()
>> 		|| type.parameterCount() != modifiedComponents.length + 1
>> 		|| type.parameterType(0) != rtype
>> 	) {
>> 		throw new IllegalArgumentException("...");
>> 	}
>> 
>> 	Set<String> remainingComponentNames = new HashSet(modifiedComponentNames);
>> 	if (remainingComponentNames.size() != modifiedComponentNames.size()) {
>> 		throw new IllegalArgumentException("Duplicate component names in modifiedComponents");
>> 	}
>> 
>> 	RecordComponent[] recordComponents = rtype.getRecordComponents();
>> 
>> 	var componentTypes = new Class<?>[recordComponents.length];
>> 	var filters = new MethodHandle[recordComponents.length];
>> 	var reorder = new int[recordComponents.length];
>> 
>> 	for (int i = 0, j = 1; i < recordComponents.length; i++) {
>> 		var component = recordComponents[i];
>> 		componentTypes[i] = component.getType();
>> 
>> 		var getter = lookup.findVirtual(rtype, component.getName(), MethodType.methodType(component.getType()));
>> 		if (modifiedComponentNames.contains(component.getName())) {
>> 			remainingComponentNames.remove(component.getName());
>> 			filters[i] = null;
>> 			reorder[i] = j++;
>> 		} else {
>> 			filters[i] = getter;
>> 			reorder[i] = 0;
>> 		}
>> 	}
>> 
>> 	if (!remainingComponentNames.isEmpty()) {
>> 		throw new IllegalArgumentException("Components " + remainingComponentNames + " are not present in the record " + rtype);...
>
> While this is very smart, I would prefer not to do that. JLS 13.4.27 talks about binary compatibility for record, and warns that adding or removing components may break record usages. So I don't think we need to provide too much support for adding or removing record components.
> 
> Overall, I believe the current code adheres to what the JLS draft says. If the author of the record wants to add or remove components, they can make it so that the existing compiled code still works (by adding constructor overloads and keeping the accessors for removed component). It is better, IMO, to keep the behavior predictable, and leave adjustments after record component modifications up to the record author, than to introduce some magic.

This was once again asked about on amber‑dev:
- https://mail.openjdk.org/pipermail/amber-dev/2024-April/008748.html

-------------

PR Review Comment: https://git.openjdk.org/jdk/pull/18509#discussion_r1583084422


More information about the core-libs-dev mailing list