This commit is contained in:
Jesper Wilhelmsson 2022-07-08 02:07:36 +00:00
commit 01b9f95c62
28 changed files with 755 additions and 138 deletions

View file

@ -723,7 +723,7 @@ void MacroAssembler::emit_static_call_stub() {
isb();
mov_metadata(rmethod, (Metadata*)NULL);
// Jump to the entry point of the i2c stub.
// Jump to the entry point of the c2i stub.
movptr(rscratch1, 0);
br(rscratch1);
}

View file

@ -1013,21 +1013,64 @@ static void gen_continuation_enter(MacroAssembler* masm,
int& exception_offset,
OopMapSet*oop_maps,
int& frame_complete,
int& stack_slots) {
int& stack_slots,
int& interpreted_entry_offset,
int& compiled_entry_offset) {
//verify_oop_args(masm, method, sig_bt, regs);
Address resolve(SharedRuntime::get_resolve_static_call_stub(),
relocInfo::static_call_type);
Address resolve(SharedRuntime::get_resolve_static_call_stub(), relocInfo::static_call_type);
stack_slots = 2; // will be overwritten
address start = __ pc();
Label call_thaw, exit;
// i2i entry used at interp_only_mode only
interpreted_entry_offset = __ pc() - start;
{
#ifdef ASSERT
Label is_interp_only;
__ ldrw(rscratch1, Address(rthread, JavaThread::interp_only_mode_offset()));
__ cbnzw(rscratch1, is_interp_only);
__ stop("enterSpecial interpreter entry called when not in interp_only_mode");
__ bind(is_interp_only);
#endif
// Read interpreter arguments into registers (this is an ad-hoc i2c adapter)
__ ldr(c_rarg1, Address(esp, Interpreter::stackElementSize*2));
__ ldr(c_rarg2, Address(esp, Interpreter::stackElementSize*1));
__ ldr(c_rarg3, Address(esp, Interpreter::stackElementSize*0));
__ push_cont_fastpath(rthread);
__ enter();
stack_slots = 2; // will be adjusted in setup
OopMap* map = continuation_enter_setup(masm, stack_slots);
// The frame is complete here, but we only record it for the compiled entry, so the frame would appear unsafe,
// but that's okay because at the very worst we'll miss an async sample, but we're in interp_only_mode anyway.
// Frame is now completed as far as size and linkage.
fill_continuation_entry(masm);
__ cmp(c_rarg2, (u1)0);
__ br(Assembler::NE, call_thaw);
address mark = __ pc();
__ trampoline_call1(resolve, NULL, false);
oop_maps->add_gc_map(__ pc() - start, map);
__ post_call_nop();
__ b(exit);
CodeBuffer* cbuf = masm->code_section()->outer();
CompiledStaticCall::emit_to_interp_stub(*cbuf, mark);
}
// compiled entry
__ align(CodeEntryAlignment);
compiled_entry_offset = __ pc() - start;
__ enter();
stack_slots = 2; // will be adjusted in setup
OopMap* map = continuation_enter_setup(masm, stack_slots);
frame_complete = __ pc() - start;
fill_continuation_entry(masm);
@ -1036,7 +1079,6 @@ static void gen_continuation_enter(MacroAssembler* masm,
__ br(Assembler::NE, call_thaw);
address mark = __ pc();
__ trampoline_call1(resolve, NULL, false);
oop_maps->add_gc_map(__ pc() - start, map);
@ -1079,7 +1121,7 @@ static void gen_continuation_enter(MacroAssembler* masm,
}
CodeBuffer* cbuf = masm->code_section()->outer();
address stub = CompiledStaticCall::emit_to_interp_stub(*cbuf, mark);
CompiledStaticCall::emit_to_interp_stub(*cbuf, mark);
}
static void gen_special_dispatch(MacroAssembler* masm,
@ -1169,11 +1211,12 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
if (method->is_continuation_enter_intrinsic()) {
vmIntrinsics::ID iid = method->intrinsic_id();
intptr_t start = (intptr_t)__ pc();
int vep_offset = ((intptr_t)__ pc()) - start;
int vep_offset = 0;
int exception_offset = 0;
int frame_complete = 0;
int stack_slots = 0;
OopMapSet* oop_maps = new OopMapSet();
int interpreted_entry_offset = -1;
gen_continuation_enter(masm,
method,
in_sig_bt,
@ -1181,7 +1224,9 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
exception_offset,
oop_maps,
frame_complete,
stack_slots);
stack_slots,
interpreted_entry_offset,
vep_offset);
__ flush();
nmethod* nm = nmethod::new_native_nmethod(method,
compile_id,
@ -1193,7 +1238,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
in_ByteSize(-1),
oop_maps,
exception_offset);
ContinuationEntry::set_enter_code(nm);
ContinuationEntry::set_enter_code(nm, interpreted_entry_offset);
return nm;
}

View file

@ -1276,7 +1276,9 @@ static void gen_continuation_enter(MacroAssembler* masm,
int& exception_offset,
OopMapSet* oop_maps,
int& frame_complete,
int& stack_slots) {
int& stack_slots,
int& interpreted_entry_offset,
int& compiled_entry_offset) {
// enterSpecial(Continuation c, boolean isContinue, boolean isVirtualThread)
int pos_cont_obj = 0;
@ -1298,8 +1300,68 @@ static void gen_continuation_enter(MacroAssembler* masm,
// Utility methods kill rax, make sure there are no collisions
assert_different_registers(rax, reg_cont_obj, reg_is_cont, reg_is_virtual);
AddressLiteral resolve(SharedRuntime::get_resolve_static_call_stub(),
relocInfo::static_call_type);
address start = __ pc();
Label L_thaw, L_exit;
// i2i entry used at interp_only_mode only
interpreted_entry_offset = __ pc() - start;
{
#ifdef ASSERT
Label is_interp_only;
__ cmpb(Address(r15_thread, JavaThread::interp_only_mode_offset()), 0);
__ jcc(Assembler::notEqual, is_interp_only);
__ stop("enterSpecial interpreter entry called when not in interp_only_mode");
__ bind(is_interp_only);
#endif
__ pop(rax); // return address
// Read interpreter arguments into registers (this is an ad-hoc i2c adapter)
__ movptr(c_rarg1, Address(rsp, Interpreter::stackElementSize*2));
__ movl(c_rarg2, Address(rsp, Interpreter::stackElementSize*1));
__ movl(c_rarg3, Address(rsp, Interpreter::stackElementSize*0));
__ andptr(rsp, -16); // Ensure compiled code always sees stack at proper alignment
__ push(rax); // return address
__ push_cont_fastpath();
__ enter();
stack_slots = 2; // will be adjusted in setup
OopMap* map = continuation_enter_setup(masm, stack_slots);
// The frame is complete here, but we only record it for the compiled entry, so the frame would appear unsafe,
// but that's okay because at the very worst we'll miss an async sample, but we're in interp_only_mode anyway.
__ verify_oop(reg_cont_obj);
fill_continuation_entry(masm, reg_cont_obj, reg_is_virtual);
// If continuation, call to thaw. Otherwise, resolve the call and exit.
__ testptr(reg_is_cont, reg_is_cont);
__ jcc(Assembler::notZero, L_thaw);
// --- Resolve path
// Make sure the call is patchable
__ align(BytesPerWord, __ offset() + NativeCall::displacement_offset);
// Emit stub for static call
CodeBuffer* cbuf = masm->code_section()->outer();
address stub = CompiledStaticCall::emit_to_interp_stub(*cbuf, __ pc());
if (stub == nullptr) {
fatal("CodeCache is full at gen_continuation_enter");
}
__ call(resolve);
oop_maps->add_gc_map(__ pc() - start, map);
__ post_call_nop();
__ jmp(L_exit);
}
// compiled entry
__ align(CodeEntryAlignment);
compiled_entry_offset = __ pc() - start;
__ enter();
stack_slots = 2; // will be adjusted in setup
@ -1312,8 +1374,6 @@ static void gen_continuation_enter(MacroAssembler* masm,
fill_continuation_entry(masm, reg_cont_obj, reg_is_virtual);
Label L_thaw, L_exit;
// If isContinue, call to thaw. Otherwise, call Continuation.enter(Continuation c, boolean isContinue)
__ testptr(reg_is_cont, reg_is_cont);
__ jccb(Assembler::notZero, L_thaw);
@ -1334,8 +1394,6 @@ static void gen_continuation_enter(MacroAssembler* masm,
// SharedRuntime::find_callee_info_helper() which calls
// LinkResolver::resolve_continuation_enter() which resolves the call to
// Continuation.enter(Continuation c, boolean isContinue).
AddressLiteral resolve(SharedRuntime::get_resolve_static_call_stub(),
relocInfo::static_call_type);
__ call(resolve);
oop_maps->add_gc_map(__ pc() - start, map);
@ -1474,17 +1532,20 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
if (method->is_continuation_enter_intrinsic()) {
vmIntrinsics::ID iid = method->intrinsic_id();
intptr_t start = (intptr_t)__ pc();
int vep_offset = ((intptr_t)__ pc()) - start;
int vep_offset = 0;
int exception_offset = 0;
int frame_complete = 0;
int stack_slots = 0;
OopMapSet* oop_maps = new OopMapSet();
int interpreted_entry_offset = -1;
gen_continuation_enter(masm,
in_regs,
exception_offset,
oop_maps,
frame_complete,
stack_slots);
stack_slots,
interpreted_entry_offset,
vep_offset);
__ flush();
nmethod* nm = nmethod::new_native_nmethod(method,
compile_id,
@ -1496,7 +1557,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
in_ByteSize(-1),
oop_maps,
exception_offset);
ContinuationEntry::set_enter_code(nm);
ContinuationEntry::set_enter_code(nm, interpreted_entry_offset);
return nm;
}

View file

@ -39,6 +39,7 @@
#include "oops/method.inline.hpp"
#include "oops/oop.inline.hpp"
#include "oops/symbol.hpp"
#include "runtime/continuationEntry.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/icache.hpp"
#include "runtime/safepoint.hpp"
@ -653,6 +654,13 @@ void CompiledStaticCall::compute_entry(const methodHandle& m, bool caller_is_nme
}
}
void CompiledStaticCall::compute_entry_for_continuation_entry(const methodHandle& m, StaticCallInfo& info) {
if (ContinuationEntry::is_interpreted_call(instruction_address())) {
info._to_interpreter = true;
info._entry = m()->get_c2i_entry();
}
}
address CompiledDirectStaticCall::find_stub_for(address instruction) {
// Find reloc. information containing this call-site
RelocIterator iter((nmethod*)NULL, instruction);

View file

@ -345,6 +345,7 @@ class CompiledStaticCall : public ResourceObj {
// Compute entry point given a method
static void compute_entry(const methodHandle& m, bool caller_is_nmethod, StaticCallInfo& info);
void compute_entry_for_continuation_entry(const methodHandle& m, StaticCallInfo& info);
public:
// Clean static call (will force resolving on next use)

View file

@ -61,6 +61,7 @@
#include "prims/methodHandles.hpp"
#include "runtime/arguments.hpp"
#include "runtime/atomic.hpp"
#include "runtime/continuationEntry.hpp"
#include "runtime/frame.inline.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/init.hpp"
@ -1328,7 +1329,7 @@ void Method::set_code(const methodHandle& mh, CompiledMethod *code) {
assert(mh->_from_interpreted_entry == NULL, "initialized incorrectly"); // see link_method
// This is the entry used when we're in interpreter-only mode; see InterpreterMacroAssembler::jump_from_interpreted
mh->_i2i_entry = mh->get_i2c_entry();
mh->_i2i_entry = ContinuationEntry::interpreted_entry();
// This must come last, as it is what's tested in LinkResolver::resolve_static_call
Atomic::release_store(&mh->_from_interpreted_entry , mh->get_i2c_entry());
} else if (!mh->is_method_handle_intrinsic()) {

View file

@ -23,6 +23,7 @@
*/
#include "precompiled.hpp"
#include "code/compiledIC.hpp"
#include "code/nmethod.hpp"
#include "runtime/continuation.hpp"
#include "runtime/continuationEntry.inline.hpp"
@ -34,10 +35,32 @@
int ContinuationEntry::_return_pc_offset = 0;
address ContinuationEntry::_return_pc = nullptr;
CompiledMethod* ContinuationEntry::_enter_special = nullptr;
int ContinuationEntry::_interpreted_entry_offset = 0;
void ContinuationEntry::set_enter_code(CompiledMethod* cm) {
void ContinuationEntry::set_enter_code(CompiledMethod* cm, int interpreted_entry_offset) {
assert(_return_pc_offset != 0, "");
_return_pc = cm->code_begin() + _return_pc_offset;
_enter_special = cm;
_interpreted_entry_offset = interpreted_entry_offset;
assert(_enter_special->code_contains(compiled_entry()), "entry not in enterSpecial");
assert(_enter_special->code_contains(interpreted_entry()), "entry not in enterSpecial");
assert(interpreted_entry() < compiled_entry(), "unexpected code layout");
}
address ContinuationEntry::compiled_entry() {
return _enter_special->verified_entry_point();
}
address ContinuationEntry::interpreted_entry() {
return _enter_special->code_begin() + _interpreted_entry_offset;
}
bool ContinuationEntry::is_interpreted_call(address call_address) {
assert(_enter_special->code_contains(call_address), "call not in enterSpecial");
assert(call_address >= interpreted_entry(), "unexpected location");
return call_address < compiled_entry();
}
ContinuationEntry* ContinuationEntry::from_frame(const frame& f) {

View file

@ -53,10 +53,13 @@ public:
public:
static int _return_pc_offset; // friend gen_continuation_enter
static void set_enter_code(CompiledMethod* nm); // friend SharedRuntime::generate_native_wrapper
static void set_enter_code(CompiledMethod* cm, int interpreted_entry_offset);
static bool is_interpreted_call(address call_address);
private:
static address _return_pc;
static CompiledMethod* _enter_special;
static int _interpreted_entry_offset;
private:
ContinuationEntry* _parent;
@ -90,6 +93,11 @@ public:
intptr_t* entry_sp() const { return (intptr_t*)this; }
intptr_t* entry_fp() const;
static address compiled_entry();
static address interpreted_entry();
static CompiledMethod* enter_special() { return _enter_special; }
int argsize() const { return _argsize; }
void set_argsize(int value) { _argsize = value; }

View file

@ -1343,6 +1343,9 @@ bool SharedRuntime::resolve_sub_helper_internal(methodHandle callee_method, cons
return true; // skip patching for JVMCI
}
CompiledStaticCall* ssc = caller_nm->compiledStaticCall_before(caller_frame.pc());
if (is_nmethod && caller_nm->method()->is_continuation_enter_intrinsic()) {
ssc->compute_entry_for_continuation_entry(callee_method, static_call_info);
}
if (ssc->is_clean()) ssc->set(static_call_info);
}
}
@ -1557,10 +1560,32 @@ JRT_END
// resolve a static call and patch code
JRT_BLOCK_ENTRY(address, SharedRuntime::resolve_static_call_C(JavaThread* current ))
methodHandle callee_method;
bool enter_special = false;
JRT_BLOCK
callee_method = SharedRuntime::resolve_helper(false, false, CHECK_NULL);
current->set_vm_result_2(callee_method());
if (current->is_interp_only_mode()) {
RegisterMap reg_map(current, false);
frame stub_frame = current->last_frame();
assert(stub_frame.is_runtime_frame(), "must be a runtimeStub");
frame caller = stub_frame.sender(&reg_map);
enter_special = caller.cb() != NULL && caller.cb()->is_compiled()
&& caller.cb()->as_compiled_method()->method()->is_continuation_enter_intrinsic();
}
JRT_BLOCK_END
if (current->is_interp_only_mode() && enter_special) {
// enterSpecial is compiled and calls this method to resolve the call to Continuation::enter
// but in interp_only_mode we need to go to the interpreted entry
// The c2i won't patch in this mode -- see fixup_callers_callsite
//
// This should probably be done in all cases, not just enterSpecial (see JDK-8218403),
// but that's part of a larger fix, and the situation is worse for enterSpecial, as it has no
// interpreted version.
return callee_method->get_c2i_entry();
}
// return compiled code entry point after potential safepoints
assert(callee_method->verified_code_entry() != NULL, " Jump to zero!");
return callee_method->verified_code_entry();
@ -1991,6 +2016,9 @@ JRT_LEAF(void, SharedRuntime::fixup_callers_callsite(Method* method, address cal
// Get the return PC for the passed caller PC.
address return_pc = caller_pc + frame::pc_return_offset;
assert(!JavaThread::current()->is_interp_only_mode() || !nm->method()->is_continuation_enter_intrinsic()
|| ContinuationEntry::is_interpreted_call(return_pc), "interp_only_mode but not in enterSpecial interpreted entry");
// There is a benign race here. We could be attempting to patch to a compiled
// entry point at the same time the callee is being deoptimized. If that is
// the case then entry_point may in fact point to a c2i and we'd patch the
@ -2027,6 +2055,13 @@ JRT_LEAF(void, SharedRuntime::fixup_callers_callsite(Method* method, address cal
typ != relocInfo::static_stub_type) {
return;
}
if (nm->method()->is_continuation_enter_intrinsic()) {
assert(ContinuationEntry::is_interpreted_call(call->instruction_address()) == JavaThread::current()->is_interp_only_mode(),
"mode: %d", JavaThread::current()->is_interp_only_mode());
if (ContinuationEntry::is_interpreted_call(call->instruction_address())) {
return;
}
}
address destination = call->destination();
if (should_fixup_call_destination(destination, entry_point, caller_pc, moop, cb)) {
call->set_destination_mt_safe(entry_point);
@ -3056,7 +3091,7 @@ void AdapterHandlerLibrary::create_native_wrapper(const methodHandle& method) {
CodeBuffer buffer(buf);
if (method->is_continuation_enter_intrinsic()) {
buffer.initialize_stubs_size(64);
buffer.initialize_stubs_size(128);
}
struct { double data[20]; } locs_buf;

View file

@ -120,12 +120,15 @@ abstract non-sealed class AbstractLayout implements MemoryLayout {
return this instanceof PaddingLayout;
}
// the following methods have to copy the same Javadoc as in MemoryLayout, or subclasses will just show
// the Object methods javadoc
/**
* {@return the hash code value for this layout}
*/
@Override
public int hashCode() {
return name.hashCode() << Long.hashCode(alignment);
return Objects.hash(name, size, alignment);
}
/**
@ -134,28 +137,27 @@ abstract non-sealed class AbstractLayout implements MemoryLayout {
* the same kind, have the same size, name and alignment constraints. Furthermore, depending on the layout kind, additional
* conditions must be satisfied:
* <ul>
* <li>two value layouts are considered equal if they have the same byte order (see {@link ValueLayout#order()})</li>
* <li>two value layouts are considered equal if they have the same {@linkplain ValueLayout#order() order},
* and {@linkplain ValueLayout#carrier() carrier}</li>
* <li>two sequence layouts are considered equal if they have the same element count (see {@link SequenceLayout#elementCount()}), and
* if their element layouts (see {@link SequenceLayout#elementLayout()}) are also equal</li>
* <li>two group layouts are considered equal if they are of the same kind (see {@link GroupLayout#isStruct()},
* {@link GroupLayout#isUnion()}) and if their member layouts (see {@link GroupLayout#memberLayouts()}) are also equal</li>
* </ul>
*
* @param that the object to be compared for equality with this layout.
* @param other the object to be compared for equality with this layout.
* @return {@code true} if the specified object is equal to this layout.
*/
@Override
public boolean equals(Object that) {
if (this == that) {
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(that instanceof AbstractLayout)) {
return false;
}
return Objects.equals(name, ((AbstractLayout) that).name) &&
Objects.equals(alignment, ((AbstractLayout) that).alignment);
return other instanceof AbstractLayout otherLayout &&
name.equals(otherLayout.name) &&
size == otherLayout.size &&
alignment == otherLayout.alignment;
}
/**

View file

@ -143,10 +143,9 @@ public final class GroupLayout extends AbstractLayout implements MemoryLayout {
if (!super.equals(other)) {
return false;
}
if (!(other instanceof GroupLayout g)) {
return false;
}
return kind.equals(g.kind) && elements.equals(g.elements);
return other instanceof GroupLayout otherGroup &&
kind == otherGroup.kind &&
elements.equals(otherGroup.elements);
}
/**

View file

@ -584,17 +584,18 @@ public sealed interface MemoryLayout permits AbstractLayout, SequenceLayout, Gro
* the same kind, have the same size, name and alignment constraints. Furthermore, depending on the layout kind, additional
* conditions must be satisfied:
* <ul>
* <li>two value layouts are considered equal if they have the same byte order (see {@link ValueLayout#order()})</li>
* <li>two value layouts are considered equal if they have the same {@linkplain ValueLayout#order() order},
* and {@linkplain ValueLayout#carrier() carrier}</li>
* <li>two sequence layouts are considered equal if they have the same element count (see {@link SequenceLayout#elementCount()}), and
* if their element layouts (see {@link SequenceLayout#elementLayout()}) are also equal</li>
* <li>two group layouts are considered equal if they are of the same kind (see {@link GroupLayout#isStruct()},
* {@link GroupLayout#isUnion()}) and if their member layouts (see {@link GroupLayout#memberLayouts()}) are also equal</li>
* </ul>
*
* @param that the object to be compared for equality with this layout.
* @param other the object to be compared for equality with this layout.
* @return {@code true} if the specified object is equal to this layout.
*/
boolean equals(Object that);
boolean equals(Object other);
/**
* {@return the hash code value for this layout}

View file

@ -124,7 +124,6 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
* @param elementCounts an array of element counts, of which at most one can be {@code -1}.
* @return a sequence layout where element layouts in the flattened projection of this
* sequence layout (see {@link #flatten()}) are re-arranged into one or more nested sequence layouts.
* @throws UnsupportedOperationException if this sequence layout does not have an element count.
* @throws IllegalArgumentException if two or more element counts are set to {@code -1}, or if one
* or more element count is {@code <= 0} (but other than {@code -1}) or, if, after any required inference,
* multiplying the element counts does not yield the same element count as the flattened projection of this
@ -187,8 +186,6 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
* }
* @return a sequence layout with the same size as this layout (but, possibly, with different
* element count), whose element layout is not a sequence layout.
* @throws UnsupportedOperationException if this sequence layout, or one of the nested sequence layouts being
* flattened, does not have an element count.
*/
public SequenceLayout flatten() {
long count = elementCount();
@ -214,10 +211,9 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
if (!super.equals(other)) {
return false;
}
if (!(other instanceof SequenceLayout s)) {
return false;
}
return elemCount == s.elemCount && elementLayout.equals(s.elementLayout);
return other instanceof SequenceLayout otherSeq &&
elemCount == otherSeq.elemCount &&
elementLayout.equals(otherSeq.elementLayout);
}
@Override

View file

@ -93,6 +93,9 @@ public sealed class ValueLayout extends AbstractLayout implements MemoryLayout {
return new ValueLayout(carrier, Objects.requireNonNull(order), bitSize(), alignment, name());
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
char descriptor = carrier == MemoryAddress.class ? 'A' : carrier.descriptorString().charAt(0);
@ -102,6 +105,9 @@ public sealed class ValueLayout extends AbstractLayout implements MemoryLayout {
return decorateLayoutString(String.format("%s%d", descriptor, bitSize()));
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object other) {
if (this == other) {
@ -110,13 +116,9 @@ public sealed class ValueLayout extends AbstractLayout implements MemoryLayout {
if (!super.equals(other)) {
return false;
}
if (!(other instanceof ValueLayout v)) {
return false;
}
return carrier.equals(v.carrier) &&
order.equals(v.order) &&
bitSize() == v.bitSize() &&
alignment == v.alignment;
return other instanceof ValueLayout otherValue &&
carrier.equals(otherValue.carrier) &&
order.equals(otherValue.order);
}
/**
@ -171,7 +173,7 @@ public sealed class ValueLayout extends AbstractLayout implements MemoryLayout {
* @return a var handle which can be used to dereference a multi-dimensional array, featuring {@code shape.length + 1}
* {@code long} coordinates.
* @throws IllegalArgumentException if {@code shape[i] < 0}, for at least one index {@code i}.
* @throws UnsupportedOperationException if the layout path has one or more elements with incompatible alignment constraints.
* @throws UnsupportedOperationException if {@code bitAlignment() > bitSize()}.
* @see MethodHandles#memorySegmentViewVarHandle
* @see MemoryLayout#varHandle(PathElement...)
* @see SequenceLayout
@ -198,9 +200,12 @@ public sealed class ValueLayout extends AbstractLayout implements MemoryLayout {
return carrier;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), order, bitSize(), alignment);
return Objects.hash(super.hashCode(), order, carrier);
}
@Override

View file

@ -740,14 +740,9 @@ public interface Map<K, V> {
* is not supported by this map's entry set iterator.
* @throws ClassCastException if the class of a replacement value
* prevents it from being stored in this map
* @throws NullPointerException if the specified function is null, or the
* specified replacement value is null, and this map does not permit null
* values
* @throws ClassCastException if a replacement value is of an inappropriate
* type for this map
* (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if function or a replacement value is null,
* and this map does not permit null keys or values
* @throws NullPointerException if the specified function is null, or if a
* replacement value is null and this map does not permit null values
* (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
* @throws IllegalArgumentException if some property of a replacement value
* prevents it from being stored in this map

View file

@ -137,7 +137,7 @@ import java.util.Stack;
* @see com.sun.java_cup.internal.runtime.virtual_parse_stack
* @author Frank Flannery
*
* @LastModified: June 2022
* @LastModified: July 2022
*/
public abstract class lr_parser {
@ -150,6 +150,10 @@ public abstract class lr_parser {
private int opCount = 0;
private int totalOpCount = 0;
private int lastSym;
private boolean overLimit = false;
public int grpLimit = 0;
public int opLimit = 0;
public int totalOpLimit = 0;
/*-----------------------------------------------------------*/
/*--- Constructor(s) ----------------------------------------*/
@ -376,11 +380,13 @@ public abstract class lr_parser {
grpCount++;
}
opCount++; // function
totalOpCount++;
isLiteral = false;
} else if (contains(sym.OPERATORS, s.sym)) {
// axis nodetest is counted as one step, so not counted if last=DCOLON
if (lastSym != sym.DCOLON) {
opCount++;
totalOpCount++;
}
isLiteral = false;
}
@ -390,6 +396,16 @@ public abstract class lr_parser {
}
lastSym = s.sym;
/*
* Sets the overLimit status as soon as the count of operators is over the
* limit, which in turn triggers the XPathParser to report an error.
*/
if (grpLimit > 0 && grpCount > grpLimit
|| opLimit > 0 && opCount > opLimit
|| totalOpLimit > 0 && totalOpCount > totalOpLimit) {
overLimit = true;
}
return s;
}
@ -591,12 +607,14 @@ public abstract class lr_parser {
/* do user initialization */
user_init();
isLiteral = false;
overLimit = false;
grpCount = 0;
opCount = 0;
lastSym = -1;
/* get the first token */
cur_token = scan();
if (overLimit) return null;
/* push dummy Symbol with start state to get us underway */
stack.removeAllElements();
@ -671,12 +689,16 @@ public abstract class lr_parser {
lhs_sym = stack.peek();
}
}
if (overLimit) return null;
}
totalOpCount += opCount;
return lhs_sym;
}
public boolean isOverLimit() {
return overLimit;
}
/**
* Returns the count of operators in XPath expressions.
*

View file

@ -43,12 +43,9 @@ import jdk.xml.internal.XMLSecurityManager.Limit;
* CUP v0.11b generated parser.
* This class was generated by CUP v0.11b on Nov 12, 2019.
*
* @LastModified: Jan 2022
* @LastModified: July 2022
*/
public class XPathParser extends lr_parser {
private int grpLimit = 0;
private int opLimit = 0;
private int totalOpLimit = 0;
/**
* Default constructor.
@ -1118,6 +1115,13 @@ public class XPathParser extends lr_parser {
_expression = expression;
_lineNumber = lineNumber;
Symbol s = super.parse();
/*
* While the Java CUP parser is used for parsing symbols, the error
* report mechanism has so far been kept within the Xalan implementation.
* An error, i.e. the count of operators is over the limit, is
* therefore handled here.
*/
if (isOverLimit()) {
int grpCount = getCount(ID_GROUP);
int opCount = getCount(ID_OPERATOR);
int totalOpCount = getCount(ID_TOTAL_OPERATOR);
@ -1142,6 +1146,7 @@ public class XPathParser extends lr_parser {
new ErrorMsg(errCode, lineNumber, params));
throw new RuntimeException(ErrorMsg.XPATH_LIMIT);
}
}
return s;
} catch (IllegalCharException e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.ILLEGAL_CHAR_ERR,

View file

@ -1694,9 +1694,7 @@ public class Attr extends JCTree.Visitor {
// Attribute all cases and
// check that there are no duplicate case labels or default clauses.
Set<Object> labels = new HashSet<>(); // The set of case labels.
List<Type> coveredTypesForPatterns = List.nil();
List<Type> coveredTypesForConstants = List.nil();
Set<Object> constants = new HashSet<>(); // The set of case constants.
boolean hasDefault = false; // Is there a default label?
boolean hasUnconditionalPattern = false; // Is there a unconditional pattern?
boolean lastPatternErroneous = false; // Has the last pattern erroneous type?
@ -1732,10 +1730,8 @@ public class Attr extends JCTree.Visitor {
Symbol sym = enumConstant(expr, seltype);
if (sym == null) {
log.error(expr.pos(), Errors.EnumLabelMustBeUnqualifiedEnum);
} else if (!labels.add(sym)) {
} else if (!constants.add(sym)) {
log.error(label.pos(), Errors.DuplicateCaseLabel);
} else {
checkCaseLabelDominated(label.pos(), coveredTypesForConstants, sym.type);
}
} else if (errorEnumSwitch) {
//error recovery: the selector is erroneous, and all the case labels
@ -1765,10 +1761,8 @@ public class Attr extends JCTree.Visitor {
}
} else if (!stringSwitch && !types.isAssignable(seltype, syms.intType)) {
log.error(label.pos(), Errors.ConstantLabelNotCompatible(pattype, seltype));
} else if (!labels.add(pattype.constValue())) {
} else if (!constants.add(pattype.constValue())) {
log.error(c.pos(), Errors.DuplicateCaseLabel);
} else {
checkCaseLabelDominated(label.pos(), coveredTypesForConstants, types.boxedTypeOrType(pattype));
}
}
}
@ -1820,13 +1814,6 @@ public class Attr extends JCTree.Visitor {
hasUnconditionalPattern = true;
}
lastPatternErroneous = patternType.isErroneous();
checkCaseLabelDominated(pat.pos(), coveredTypesForPatterns, patternType);
if (!patternType.isErroneous()) {
coveredTypesForConstants = coveredTypesForConstants.prepend(patternType);
if (unguarded) {
coveredTypesForPatterns = coveredTypesForPatterns.prepend(patternType);
}
}
} else {
Assert.error();
}
@ -1850,6 +1837,7 @@ public class Attr extends JCTree.Visitor {
}
if (patternSwitch) {
chk.checkSwitchCaseStructure(cases);
chk.checkSwitchCaseLabelDominated(cases);
}
if (switchTree.hasTag(SWITCH)) {
((JCSwitch) switchTree).hasUnconditionalPattern =
@ -1875,14 +1863,6 @@ public class Attr extends JCTree.Visitor {
switchScope.enter(((JCVariableDecl) stat).sym);
}
}
private void checkCaseLabelDominated(DiagnosticPosition pos,
List<Type> coveredTypes, Type patternType) {
for (Type existing : coveredTypes) {
if (types.isSubtype(patternType, existing)) {
log.error(pos, Errors.PatternDominated);
}
}
}
// where
/** Return the selected enumeration constant symbol, or null. */
private Symbol enumConstant(JCTree tree, Type enumType) {

View file

@ -4376,6 +4376,93 @@ public class Check {
}
}
void checkSwitchCaseLabelDominated(List<JCCase> cases) {
List<JCCaseLabel> caseLabels = List.nil();
for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
JCCase c = l.head;
for (JCCaseLabel label : c.labels) {
if (label.hasTag(DEFAULTCASELABEL) || TreeInfo.isNullCaseLabel(label)) {
continue;
}
Type currentType = labelType(label);
for (JCCaseLabel testCaseLabel : caseLabels) {
Type testType = labelType(testCaseLabel);
if (types.isSubtype(currentType, testType) &&
!currentType.hasTag(ERROR) && !testType.hasTag(ERROR)) {
//the current label is potentially dominated by the existing (test) label, check:
boolean dominated = false;
if (label instanceof JCConstantCaseLabel) {
dominated |= !(testCaseLabel instanceof JCConstantCaseLabel);
} else if (label instanceof JCPatternCaseLabel patternCL &&
testCaseLabel instanceof JCPatternCaseLabel testPatternCaseLabel &&
TreeInfo.unguardedCaseLabel(testCaseLabel)) {
dominated = patternDominated(testPatternCaseLabel.pat,
patternCL.pat);
}
if (dominated) {
log.error(label.pos(), Errors.PatternDominated);
}
}
}
caseLabels = caseLabels.prepend(label);
}
}
}
//where:
private Type labelType(JCCaseLabel label) {
return types.erasure(switch (label.getTag()) {
case PATTERNCASELABEL -> ((JCPatternCaseLabel) label).pat.type;
case CONSTANTCASELABEL -> types.boxedTypeOrType(((JCConstantCaseLabel) label).expr.type);
default -> throw Assert.error("Unexpected tree kind: " + label.getTag());
});
}
private boolean patternDominated(JCPattern existingPattern, JCPattern currentPattern) {
Type existingPatternType = types.erasure(existingPattern.type);
Type currentPatternType = types.erasure(currentPattern.type);
if (existingPatternType.isPrimitive() ^ currentPatternType.isPrimitive()) {
return false;
}
if (existingPatternType.isPrimitive()) {
return types.isSameType(existingPatternType, currentPatternType);
} else {
if (!types.isSubtype(currentPatternType, existingPatternType)) {
return false;
}
}
while (existingPattern instanceof JCParenthesizedPattern parenthesized) {
existingPattern = parenthesized.pattern;
}
while (currentPattern instanceof JCParenthesizedPattern parenthesized) {
currentPattern = parenthesized.pattern;
}
if (currentPattern instanceof JCBindingPattern) {
return existingPattern instanceof JCBindingPattern;
} else if (currentPattern instanceof JCRecordPattern currentRecordPattern) {
if (existingPattern instanceof JCBindingPattern) {
return true;
} else if (existingPattern instanceof JCRecordPattern existingRecordPattern) {
List<JCPattern> existingNested = existingRecordPattern.nested;
List<JCPattern> currentNested = currentRecordPattern.nested;
if (existingNested.size() != currentNested.size()) {
return false;
}
while (existingNested.nonEmpty()) {
if (!patternDominated(existingNested.head, currentNested.head)) {
return false;
}
existingNested = existingNested.tail;
currentNested = currentNested.tail;
}
return true;
} else {
Assert.error("Unknown pattern: " + existingPattern.getTag());
}
} else {
Assert.error("Unknown pattern: " + currentPattern.getTag());
}
return false;
}
/** check if a type is a subtype of Externalizable, if that is available. */
boolean isExternalizable(Type t) {
try {

View file

@ -25,6 +25,7 @@
package jdk.javadoc.internal.doclets.toolkit.taglets;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
@ -32,7 +33,6 @@ import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.lang.model.element.Element;
@ -160,22 +160,21 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
var utils = writer.configuration().utils;
Content result = writer.getOutputInstance();
var documentedInThisCall = new HashSet<String>();
for (Entry<ThrowsTree, ExecutableElement> entry : throwsTags.entrySet()) {
Element e = entry.getValue();
Map<ThrowsTree, ExecutableElement> flattenedExceptions = flatten(throwsTags, writer);
flattenedExceptions.forEach((ThrowsTree t, ExecutableElement e) -> {
var ch = utils.getCommentHelper(e);
ThrowsTree tag = entry.getKey();
Element te = ch.getException(tag);
String excName = tag.getExceptionName().toString();
Element te = ch.getException(t);
String excName = t.getExceptionName().toString();
TypeMirror substituteType = typeSubstitutions.get(excName);
if (alreadyDocumented.contains(excName)
|| (te != null && alreadyDocumented.contains(utils.getFullyQualifiedName(te, false)))
|| (substituteType != null && alreadyDocumented.contains(substituteType.toString()))) {
continue;
return;
}
if (alreadyDocumented.isEmpty() && documentedInThisCall.isEmpty()) {
result.add(writer.getThrowsHeader());
}
result.add(writer.throwsTagOutput(e, tag, substituteType));
result.add(writer.throwsTagOutput(e, t, substituteType));
if (substituteType != null) {
documentedInThisCall.add(substituteType.toString());
} else {
@ -183,11 +182,57 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
? utils.getFullyQualifiedName(te, false)
: excName);
}
}
});
alreadyDocumented.addAll(documentedInThisCall);
return result;
}
/*
* A single @throws tag from an overriding method can correspond to multiple
* @throws tags from an overridden method.
*/
private Map<ThrowsTree, ExecutableElement> flatten(Map<ThrowsTree, ExecutableElement> throwsTags,
TagletWriter writer) {
Map<ThrowsTree, ExecutableElement> result = new LinkedHashMap<>();
throwsTags.forEach((tag, taggedElement) -> {
var expandedTags = expand(tag, taggedElement, writer);
assert Collections.disjoint(result.entrySet(), expandedTags.entrySet());
result.putAll(expandedTags);
});
return result;
}
private Map<ThrowsTree, ExecutableElement> expand(ThrowsTree tag,
ExecutableElement e,
TagletWriter writer) {
// This method uses Map.of() to create maps of size zero and one.
// While such maps are effectively ordered, the syntax is more
// compact than that of LinkedHashMap.
// peek into @throws description
if (tag.getDescription().stream().noneMatch(d -> d.getKind() == DocTree.Kind.INHERIT_DOC)) {
// nothing to inherit
return Map.of(tag, e);
}
var input = new DocFinder.Input(writer.configuration().utils, e, this, new DocFinder.DocTreeInfo(tag, e), false, true);
var output = DocFinder.search(writer.configuration(), input);
if (output.tagList.size() <= 1) {
// outer code will handle this trivial case of inheritance
return Map.of(tag, e);
}
if (tag.getDescription().size() > 1) {
// there's more to description than just {@inheritDoc}
// it's likely a documentation error
var ch = writer.configuration().utils.getCommentHelper(e);
writer.configuration().getMessages().error(ch.getDocTreePath(tag), "doclet.inheritDocWithinInappropriateTag");
return Map.of();
}
Map<ThrowsTree, ExecutableElement> tags = new LinkedHashMap<>();
output.tagList.forEach(t -> tags.put((ThrowsTree) t, (ExecutableElement) output.holder));
return tags;
}
/**
* Inherit throws documentation for exceptions that were declared but not
* documented.

View file

@ -38,5 +38,3 @@ vmTestbase/nsk/jvmti/scenarios/sampling/SP07/sp07t002/TestDescription.java 82456
vmTestbase/vm/mlvm/mixed/stress/regression/b6969574/INDIFY_Test.java 8265295 linux-x64,windows-x64
serviceability/jvmti/VMObjectAlloc/VMObjectAllocTest.java 8288430 generic-all
serviceability/jvmti/vthread/ContStackDepthTest/ContStackDepthTest.java 8288949 generic-all

View file

@ -68,6 +68,8 @@ vmTestbase/nsk/jvmti/GetObjectMonitorUsage/objmonusage006/TestDescription.java
####
## NSK JDWP Tests failing with wrapper
vmTestbase/nsk/jdwp/ThreadReference/ForceEarlyReturn/forceEarlyReturn002/forceEarlyReturn002.java 8286789 generic-all
##########
## NSK JDB Tests failing with wrapper

View file

@ -44,3 +44,5 @@ serviceability/sa/TestJmapCoreMetaspace.java 8268722,8268636
serviceability/sa/TestJhsdbJstackMixed.java 8248912 generic-all
serviceability/sa/ClhsdbPstack.java#process 8248912 generic-all
serviceability/sa/ClhsdbPstack.java#core 8248912 generic-all
vmTestbase/gc/gctests/MemoryEaterMT/MemoryEaterMT.java 8289582 windows-x64

View file

@ -753,6 +753,7 @@ jdk/jfr/startupargs/TestStartName.java 8214685 windows-
jdk/jfr/startupargs/TestStartDuration.java 8214685 windows-x64
jdk/jfr/jvm/TestWaste.java 8282427 generic-all
jdk/jfr/api/consumer/recordingstream/TestOnEvent.java 8255404 linux-x64
jdk/jfr/event/runtime/TestActiveSettingEvent.java 8287832 generic-all
############################################################################

View file

@ -23,7 +23,7 @@
/*
* @test
* @bug 8067757
* @bug 8067757 6509045
* @library /tools/lib ../../lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
@ -350,4 +350,218 @@ public class TestOneToMany extends JavadocTester {
<dd><code><a href="MySubException.html" title="class in x">MySubException</a></code> - if that</dd>
</dl>""");
}
@Test
public void testUncheckedExceptionTag(Path base) throws Exception {
var src = base.resolve("src");
tb.writeJavaFiles(src, """
package x;
public class MyRuntimeException extends RuntimeException { }
""", """
package x;
public interface I {
/**
* @throws MyRuntimeException if this
* @throws MyRuntimeException if that
*/
void m();
}
""", """
package x;
public interface I1 extends I {
/**
* @throws MyRuntimeException {@inheritDoc}
*/
@Override
void m();
}
""", """
package x;
public class IImpl implements I {
/**
* @throws MyRuntimeException {@inheritDoc}
*/
@Override
public void m() { }
}
""", """
package x;
public class C {
/**
* @throws MyRuntimeException if this
* @throws MyRuntimeException if that
*/
public void m();
}
""", """
package x;
public class C1 extends C {
/**
* @throws MyRuntimeException {@inheritDoc}
*/
@Override
public void m() { }
}
""");
javadoc("-d", base.resolve("out").toString(),
"-sourcepath", src.toString(),
"x");
checkExit(Exit.OK);
checkOutput("x/IImpl.html", true, """
<dl class="notes">
<dt>Specified by:</dt>
<dd><code><a href="I.html#m()">m</a></code>&nbsp;in interface&nbsp;<code><a href="I.html" title="interface in x">I</a></code></dd>
<dt>Throws:</dt>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if this</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if that</dd>
</dl>""");
checkOutput("x/I1.html", true, """
<dl class="notes">
<dt>Specified by:</dt>
<dd><code><a href="I.html#m()">m</a></code>&nbsp;in interface&nbsp;<code><a href="I.html" title="interface in x">I</a></code></dd>
<dt>Throws:</dt>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if this</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if that</dd>
</dl>""");
checkOutput("x/C1.html", true, """
<dl class="notes">
<dt>Overrides:</dt>
<dd><code><a href="C.html#m()">m</a></code>&nbsp;in class&nbsp;<code><a href="C.html" title="class in x">C</a></code></dd>
<dt>Throws:</dt>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if this</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if that</dd>
</dl>""");
}
@Test
public void testWholeShebang(Path base) throws Exception {
var src = base.resolve("src");
tb.writeJavaFiles(src, """
package x;
public class MyRuntimeException extends RuntimeException { }
""", """
package x;
public interface I {
/**
* @throws MyRuntimeException always
*/
void m();
}
""", """
package x;
public interface I1 extends I {
/**
* @throws MyRuntimeException sometimes
* @throws MyRuntimeException rarely
* @throws MyRuntimeException "{@inheritDoc}"
*/
@Override
void m();
}
""", """
package x;
public interface I2 extends I1 {
/**
* @throws MyRuntimeException occasionally
* @throws MyRuntimeException {@inheritDoc}
* @throws MyRuntimeException frequently
*/
@Override
void m() throws MyRuntimeException,
MyRuntimeException,
MyRuntimeException,
MyRuntimeException;
}
""");
javadoc("-d", base.resolve("out").toString(),
"-sourcepath", src.toString(),
"x");
checkExit(Exit.OK);
checkOutput("x/I.html", true, """
<dl class="notes">
<dt>Throws:</dt>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - always</dd>
</dl>""");
checkOutput("x/I1.html", true, """
<dl class="notes">
<dt>Specified by:</dt>
<dd><code><a href="I.html#m()">m</a></code>&nbsp;in interface&nbsp;<code><a href="I.html" title="interface in x">I</a></code></dd>
<dt>Throws:</dt>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - sometimes</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - rarely</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - "always"</dd>
</dl>""");
checkOutput("x/I2.html", true, """
<dl class="notes">
<dt>Specified by:</dt>
<dd><code><a href="I.html#m()">m</a></code>&nbsp;in interface&nbsp;<code><a href="I.html" title="interface in x">I</a></code></dd>
<dt>Specified by:</dt>
<dd><code><a href="I1.html#m()">m</a></code>&nbsp;in interface&nbsp;<code><a href="I1.html" title="interface in x">I1</a></code></dd>
<dt>Throws:</dt>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - occasionally</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - sometimes</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - rarely</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - "always"</dd>
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - frequently</dd>
</dl>""");
}
@Test
public void testError(Path base) throws Exception {
var src = base.resolve("src");
tb.writeJavaFiles(src, """
package x;
public class MyRuntimeException extends RuntimeException { }
""", """
package x;
public interface I {
/**
* @throws MyRuntimeException sometimes
* @throws MyRuntimeException rarely
*/
void m();
}
""", """
package x;
public interface I1 extends I {
/**
* @throws MyRuntimeException "{@inheritDoc}"
*/
@Override
void m();
}
""");
javadoc("-d", base.resolve("out").toString(),
"-sourcepath", src.toString(),
"x");
checkExit(Exit.ERROR);
checkOutput(Output.OUT, true, """
I1.java:6: error: @inheritDoc cannot be used within this tag
* @throws MyRuntimeException "{@inheritDoc}"
^
""");
}
}

View file

@ -136,4 +136,79 @@ public class Domination {
}
}
int testRecordPatternsDominated1() {
record R(int a) {}
Object o = null;
switch (o) {
case R r: return 1;
case R(int a): return -1;
}
}
int testRecordPatternsDominated2() {
record R(int a) {}
Object o = null;
switch (o) {
case R(int a): return 1;
case R(int a): return -1;
}
}
int testRecordPatternsDominated3() {
record R(int a) {}
Object o = null;
switch (o) {
case R r when guard(): return 1;
case R(int a): return -1;
}
}
int testRecordPatternsDominated4() {
record R(int a) {}
Object o = null;
switch (o) {
case R(int a) when guard(): return 1;
case R(int a): return -1;
}
}
boolean guard() {
return false;
}
int testRecordPatternsDominated5() {
record R(int a) {}
Object o = null;
switch (o) {
case ((R r)): return 1;
case ((R(int a))): return -1;
}
}
int testRecordPatternsDominated6() {
record R(int a) {}
Object o = null;
switch (o) {
case ((R(int a))): return 1;
case ((R(int a))): return -1;
}
}
int testRecordPatternsDominated7() {
record R(int a) {}
Object o = null;
switch (o) {
case R r when true: return 1;
case R(int a): return -1;
}
}
int testRecordPatternsDominated8() {
record R(int a) {}
Object o = null;
switch (o) {
case R(int a) when true: return 1;
case R(int a): return -1;
}
}
}

View file

@ -10,6 +10,12 @@ Domination.java:102:18: compiler.err.pattern.dominated
Domination.java:113:18: compiler.err.pattern.dominated
Domination.java:124:18: compiler.err.pattern.dominated
Domination.java:135:18: compiler.err.pattern.dominated
Domination.java:144:18: compiler.err.pattern.dominated
Domination.java:153:18: compiler.err.pattern.dominated
Domination.java:184:18: compiler.err.pattern.dominated
Domination.java:193:18: compiler.err.pattern.dominated
Domination.java:202:18: compiler.err.pattern.dominated
Domination.java:211:18: compiler.err.pattern.dominated
- compiler.note.preview.filename: Domination.java, DEFAULT
- compiler.note.preview.recompile
12 errors
18 errors

View file

@ -32,11 +32,11 @@ SwitchErrors.java:172:18: compiler.err.pattern.expected
SwitchErrors.java:178:78: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
SwitchErrors.java:184:73: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
SwitchErrors.java:191:21: compiler.err.flows.through.to.pattern
SwitchErrors.java:200:44: compiler.err.pattern.dominated
SwitchErrors.java:200:44: compiler.err.flows.through.from.pattern
SwitchErrors.java:218:29: compiler.err.unconditional.pattern.and.default
SwitchErrors.java:225:21: compiler.err.flows.through.to.pattern
SwitchErrors.java:225:47: compiler.err.flows.through.from.pattern
SwitchErrors.java:232:44: compiler.err.pattern.dominated
SwitchErrors.java:232:44: compiler.err.flows.through.from.pattern
SwitchErrors.java:232:47: compiler.err.flows.through.from.pattern
SwitchErrors.java:244:18: compiler.err.duplicate.unconditional.pattern
SwitchErrors.java:249:18: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, java.lang.Integer)