# HG changeset patch # User tschatzl # Date 1574846482 -3600 # Node ID c887851da33d0f6bc48d9d109945af843748ead7 # Parent 3b1afeb49a20cb02206cbe25762b04bf975f1ba7# Parent c04fa10636fd35d7abfc899da1d0fda39c015052 Merge diff -r 3b1afeb49a20 -r c887851da33d make/autoconf/flags-cflags.m4 --- a/make/autoconf/flags-cflags.m4 Wed Nov 27 10:19:45 2019 +0100 +++ b/make/autoconf/flags-cflags.m4 Wed Nov 27 10:21:22 2019 +0100 @@ -190,20 +190,7 @@ WARNINGS_ENABLE_ALL_CXXFLAGS="$WARNINGS_ENABLE_ALL_CFLAGS $WARNINGS_ENABLE_ADDITIONAL_CXX" DISABLED_WARNINGS="unused-parameter unused" - - # Repeate the check for the BUILD_CC and BUILD_CXX. Need to also reset - # CFLAGS since any target specific flags will likely not work with the - # build compiler - CC_OLD="$CC" - CXX_OLD="$CXX" - CC="$BUILD_CC" - CXX="$BUILD_CXX" - CFLAGS_OLD="$CFLAGS" - CFLAGS="" BUILD_CC_DISABLE_WARNING_PREFIX="-Wno-" - CC="$CC_OLD" - CXX="$CXX_OLD" - CFLAGS="$CFLAGS_OLD" ;; clang) @@ -420,6 +407,17 @@ FLAGS_SETUP_CFLAGS_CPU_DEP([TARGET]) + # Repeat the check for the BUILD_CC and BUILD_CXX. Need to also reset CFLAGS + # since any target specific flags will likely not work with the build compiler. + CC_OLD="$CC" + CXX_OLD="$CXX" + CFLAGS_OLD="$CFLAGS" + CXXFLAGS_OLD="$CXXFLAGS" + CC="$BUILD_CC" + CXX="$BUILD_CXX" + CFLAGS="" + CXXFLAGS="" + FLAGS_OS=$OPENJDK_BUILD_OS FLAGS_OS_TYPE=$OPENJDK_BUILD_OS_TYPE FLAGS_CPU=$OPENJDK_BUILD_CPU @@ -430,6 +428,11 @@ FLAGS_CPU_LEGACY_LIB=$OPENJDK_BUILD_CPU_LEGACY_LIB FLAGS_SETUP_CFLAGS_CPU_DEP([BUILD], [OPENJDK_BUILD_], [BUILD_]) + + CC="$CC_OLD" + CXX="$CXX_OLD" + CFLAGS="$CFLAGS_OLD" + CXXFLAGS="$CXXFLAGS_OLD" ]) ################################################################################ @@ -529,6 +532,11 @@ if test "x$TOOLCHAIN_TYPE" = xgcc; then TOOLCHAIN_CFLAGS_JVM="$TOOLCHAIN_CFLAGS_JVM -fcheck-new -fstack-protector" TOOLCHAIN_CFLAGS_JDK="-pipe -fstack-protector" + # reduce lib size on s390x in link step, this needs also special compile flags + if test "x$OPENJDK_TARGET_CPU" = xs390x; then + TOOLCHAIN_CFLAGS_JVM="$TOOLCHAIN_CFLAGS_JVM -ffunction-sections -fdata-sections" + TOOLCHAIN_CFLAGS_JDK="$TOOLCHAIN_CFLAGS_JDK -ffunction-sections -fdata-sections" + fi # technically NOT for CXX (but since this gives *worse* performance, use # no-strict-aliasing everywhere!) TOOLCHAIN_CFLAGS_JDK_CONLY="-fno-strict-aliasing" diff -r 3b1afeb49a20 -r c887851da33d make/autoconf/flags-ldflags.m4 --- a/make/autoconf/flags-ldflags.m4 Wed Nov 27 10:19:45 2019 +0100 +++ b/make/autoconf/flags-ldflags.m4 Wed Nov 27 10:21:22 2019 +0100 @@ -70,10 +70,14 @@ fi # Add -z defs, to forbid undefined symbols in object files. - BASIC_LDFLAGS="$BASIC_LDFLAGS -Wl,-z,defs" + # add relro (mark relocations read only) for all libs + BASIC_LDFLAGS="$BASIC_LDFLAGS -Wl,-z,defs -Wl,-z,relro" + # s390x : remove unused code+data in link step + if test "x$OPENJDK_TARGET_CPU" = xs390x; then + BASIC_LDFLAGS="$BASIC_LDFLAGS -Wl,--gc-sections -Wl,--print-gc-sections" + fi - BASIC_LDFLAGS_JVM_ONLY="-Wl,-O1 -Wl,-z,relro" - + BASIC_LDFLAGS_JVM_ONLY="-Wl,-O1" elif test "x$TOOLCHAIN_TYPE" = xclang; then BASIC_LDFLAGS_JVM_ONLY="-mno-omit-leaf-frame-pointer -mstack-alignment=16 \ @@ -120,9 +124,6 @@ if test "x$OPENJDK_TARGET_OS" = xlinux; then if test x$DEBUG_LEVEL = xrelease; then DEBUGLEVEL_LDFLAGS_JDK_ONLY="$DEBUGLEVEL_LDFLAGS_JDK_ONLY -Wl,-O1" - else - # mark relocations read only on (fast/slow) debug builds - DEBUGLEVEL_LDFLAGS_JDK_ONLY="-Wl,-z,relro" fi if test x$DEBUG_LEVEL = xslowdebug; then # do relocations at load diff -r 3b1afeb49a20 -r c887851da33d src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp --- a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp Wed Nov 27 10:19:45 2019 +0100 +++ b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp Wed Nov 27 10:21:22 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -327,24 +327,42 @@ #endif } +#ifdef _LP64 void BarrierSetAssembler::nmethod_entry_barrier(MacroAssembler* masm) { BarrierSetNMethod* bs_nm = BarrierSet::barrier_set()->barrier_set_nmethod(); if (bs_nm == NULL) { return; } -#ifndef _LP64 - ShouldNotReachHere(); -#else Label continuation; - Register thread = LP64_ONLY(r15_thread); + Register thread = r15_thread; Address disarmed_addr(thread, in_bytes(bs_nm->thread_disarmed_offset())); __ align(8); __ cmpl(disarmed_addr, 0); __ jcc(Assembler::equal, continuation); __ call(RuntimeAddress(StubRoutines::x86::method_entry_barrier())); __ bind(continuation); +} +#else +void BarrierSetAssembler::nmethod_entry_barrier(MacroAssembler* masm) { + BarrierSetNMethod* bs_nm = BarrierSet::barrier_set()->barrier_set_nmethod(); + if (bs_nm == NULL) { + return; + } + + Label continuation; + + Register tmp = rdi; + __ push(tmp); + __ movptr(tmp, (intptr_t)bs_nm->disarmed_value_address()); + Address disarmed_addr(tmp, 0); + __ align(4); + __ cmpl(disarmed_addr, 0); + __ pop(tmp); + __ jcc(Assembler::equal, continuation); + __ call(RuntimeAddress(StubRoutines::x86::method_entry_barrier())); + __ bind(continuation); +} #endif -} void BarrierSetAssembler::c2i_entry_barrier(MacroAssembler* masm) { BarrierSetNMethod* bs = BarrierSet::barrier_set()->barrier_set_nmethod(); diff -r 3b1afeb49a20 -r c887851da33d src/hotspot/cpu/x86/gc/shared/barrierSetNMethod_x86.cpp --- a/src/hotspot/cpu/x86/gc/shared/barrierSetNMethod_x86.cpp Wed Nov 27 10:19:45 2019 +0100 +++ b/src/hotspot/cpu/x86/gc/shared/barrierSetNMethod_x86.cpp Wed Nov 27 10:21:22 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,7 @@ class NativeNMethodCmpBarrier: public NativeInstruction { public: +#ifdef _LP64 enum Intel_specific_constants { instruction_code = 0x81, instruction_size = 8, @@ -42,6 +43,14 @@ instruction_rex_prefix = Assembler::REX | Assembler::REX_B, instruction_modrm = 0x7f // [r15 + offset] }; +#else + enum Intel_specific_constants { + instruction_code = 0x81, + instruction_size = 7, + imm_offset = 2, + instruction_modrm = 0x3f // [rdi] + }; +#endif address instruction_address() const { return addr_at(0); } address immediate_address() const { return addr_at(imm_offset); } @@ -51,6 +60,7 @@ void verify() const; }; +#ifdef _LP64 void NativeNMethodCmpBarrier::verify() const { if (((uintptr_t) instruction_address()) & 0x7) { fatal("Not properly aligned"); @@ -77,6 +87,27 @@ fatal("not a cmp barrier"); } } +#else +void NativeNMethodCmpBarrier::verify() const { + if (((uintptr_t) instruction_address()) & 0x3) { + fatal("Not properly aligned"); + } + + int inst = ubyte_at(0); + if (inst != instruction_code) { + tty->print_cr("Addr: " INTPTR_FORMAT " Code: 0x%x", p2i(instruction_address()), + inst); + fatal("not a cmp barrier"); + } + + int modrm = ubyte_at(1); + if (modrm != instruction_modrm) { + tty->print_cr("Addr: " INTPTR_FORMAT " mod/rm: 0x%x", p2i(instruction_address()), + modrm); + fatal("not a cmp barrier"); + } +} +#endif // _LP64 void BarrierSetNMethod::deoptimize(nmethod* nm, address* return_address_ptr) { /* @@ -127,7 +158,7 @@ // NativeNMethodCmpBarrier::verify() will immediately complain when it does // not find the expected native instruction at this offset, which needs updating. // Note that this offset is invariant of PreserveFramePointer. -static const int entry_barrier_offset = -19; +static const int entry_barrier_offset = LP64_ONLY(-19) NOT_LP64(-18); static NativeNMethodCmpBarrier* native_nmethod_barrier(nmethod* nm) { address barrier_address = nm->code_begin() + nm->frame_complete_offset() + entry_barrier_offset; diff -r 3b1afeb49a20 -r c887851da33d src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp Wed Nov 27 10:19:45 2019 +0100 +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp Wed Nov 27 10:21:22 2019 +0100 @@ -511,6 +511,7 @@ // 3: apply keep-alive barrier if needed if (ShenandoahBarrierSet::need_keep_alive_barrier(decorators, type)) { + __ push_IU_state(); const Register thread = NOT_LP64(tmp_thread) LP64_ONLY(r15_thread); assert_different_registers(dst, tmp1, tmp_thread); NOT_LP64(__ get_thread(thread)); @@ -523,6 +524,7 @@ tmp1 /* tmp */, true /* tosca_live */, true /* expand_call */); + __ pop_IU_state(); } } diff -r 3b1afeb49a20 -r c887851da33d src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp --- a/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp Wed Nov 27 10:19:45 2019 +0100 +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp Wed Nov 27 10:21:22 2019 +0100 @@ -975,6 +975,9 @@ address c2i_entry = __ pc(); + BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); + bs->c2i_entry_barrier(masm); + gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup); __ flush(); @@ -1886,6 +1889,10 @@ // -2 because return address is already present and so is saved rbp __ subptr(rsp, stack_size - 2*wordSize); + + BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); + bs->nmethod_entry_barrier(masm); + // Frame is now completed as far as size and linkage. int frame_complete = ((intptr_t)__ pc()) - start; @@ -1921,12 +1928,12 @@ // if we load it once it is usable thru the entire wrapper const Register thread = rdi; - // We use rsi as the oop handle for the receiver/klass - // It is callee save so it survives the call to native - - const Register oop_handle_reg = rsi; - - __ get_thread(thread); + // We use rsi as the oop handle for the receiver/klass + // It is callee save so it survives the call to native + + const Register oop_handle_reg = rsi; + + __ get_thread(thread); if (is_critical_native && !Universe::heap()->supports_object_pinning()) { check_needs_gc_for_critical_native(masm, thread, stack_slots, total_c_args, total_in_args, diff -r 3b1afeb49a20 -r c887851da33d src/hotspot/cpu/x86/stubGenerator_x86_32.cpp --- a/src/hotspot/cpu/x86/stubGenerator_x86_32.cpp Wed Nov 27 10:19:45 2019 +0100 +++ b/src/hotspot/cpu/x86/stubGenerator_x86_32.cpp Wed Nov 27 10:21:22 2019 +0100 @@ -27,6 +27,7 @@ #include "asm/macroAssembler.inline.hpp" #include "gc/shared/barrierSet.hpp" #include "gc/shared/barrierSetAssembler.hpp" +#include "gc/shared/barrierSetNMethod.hpp" #include "interpreter/interpreter.hpp" #include "memory/universe.hpp" #include "nativeInst_x86.hpp" @@ -3663,6 +3664,68 @@ __ ret(0); } + address generate_method_entry_barrier() { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "nmethod_entry_barrier"); + + Label deoptimize_label; + + address start = __ pc(); + + __ push(-1); // cookie, this is used for writing the new rsp when deoptimizing + + BLOCK_COMMENT("Entry:"); + __ enter(); // save rbp + + // save rbx, because we want to use that value. + // We could do without it but then we depend on the number of slots used by pusha + __ push(rbx); + + __ lea(rbx, Address(rsp, wordSize * 3)); // 1 for cookie, 1 for rbp, 1 for rbx - this should be the return address + + __ pusha(); + + // xmm0 and xmm1 may be used for passing float/double arguments + const int xmm_size = wordSize * 2; + const int xmm_spill_size = xmm_size * 2; + __ subptr(rsp, xmm_spill_size); + __ movdqu(Address(rsp, xmm_size * 1), xmm1); + __ movdqu(Address(rsp, xmm_size * 0), xmm0); + + __ call_VM_leaf(CAST_FROM_FN_PTR(address, static_cast(BarrierSetNMethod::nmethod_stub_entry_barrier)), rbx); + + __ movdqu(xmm0, Address(rsp, xmm_size * 0)); + __ movdqu(xmm1, Address(rsp, xmm_size * 1)); + __ addptr(rsp, xmm_spill_size); + + __ cmpl(rax, 1); // 1 means deoptimize + __ jcc(Assembler::equal, deoptimize_label); + + __ popa(); + __ pop(rbx); + + __ leave(); + + __ addptr(rsp, 1 * wordSize); // cookie + __ ret(0); + + __ BIND(deoptimize_label); + + __ popa(); + __ pop(rbx); + + __ leave(); + + // this can be taken out, but is good for verification purposes. getting a SIGSEGV + // here while still having a correct stack is valuable + __ testptr(rsp, Address(rsp, 0)); + + __ movptr(rsp, Address(rsp, 0)); // new rsp was written in the barrier + __ jmp(Address(rsp, -1 * wordSize)); // jmp target should be callers verified_entry_point + + return start; + } + public: // Information about frame layout at time of blocking runtime call. // Note that we only have to preserve callee-saved registers since @@ -3959,6 +4022,11 @@ StubRoutines::_safefetchN_entry = StubRoutines::_safefetch32_entry; StubRoutines::_safefetchN_fault_pc = StubRoutines::_safefetch32_fault_pc; StubRoutines::_safefetchN_continuation_pc = StubRoutines::_safefetch32_continuation_pc; + + BarrierSetNMethod* bs_nm = BarrierSet::barrier_set()->barrier_set_nmethod(); + if (bs_nm != NULL) { + StubRoutines::x86::_method_entry_barrier = generate_method_entry_barrier(); + } } diff -r 3b1afeb49a20 -r c887851da33d src/hotspot/cpu/x86/stubRoutines_x86.hpp --- a/src/hotspot/cpu/x86/stubRoutines_x86.hpp Wed Nov 27 10:19:45 2019 +0100 +++ b/src/hotspot/cpu/x86/stubRoutines_x86.hpp Wed Nov 27 10:21:22 2019 +0100 @@ -55,14 +55,8 @@ static address _double_sign_mask; static address _double_sign_flip; - static address _method_entry_barrier; - public: - static address method_entry_barrier() { - return _method_entry_barrier; - } - static address get_previous_fp_entry() { return _get_previous_fp_entry; } @@ -121,6 +115,8 @@ //shuffle mask for big-endian 128-bit integers static address _counter_shuffle_mask_addr; + static address _method_entry_barrier; + // masks and table for CRC32 static uint64_t _crc_by128_masks[]; static juint _crc_table[]; @@ -221,6 +217,7 @@ static address upper_word_mask_addr() { return _upper_word_mask_addr; } static address shuffle_byte_flip_mask_addr() { return _shuffle_byte_flip_mask_addr; } static address k256_addr() { return _k256_adr; } + static address method_entry_barrier() { return _method_entry_barrier; } static address vector_short_to_byte_mask() { return _vector_short_to_byte_mask; diff -r 3b1afeb49a20 -r c887851da33d src/hotspot/cpu/x86/stubRoutines_x86_32.cpp --- a/src/hotspot/cpu/x86/stubRoutines_x86_32.cpp Wed Nov 27 10:19:45 2019 +0100 +++ b/src/hotspot/cpu/x86/stubRoutines_x86_32.cpp Wed Nov 27 10:21:22 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,3 +32,5 @@ // a description of how to extend it, see the stubRoutines.hpp file. address StubRoutines::x86::_verify_fpu_cntrl_wrd_entry = NULL; +address StubRoutines::x86::_method_entry_barrier = NULL; + diff -r 3b1afeb49a20 -r c887851da33d src/hotspot/cpu/x86/vm_version_x86.cpp --- a/src/hotspot/cpu/x86/vm_version_x86.cpp Wed Nov 27 10:19:45 2019 +0100 +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp Wed Nov 27 10:21:22 2019 +0100 @@ -367,26 +367,29 @@ // intx saved_useavx = UseAVX; intx saved_usesse = UseSSE; - // check _cpuid_info.sef_cpuid7_ebx.bits.avx512f - __ lea(rsi, Address(rbp, in_bytes(VM_Version::sef_cpuid7_offset()))); - __ movl(rax, 0x10000); - __ andl(rax, Address(rsi, 4)); // xcr0 bits sse | ymm - __ cmpl(rax, 0x10000); - __ jccb(Assembler::notEqual, legacy_setup); // jump if EVEX is not supported - // check _cpuid_info.xem_xcr0_eax.bits.opmask - // check _cpuid_info.xem_xcr0_eax.bits.zmm512 - // check _cpuid_info.xem_xcr0_eax.bits.zmm32 - __ movl(rax, 0xE0); - __ andl(rax, Address(rbp, in_bytes(VM_Version::xem_xcr0_offset()))); // xcr0 bits sse | ymm - __ cmpl(rax, 0xE0); - __ jccb(Assembler::notEqual, legacy_setup); // jump if EVEX is not supported - __ lea(rsi, Address(rbp, in_bytes(VM_Version::std_cpuid1_offset()))); - __ movl(rax, Address(rsi, 0)); - __ cmpl(rax, 0x50654); // If it is Skylake - __ jcc(Assembler::equal, legacy_setup); // If UseAVX is unitialized or is set by the user to include EVEX if (use_evex) { + // check _cpuid_info.sef_cpuid7_ebx.bits.avx512f + __ lea(rsi, Address(rbp, in_bytes(VM_Version::sef_cpuid7_offset()))); + __ movl(rax, 0x10000); + __ andl(rax, Address(rsi, 4)); // xcr0 bits sse | ymm + __ cmpl(rax, 0x10000); + __ jccb(Assembler::notEqual, legacy_setup); // jump if EVEX is not supported + // check _cpuid_info.xem_xcr0_eax.bits.opmask + // check _cpuid_info.xem_xcr0_eax.bits.zmm512 + // check _cpuid_info.xem_xcr0_eax.bits.zmm32 + __ movl(rax, 0xE0); + __ andl(rax, Address(rbp, in_bytes(VM_Version::xem_xcr0_offset()))); // xcr0 bits sse | ymm + __ cmpl(rax, 0xE0); + __ jccb(Assembler::notEqual, legacy_setup); // jump if EVEX is not supported + + if (FLAG_IS_DEFAULT(UseAVX)) { + __ lea(rsi, Address(rbp, in_bytes(VM_Version::std_cpuid1_offset()))); + __ movl(rax, Address(rsi, 0)); + __ cmpl(rax, 0x50654); // If it is Skylake + __ jcc(Assembler::equal, legacy_setup); + } // EVEX setup: run in lowest evex mode VM_Version::set_evex_cpuFeatures(); // Enable temporary to pass asserts UseAVX = 3; @@ -455,27 +458,28 @@ VM_Version::set_cpuinfo_cont_addr(__ pc()); // Returns here after signal. Save xmm0 to check it later. - // check _cpuid_info.sef_cpuid7_ebx.bits.avx512f - __ lea(rsi, Address(rbp, in_bytes(VM_Version::sef_cpuid7_offset()))); - __ movl(rax, 0x10000); - __ andl(rax, Address(rsi, 4)); - __ cmpl(rax, 0x10000); - __ jcc(Assembler::notEqual, legacy_save_restore); - // check _cpuid_info.xem_xcr0_eax.bits.opmask - // check _cpuid_info.xem_xcr0_eax.bits.zmm512 - // check _cpuid_info.xem_xcr0_eax.bits.zmm32 - __ movl(rax, 0xE0); - __ andl(rax, Address(rbp, in_bytes(VM_Version::xem_xcr0_offset()))); // xcr0 bits sse | ymm - __ cmpl(rax, 0xE0); - __ jcc(Assembler::notEqual, legacy_save_restore); - - __ lea(rsi, Address(rbp, in_bytes(VM_Version::std_cpuid1_offset()))); - __ movl(rax, Address(rsi, 0)); - __ cmpl(rax, 0x50654); // If it is Skylake - __ jcc(Assembler::equal, legacy_save_restore); - // If UseAVX is unitialized or is set by the user to include EVEX if (use_evex) { + // check _cpuid_info.sef_cpuid7_ebx.bits.avx512f + __ lea(rsi, Address(rbp, in_bytes(VM_Version::sef_cpuid7_offset()))); + __ movl(rax, 0x10000); + __ andl(rax, Address(rsi, 4)); + __ cmpl(rax, 0x10000); + __ jcc(Assembler::notEqual, legacy_save_restore); + // check _cpuid_info.xem_xcr0_eax.bits.opmask + // check _cpuid_info.xem_xcr0_eax.bits.zmm512 + // check _cpuid_info.xem_xcr0_eax.bits.zmm32 + __ movl(rax, 0xE0); + __ andl(rax, Address(rbp, in_bytes(VM_Version::xem_xcr0_offset()))); // xcr0 bits sse | ymm + __ cmpl(rax, 0xE0); + __ jcc(Assembler::notEqual, legacy_save_restore); + + if (FLAG_IS_DEFAULT(UseAVX)) { + __ lea(rsi, Address(rbp, in_bytes(VM_Version::std_cpuid1_offset()))); + __ movl(rax, Address(rsi, 0)); + __ cmpl(rax, 0x50654); // If it is Skylake + __ jcc(Assembler::equal, legacy_save_restore); + } // EVEX check: run in lowest evex mode VM_Version::set_evex_cpuFeatures(); // Enable temporary to pass asserts UseAVX = 3; diff -r 3b1afeb49a20 -r c887851da33d src/hotspot/cpu/x86/x86_32.ad --- a/src/hotspot/cpu/x86/x86_32.ad Wed Nov 27 10:19:45 2019 +0100 +++ b/src/hotspot/cpu/x86/x86_32.ad Wed Nov 27 10:21:22 2019 +0100 @@ -3917,6 +3917,13 @@ interface(REG_INTER); %} +operand eDXRegP(eRegP reg) %{ + constraint(ALLOC_IN_RC(edx_reg)); + match(reg); + format %{ "EDX" %} + interface(REG_INTER); +%} + operand eSIRegP(eRegP reg) %{ constraint(ALLOC_IN_RC(esi_reg)); match(reg); @@ -8977,7 +8984,7 @@ %} ins_pipe(ialu_reg_reg); -%} +%} //----------Long Instructions------------------------------------------------ // Add Long Register with Register diff -r 3b1afeb49a20 -r c887851da33d src/hotspot/cpu/x86/x86_64.ad --- a/src/hotspot/cpu/x86/x86_64.ad Wed Nov 27 10:19:45 2019 +0100 +++ b/src/hotspot/cpu/x86/x86_64.ad Wed Nov 27 10:21:22 2019 +0100 @@ -267,6 +267,9 @@ // Singleton class for RSI pointer register reg_class ptr_rsi_reg(RSI, RSI_H); +// Singleton class for RBP pointer register +reg_class ptr_rbp_reg(RBP, RBP_H); + // Singleton class for RDI pointer register reg_class ptr_rdi_reg(RDI, RDI_H); @@ -3530,6 +3533,16 @@ interface(REG_INTER); %} +operand rbp_RegP() +%{ + constraint(ALLOC_IN_RC(ptr_rbp_reg)); + match(RegP); + match(rRegP); + + format %{ %} + interface(REG_INTER); +%} + // Used in rep stosq operand rdi_RegP() %{ diff -r 3b1afeb49a20 -r c887851da33d src/hotspot/share/adlc/adlparse.cpp --- a/src/hotspot/share/adlc/adlparse.cpp Wed Nov 27 10:19:45 2019 +0100 +++ b/src/hotspot/share/adlc/adlparse.cpp Wed Nov 27 10:21:22 2019 +0100 @@ -123,6 +123,7 @@ parse_err(SEMERR, "Did not declare 'register' definitions"); } regBlock->addSpillRegClass(); + regBlock->addDynamicRegClass(); // Done with parsing, check consistency. diff -r 3b1afeb49a20 -r c887851da33d src/hotspot/share/adlc/archDesc.cpp --- a/src/hotspot/share/adlc/archDesc.cpp Wed Nov 27 10:19:45 2019 +0100 +++ b/src/hotspot/share/adlc/archDesc.cpp Wed Nov 27 10:21:22 2019 +0100 @@ -245,12 +245,12 @@ // Construct chain rules build_chain_rule(op); - MatchRule &mrule = *op->_matrule; - Predicate *pred = op->_predicate; + MatchRule *mrule = op->_matrule; + Predicate *pred = op->_predicate; // Grab the machine type of the operand const char *rootOp = op->_ident; - mrule._machType = rootOp; + mrule->_machType = rootOp; // Check for special cases if (strcmp(rootOp,"Universe")==0) continue; @@ -271,10 +271,13 @@ // Find result type for match. const char *result = op->reduce_result(); - bool has_root = false; - // Construct a MatchList for this entry - buildMatchList(op->_matrule, result, rootOp, pred, cost); + // Construct a MatchList for this entry. + // Iterate over the list to enumerate all match cases for operands with multiple match rules. + for (; mrule != NULL; mrule = mrule->_next) { + mrule->_machType = rootOp; + buildMatchList(mrule, result, rootOp, pred, cost); + } } } @@ -805,6 +808,8 @@ return "RegMask::Empty"; } else if (strcmp(reg_class_name,"stack_slots")==0) { return "(Compile::current()->FIRST_STACK_mask())"; + } else if (strcmp(reg_class_name, "dynamic")==0) { + return "*_opnds[0]->in_RegMask(0)"; } else { char *rc_name = toUpper(reg_class_name); const char *mask = "_mask"; @@ -867,7 +872,7 @@ } // Instructions producing 'Universe' use RegMask::Empty - if( strcmp(result,"Universe")==0 ) { + if (strcmp(result,"Universe") == 0) { return "RegMask::Empty"; } diff -r 3b1afeb49a20 -r c887851da33d src/hotspot/share/adlc/formsopt.cpp --- a/src/hotspot/share/adlc/formsopt.cpp Wed Nov 27 10:19:45 2019 +0100 +++ b/src/hotspot/share/adlc/formsopt.cpp Wed Nov 27 10:21:22 2019 +0100 @@ -80,6 +80,15 @@ _regClass.Insert(rc_name,reg_class); } +// Called after parsing the Register block. Record the register class +// for operands which are overwritten after matching. +void RegisterForm::addDynamicRegClass() { + const char *rc_name = "dynamic"; + RegClass* reg_class = new RegClass(rc_name); + reg_class->set_stack_version(false); + _rclasses.addName(rc_name); + _regClass.Insert(rc_name,reg_class); +} // Provide iteration over all register definitions // in the order used by the register allocator diff -r 3b1afeb49a20 -r c887851da33d src/hotspot/share/adlc/formsopt.hpp --- a/src/hotspot/share/adlc/formsopt.hpp Wed Nov 27 10:19:45 2019 +0100 +++ b/src/hotspot/share/adlc/formsopt.hpp Wed Nov 27 10:21:22 2019 +0100 @@ -104,6 +104,7 @@ AllocClass *addAllocClass(char *allocName); void addSpillRegClass(); + void addDynamicRegClass(); // Provide iteration over all register definitions // in the order used by the register allocator diff -r 3b1afeb49a20 -r c887851da33d src/hotspot/share/adlc/output_c.cpp --- a/src/hotspot/share/adlc/output_c.cpp Wed Nov 27 10:19:45 2019 +0100 +++ b/src/hotspot/share/adlc/output_c.cpp Wed Nov 27 10:21:22 2019 +0100 @@ -2781,6 +2781,8 @@ // Return the sole RegMask. if (strcmp(first_reg_class, "stack_slots") == 0) { fprintf(fp," return &(Compile::current()->FIRST_STACK_mask());\n"); + } else if (strcmp(first_reg_class, "dynamic") == 0) { + fprintf(fp," return &RegMask::Empty;\n"); } else { const char* first_reg_class_to_upper = toUpper(first_reg_class); fprintf(fp," return &%s_mask();\n", first_reg_class_to_upper); diff -r 3b1afeb49a20 -r c887851da33d src/hotspot/share/gc/shared/barrierSetNMethod.cpp --- a/src/hotspot/share/gc/shared/barrierSetNMethod.cpp Wed Nov 27 10:19:45 2019 +0100 +++ b/src/hotspot/share/gc/shared/barrierSetNMethod.cpp Wed Nov 27 10:21:22 2019 +0100 @@ -32,9 +32,7 @@ #include "utilities/debug.hpp" int BarrierSetNMethod::disarmed_value() const { - char* disarmed_addr = reinterpret_cast(Thread::current()); - disarmed_addr += in_bytes(thread_disarmed_offset()); - return *reinterpret_cast(disarmed_addr); + return *disarmed_value_address(); } bool BarrierSetNMethod::supports_entry_barrier(nmethod* nm) { diff -r 3b1afeb49a20 -r c887851da33d src/hotspot/share/gc/shared/barrierSetNMethod.hpp --- a/src/hotspot/share/gc/shared/barrierSetNMethod.hpp Wed Nov 27 10:19:45 2019 +0100 +++ b/src/hotspot/share/gc/shared/barrierSetNMethod.hpp Wed Nov 27 10:21:22 2019 +0100 @@ -34,13 +34,14 @@ class BarrierSetNMethod: public CHeapObj { bool supports_entry_barrier(nmethod* nm); void deoptimize(nmethod* nm, address* return_addr_ptr); + int disarmed_value() const; protected: - virtual int disarmed_value() const; virtual bool nmethod_entry_barrier(nmethod* nm) = 0; public: virtual ByteSize thread_disarmed_offset() const = 0; + virtual int* disarmed_value_address() const = 0; static int nmethod_stub_entry_barrier(address* return_address_ptr); bool nmethod_osr_entry_barrier(nmethod* nm); diff -r 3b1afeb49a20 -r c887851da33d src/hotspot/share/gc/z/zBarrierSetNMethod.cpp --- a/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp Wed Nov 27 10:19:45 2019 +0100 +++ b/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp Wed Nov 27 10:21:22 2019 +0100 @@ -61,12 +61,10 @@ return true; } -int ZBarrierSetNMethod::disarmed_value() const { - // We override the default BarrierSetNMethod::disarmed_value() since - // this can be called by GC threads, which doesn't keep an up to date - // address_bad_mask. - const uintptr_t disarmed_addr = ((uintptr_t)&ZAddressBadMask) + ZNMethodDisarmedOffset; - return *((int*)disarmed_addr); +int* ZBarrierSetNMethod::disarmed_value_address() const { + const uintptr_t mask_addr = reinterpret_cast(&ZAddressBadMask); + const uintptr_t disarmed_addr = mask_addr + ZNMethodDisarmedOffset; + return reinterpret_cast(disarmed_addr); } ByteSize ZBarrierSetNMethod::thread_disarmed_offset() const { diff -r 3b1afeb49a20 -r c887851da33d src/hotspot/share/gc/z/zBarrierSetNMethod.hpp --- a/src/hotspot/share/gc/z/zBarrierSetNMethod.hpp Wed Nov 27 10:19:45 2019 +0100 +++ b/src/hotspot/share/gc/z/zBarrierSetNMethod.hpp Wed Nov 27 10:21:22 2019 +0100 @@ -31,11 +31,11 @@ class ZBarrierSetNMethod : public BarrierSetNMethod { protected: - virtual int disarmed_value() const; virtual bool nmethod_entry_barrier(nmethod* nm); public: virtual ByteSize thread_disarmed_offset() const; + virtual int* disarmed_value_address() const; }; #endif // SHARE_GC_Z_ZBARRIERSETNMETHOD_HPP diff -r 3b1afeb49a20 -r c887851da33d src/hotspot/share/memory/filemap.cpp --- a/src/hotspot/share/memory/filemap.cpp Wed Nov 27 10:19:45 2019 +0100 +++ b/src/hotspot/share/memory/filemap.cpp Wed Nov 27 10:21:22 2019 +0100 @@ -1385,14 +1385,6 @@ } - DEBUG_ONLY(if (addr_delta == 0 && ArchiveRelocationMode == 1) { - // This is for simulating mmap failures at the requested address. We do it here (instead - // of MetaspaceShared::map_archives) so we can thoroughly test the code for failure handling - // (releasing all allocated resource, etc). - log_info(cds)("ArchiveRelocationMode == 1: always map archive(s) at an alternative address"); - return MAP_ARCHIVE_MMAP_FAILURE; - }); - header()->set_mapped_base_address(header()->requested_base_address() + addr_delta); if (addr_delta != 0 && !relocate_pointers(addr_delta)) { return MAP_ARCHIVE_OTHER_FAILURE; @@ -1446,12 +1438,14 @@ MemTracker::record_virtual_memory_type((address)requested_addr, mtClassShared); } - if (MetaspaceShared::use_windows_memory_mapping() && addr_delta != 0) { + if (MetaspaceShared::use_windows_memory_mapping() && rs.is_reserved()) { // This is the second time we try to map the archive(s). We have already created a ReservedSpace // that covers all the FileMapRegions to ensure all regions can be mapped. However, Windows // can't mmap into a ReservedSpace, so we just os::read() the data. We're going to patch all the // regions anyway, so there's no benefit for mmap anyway. if (!read_region(i, requested_addr, size)) { + log_info(cds)("Failed to read %s shared space into reserved space at " INTPTR_FORMAT, + shared_region_name[i], p2i(requested_addr)); return MAP_ARCHIVE_OTHER_FAILURE; // oom or I/O error. } } else { @@ -1459,7 +1453,8 @@ requested_addr, size, si->read_only(), si->allow_exec()); if (base != requested_addr) { - log_info(cds)("Unable to map %s shared space at required address.", shared_region_name[i]); + log_info(cds)("Unable to map %s shared space at " INTPTR_FORMAT, + shared_region_name[i], p2i(requested_addr)); _memory_mapping_failed = true; return MAP_ARCHIVE_MMAP_FAILURE; } @@ -1468,7 +1463,7 @@ si->set_mapped_base(requested_addr); if (!rs.is_reserved()) { - // When mapping on Windows with (addr_delta == 0), we don't reserve the address space for the regions + // When mapping on Windows for the first attempt, we don't reserve the address space for the regions // (Windows can't mmap into a ReservedSpace). In this case, NMT requires we call it after // os::map_memory has succeeded. assert(MetaspaceShared::use_windows_memory_mapping(), "Windows memory mapping only"); diff -r 3b1afeb49a20 -r c887851da33d src/hotspot/share/memory/metaspaceShared.cpp --- a/src/hotspot/share/memory/metaspaceShared.cpp Wed Nov 27 10:19:45 2019 +0100 +++ b/src/hotspot/share/memory/metaspaceShared.cpp Wed Nov 27 10:21:22 2019 +0100 @@ -2147,6 +2147,19 @@ MapArchiveResult dynamic_result = (static_result == MAP_ARCHIVE_SUCCESS) ? map_archive(dynamic_mapinfo, mapped_base_address, archive_space_rs) : MAP_ARCHIVE_OTHER_FAILURE; + DEBUG_ONLY(if (ArchiveRelocationMode == 1 && use_requested_addr) { + // This is for simulating mmap failures at the requested address. In debug builds, we do it + // here (after all archives have possibly been mapped), so we can thoroughly test the code for + // failure handling (releasing all allocated resource, etc). + log_info(cds)("ArchiveRelocationMode == 1: always map archive(s) at an alternative address"); + if (static_result == MAP_ARCHIVE_SUCCESS) { + static_result = MAP_ARCHIVE_MMAP_FAILURE; + } + if (dynamic_result == MAP_ARCHIVE_SUCCESS) { + dynamic_result = MAP_ARCHIVE_MMAP_FAILURE; + } + }); + if (static_result == MAP_ARCHIVE_SUCCESS) { if (dynamic_result == MAP_ARCHIVE_SUCCESS) { result = MAP_ARCHIVE_SUCCESS; @@ -2298,7 +2311,7 @@ MapArchiveResult MetaspaceShared::map_archive(FileMapInfo* mapinfo, char* mapped_base_address, ReservedSpace rs) { assert(UseSharedSpaces, "must be runtime"); if (mapinfo == NULL) { - return MAP_ARCHIVE_SUCCESS; // no error has happeed -- trivially succeeded. + return MAP_ARCHIVE_SUCCESS; // The dynamic archive has not been specified. No error has happened -- trivially succeeded. } mapinfo->set_is_mapped(false); diff -r 3b1afeb49a20 -r c887851da33d src/hotspot/share/opto/parse3.cpp --- a/src/hotspot/share/opto/parse3.cpp Wed Nov 27 10:19:45 2019 +0100 +++ b/src/hotspot/share/opto/parse3.cpp Wed Nov 27 10:21:22 2019 +0100 @@ -55,8 +55,9 @@ return; } - // Deoptimize on putfield writes to call site target field. - if (!is_get && field->is_call_site_target()) { + // Deoptimize on putfield writes to call site target field outside of CallSite ctor. + if (!is_get && field->is_call_site_target() && + !(method()->holder() == field_holder && method()->is_object_initializer())) { uncommon_trap(Deoptimization::Reason_unhandled, Deoptimization::Action_reinterpret, NULL, "put to call site target field"); diff -r 3b1afeb49a20 -r c887851da33d src/java.base/share/classes/java/lang/invoke/CallSite.java --- a/src/java.base/share/classes/java/lang/invoke/CallSite.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/java.base/share/classes/java/lang/invoke/CallSite.java Wed Nov 27 10:21:22 2019 +0100 @@ -87,9 +87,10 @@ abstract public class CallSite { - // The actual payload of this call site: + // The actual payload of this call site. + // Can be modified using {@link MethodHandleNatives#setCallSiteTargetNormal} or {@link MethodHandleNatives#setCallSiteTargetVolatile}. /*package-private*/ - MethodHandle target; // Note: This field is known to the JVM. Do not change. + final MethodHandle target; // Note: This field is known to the JVM. /** * Make a blank call site object with the given method type. @@ -129,11 +130,11 @@ */ /*package-private*/ CallSite(MethodType targetType, MethodHandle createTargetHook) throws Throwable { - this(targetType); + this(targetType); // need to initialize target to make CallSite.type() work in createTargetHook ConstantCallSite selfCCS = (ConstantCallSite) this; MethodHandle boundTarget = (MethodHandle) createTargetHook.invokeWithArguments(selfCCS); - checkTargetChange(this.target, boundTarget); - this.target = boundTarget; + setTargetNormal(boundTarget); // ConstantCallSite doesn't publish CallSite.target + UNSAFE.storeStoreFence(); // barrier between target and isFrozen updates } /** @@ -190,11 +191,12 @@ */ public abstract void setTarget(MethodHandle newTarget); - void checkTargetChange(MethodHandle oldTarget, MethodHandle newTarget) { - MethodType oldType = oldTarget.type(); + private void checkTargetChange(MethodHandle newTarget) { + MethodType oldType = target.type(); // target is always present MethodType newType = newTarget.type(); // null check! - if (!newType.equals(oldType)) + if (newType != oldType) { throw wrongTargetType(newTarget, oldType); + } } private static WrongMethodTypeException wrongTargetType(MethodHandle target, MethodType type) { @@ -217,7 +219,7 @@ */ public abstract MethodHandle dynamicInvoker(); - /*non-public*/ + /*package-private*/ MethodHandle makeDynamicInvoker() { MethodHandle getTarget = getTargetHandle().bindArgumentL(0, this); MethodHandle invoker = MethodHandles.exactInvoker(this.type()); @@ -283,19 +285,24 @@ } /*package-private*/ - void setTargetNormal(MethodHandle newTarget) { + final void setTargetNormal(MethodHandle newTarget) { + checkTargetChange(newTarget); MethodHandleNatives.setCallSiteTargetNormal(this, newTarget); } + /*package-private*/ - MethodHandle getTargetVolatile() { + final MethodHandle getTargetVolatile() { return (MethodHandle) UNSAFE.getReferenceVolatile(this, getTargetOffset()); } + /*package-private*/ - void setTargetVolatile(MethodHandle newTarget) { + final void setTargetVolatile(MethodHandle newTarget) { + checkTargetChange(newTarget); MethodHandleNatives.setCallSiteTargetVolatile(this, newTarget); } // this implements the upcall from the JVM, MethodHandleNatives.linkCallSite: + /*package-private*/ static CallSite makeSite(MethodHandle bootstrapMethod, // Callee information: String name, MethodType type, diff -r 3b1afeb49a20 -r c887851da33d src/java.base/share/classes/java/lang/invoke/ConstantCallSite.java --- a/src/java.base/share/classes/java/lang/invoke/ConstantCallSite.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/java.base/share/classes/java/lang/invoke/ConstantCallSite.java Wed Nov 27 10:21:22 2019 +0100 @@ -25,6 +25,9 @@ package java.lang.invoke; +import jdk.internal.misc.Unsafe; +import jdk.internal.vm.annotation.Stable; + /** * A {@code ConstantCallSite} is a {@link CallSite} whose target is permanent, and can never be changed. * An {@code invokedynamic} instruction linked to a {@code ConstantCallSite} is permanently @@ -33,7 +36,10 @@ * @since 1.7 */ public class ConstantCallSite extends CallSite { - private final boolean isFrozen; + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + @Stable // should NOT be constant folded during instance initialization (isFrozen == false) + /*final*/ private boolean isFrozen; /** * Creates a call site with a permanent target. @@ -43,6 +49,7 @@ public ConstantCallSite(MethodHandle target) { super(target); isFrozen = true; + UNSAFE.storeStoreFence(); // properly publish isFrozen update } /** @@ -79,8 +86,9 @@ * @throws Throwable anything else thrown by the hook function */ protected ConstantCallSite(MethodType targetType, MethodHandle createTargetHook) throws Throwable { - super(targetType, createTargetHook); + super(targetType, createTargetHook); // "this" instance leaks into createTargetHook isFrozen = true; + UNSAFE.storeStoreFence(); // properly publish isFrozen } /** diff -r 3b1afeb49a20 -r c887851da33d src/java.base/share/classes/java/lang/invoke/MutableCallSite.java --- a/src/java.base/share/classes/java/lang/invoke/MutableCallSite.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/java.base/share/classes/java/lang/invoke/MutableCallSite.java Wed Nov 27 10:21:22 2019 +0100 @@ -152,7 +152,6 @@ * @see #getTarget */ @Override public void setTarget(MethodHandle newTarget) { - checkTargetChange(this.target, newTarget); setTargetNormal(newTarget); } diff -r 3b1afeb49a20 -r c887851da33d src/java.base/share/classes/java/lang/invoke/VolatileCallSite.java --- a/src/java.base/share/classes/java/lang/invoke/VolatileCallSite.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/java.base/share/classes/java/lang/invoke/VolatileCallSite.java Wed Nov 27 10:21:22 2019 +0100 @@ -96,7 +96,6 @@ * @see #getTarget */ @Override public void setTarget(MethodHandle newTarget) { - checkTargetChange(getTargetVolatile(), newTarget); setTargetVolatile(newTarget); } diff -r 3b1afeb49a20 -r c887851da33d src/java.base/share/classes/jdk/internal/PreviewFeature.java --- a/src/java.base/share/classes/jdk/internal/PreviewFeature.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/java.base/share/classes/jdk/internal/PreviewFeature.java Wed Nov 27 10:21:22 2019 +0100 @@ -54,6 +54,8 @@ public boolean essentialAPI() default false; public enum Feature { - TEXT_BLOCKS; + PATTERN_MATCHING_IN_INSTANCEOF, + TEXT_BLOCKS, + ; } } diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/source/tree/BindingPatternTree.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/BindingPatternTree.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.source.tree; + +import javax.lang.model.element.Name; + +/** + * {@preview Associated with pattern matching for instanceof, a preview feature of + * the Java language. + * + * This interface is associated with pattern matching for instanceof, a preview + * feature of the Java language. Preview features + * may be removed in a future release, or upgraded to permanent + * features of the Java language.} + * + * A binding pattern tree + * + * @since 14 + */ +public interface BindingPatternTree extends PatternTree { + + /** + * Returns the type of the bind variable. + * @return the type + */ + Tree getType(); + + /** + * A binding variable name. + * @return the name of the binding variable + */ + Name getBinding(); + +} + diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/source/tree/InstanceOfTree.java --- a/src/jdk.compiler/share/classes/com/sun/source/tree/InstanceOfTree.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/InstanceOfTree.java Wed Nov 27 10:21:22 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,4 +51,33 @@ * @return the type */ Tree getType(); + + /** + * {@preview Associated with pattern matching for instanceof, a preview feature of + * the Java language. + * + * This method is associated with pattern matching for instanceof, a preview + * feature of the Java language. Preview features + * may be removed in a future release, or upgraded to permanent + * features of the Java language.} + * + * Returns the tested pattern, or null if this instanceof does not use + * a pattern. + * + *

For instanceof with a pattern, i.e. in the following form: + *

+     *   expression instanceof type variable name
+     * 
+ * returns the pattern. + * + *

For instanceof without a pattern, i.e. in the following form: + *

+     *   expression instanceof type
+     * 
+ * returns null. + * + * @return the tested pattern, or null if this instanceof does not use a pattern. + * @since 14 + */ + PatternTree getPattern(); } diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/source/tree/PatternTree.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/PatternTree.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.source.tree; + +/** + * {@preview Associated with pattern matching for instanceof, a preview feature of + * the Java language. + * + * This interface is associated with pattern matching for instanceof, a preview + * feature of the Java language. Preview features + * may be removed in a future release, or upgraded to permanent + * features of the Java language.} + * + * A tree node used as the base class for the different kinds of + * statements. + * + * @since 14 + */ +public interface PatternTree extends Tree {} diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java --- a/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java Wed Nov 27 10:21:22 2019 +0100 @@ -220,6 +220,21 @@ PARENTHESIZED(ParenthesizedTree.class), /** + * {@preview Associated with pattern matching for instanceof, a preview feature of + * the Java language. + * + * This enum constant is associated with pattern matching for instanceof, a preview + * feature of the Java language. Preview features + * may be removed in a future release, or upgraded to permanent + * features of the Java language.} + * + * Used for instances of {@link BindingPatternTree}. + * + * @since 14 + */ + BINDING_PATTERN(BindingPatternTree.class), + + /** * Used for instances of {@link PrimitiveTypeTree}. */ PRIMITIVE_TYPE(PrimitiveTypeTree.class), diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java --- a/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java Wed Nov 27 10:21:22 2019 +0100 @@ -258,6 +258,23 @@ R visitLiteral(LiteralTree node, P p); /** + * {@preview Associated with pattern matching for instanceof, a preview feature of + * the Java language. + * + * This method is associated with pattern matching for instanceof, a preview + * feature of the Java language. Preview features + * may be removed in a future release, or upgraded to permanent + * features of the Java language.} + * + * Visits an BindingPattern node. + * @param node the node being visited + * @param p a parameter value + * @return a result value + * @since 14 + */ + R visitBindingPattern(BindingPatternTree node, P p); + + /** * Visits a MethodTree node. * @param node the node being visited * @param p a parameter value diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java --- a/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java Wed Nov 27 10:21:22 2019 +0100 @@ -557,6 +557,19 @@ * @param node {@inheritDoc} * @param p {@inheritDoc} * @return the result of {@code defaultAction} + * @since 14 + */ + @Override + public R visitBindingPattern(BindingPatternTree node, P p) { + return defaultAction(node, p); + } + + /** + * {@inheritDoc} This implementation calls {@code defaultAction}. + * + * @param node {@inheritDoc} + * @param p {@inheritDoc} + * @return the result of {@code defaultAction} */ @Override public R visitArrayAccess(ArrayAccessTree node, P p) { diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java --- a/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java Wed Nov 27 10:21:22 2019 +0100 @@ -667,7 +667,11 @@ @Override public R visitInstanceOf(InstanceOfTree node, P p) { R r = scan(node.getExpression(), p); - r = scanAndReduce(node.getType(), p, r); + if (node.getPattern() != null) { + r = scanAndReduce(node.getPattern(), p, r); + } else { + r = scanAndReduce(node.getType(), p, r); + } return r; } @@ -677,6 +681,19 @@ * @param node {@inheritDoc} * @param p {@inheritDoc} * @return the result of scanning + * @since 14 + */ + @Override + public R visitBindingPattern(BindingPatternTree node, P p) { + return scan(node.getType(), p); + } + + /** + * {@inheritDoc} This implementation scans the children in left to right order. + * + * @param node {@inheritDoc} + * @param p {@inheritDoc} + * @return the result of scanning */ @Override public R visitArrayAccess(ArrayAccessTree node, P p) { diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java Wed Nov 27 10:21:22 2019 +0100 @@ -334,6 +334,16 @@ */ public static final long PREVIEW_ESSENTIAL_API = 1L<<58; //any Symbol kind + /** + * Flag to indicate the given variable is a match binding variable. + */ + public static final long MATCH_BINDING = 1L<<59; + + /** + * A flag to indicate a match binding variable whose scope extends after the current statement. + */ + public static final long MATCH_BINDING_TO_OUTER = 1L<<60; + /** Modifier masks. */ public static final int @@ -453,7 +463,9 @@ ANONCONSTR_BASED(Flags.ANONCONSTR_BASED), NAME_FILLED(Flags.NAME_FILLED), PREVIEW_API(Flags.PREVIEW_API), - PREVIEW_ESSENTIAL_API(Flags.PREVIEW_ESSENTIAL_API); + PREVIEW_ESSENTIAL_API(Flags.PREVIEW_ESSENTIAL_API), + MATCH_BINDING(Flags.MATCH_BINDING), + MATCH_BINDING_TO_OUTER(Flags.MATCH_BINDING_TO_OUTER); Flag(long flag) { this.value = flag; diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java Wed Nov 27 10:21:22 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -165,7 +165,9 @@ * @return true, if given feature is a preview feature. */ public boolean isPreview(Feature feature) { - if (feature == Feature.TEXT_BLOCKS) + if (feature == Feature.PATTERN_MATCHING_IN_INSTANCEOF || + feature == Feature.REIFIABLE_TYPES_INSTANCEOF || + feature == Feature.TEXT_BLOCKS) return true; //Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing). //When real preview features will be added, this method can be implemented to return 'true' diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java Wed Nov 27 10:21:22 2019 +0100 @@ -198,7 +198,10 @@ SWITCH_MULTIPLE_CASE_LABELS(JDK14, Fragments.FeatureMultipleCaseLabels, DiagKind.PLURAL), SWITCH_RULE(JDK14, Fragments.FeatureSwitchRules, DiagKind.PLURAL), SWITCH_EXPRESSION(JDK14, Fragments.FeatureSwitchExpressions, DiagKind.PLURAL), - TEXT_BLOCKS(JDK14, Fragments.FeatureTextBlocks, DiagKind.PLURAL); + TEXT_BLOCKS(JDK14, Fragments.FeatureTextBlocks, DiagKind.PLURAL), + PATTERN_MATCHING_IN_INSTANCEOF(JDK14, Fragments.FeaturePatternMatchingInstanceof, DiagKind.NORMAL), + REIFIABLE_TYPES_INSTANCEOF(JDK14, Fragments.FeatureReifiableTypesInstanceof, DiagKind.PLURAL), + ; enum DiagKind { NORMAL, diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java Wed Nov 27 10:21:22 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -390,22 +390,26 @@ sym.getKind() == ElementKind.LOCAL_VARIABLE || sym.getKind() == ElementKind.RESOURCE_VARIABLE || sym.getKind() == ElementKind.EXCEPTION_PARAMETER) { - // Make sure all type annotations from the symbol are also - // on the owner. If the owner is an initializer block, propagate - // to the type. - final long ownerFlags = sym.owner.flags(); - if ((ownerFlags & Flags.BLOCK) != 0) { - // Store init and clinit type annotations with the ClassSymbol - // to allow output in Gen.normalizeDefs. - ClassSymbol cs = (ClassSymbol) sym.owner.owner; - if ((ownerFlags & Flags.STATIC) != 0) { - cs.appendClassInitTypeAttributes(typeAnnotations); - } else { - cs.appendInitTypeAttributes(typeAnnotations); - } + appendTypeAnnotationsToOwner(sym, typeAnnotations); + } + } + + private void appendTypeAnnotationsToOwner(Symbol sym, List typeAnnotations) { + // Make sure all type annotations from the symbol are also + // on the owner. If the owner is an initializer block, propagate + // to the type. + final long ownerFlags = sym.owner.flags(); + if ((ownerFlags & Flags.BLOCK) != 0) { + // Store init and clinit type annotations with the ClassSymbol + // to allow output in Gen.normalizeDefs. + ClassSymbol cs = (ClassSymbol) sym.owner.owner; + if ((ownerFlags & Flags.STATIC) != 0) { + cs.appendClassInitTypeAttributes(typeAnnotations); } else { - sym.owner.appendUniqueTypeAttributes(sym.getRawTypeAttributes()); + cs.appendInitTypeAttributes(typeAnnotations); } + } else { + sym.owner.appendUniqueTypeAttributes(typeAnnotations); } } @@ -943,10 +947,11 @@ " within frame " + frame); } + case BINDING_PATTERN: case VARIABLE: - VarSymbol v = ((JCVariableDecl)frame).sym; + VarSymbol v = frame.hasTag(Tag.BINDINGPATTERN) ? ((JCBindingPattern) frame).symbol : ((JCVariableDecl) frame).sym; if (v.getKind() != ElementKind.FIELD) { - v.owner.appendUniqueTypeAttributes(v.getRawTypeAttributes()); + appendTypeAnnotationsToOwner(v, v.getRawTypeAttributes()); } switch (v.getKind()) { case LOCAL_VARIABLE: diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java Wed Nov 27 10:21:22 2019 +0100 @@ -1122,6 +1122,13 @@ } @Override + public void visitBindingPattern(JCTree.JCBindingPattern tree) { + //type binding pattern's type will be annotated separatelly, avoid + //adding its annotations into the owning method here (would clash + //with repeatable annotations). + } + + @Override public void visitClassDef(JCClassDecl tree) { // We can only hit a classdef if it is declared within // a method. Ignore it - the class will be visited diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java Wed Nov 27 10:21:22 2019 +0100 @@ -49,6 +49,7 @@ import com.sun.tools.javac.comp.ArgumentAttr.LocalCacheContext; import com.sun.tools.javac.comp.Check.CheckContext; import com.sun.tools.javac.comp.DeferredAttr.AttrMode; +import com.sun.tools.javac.comp.MatchBindingsComputer.BindingSymbol; import com.sun.tools.javac.jvm.*; import static com.sun.tools.javac.resources.CompilerProperties.Fragments.Diamond; import static com.sun.tools.javac.resources.CompilerProperties.Fragments.DiamondInvalidArg; @@ -110,6 +111,7 @@ final Enter enter; final Target target; final Types types; + final Preview preview; final JCDiagnostic.Factory diags; final TypeAnnotations typeAnnotations; final DeferredLintHandler deferredLintHandler; @@ -117,6 +119,7 @@ final Dependencies dependencies; final Annotate annotate; final ArgumentAttr argumentAttr; + final MatchBindingsComputer matchBindingsComputer; public static Attr instance(Context context) { Attr instance = context.get(attrKey); @@ -145,6 +148,7 @@ cfolder = ConstFold.instance(context); target = Target.instance(context); types = Types.instance(context); + preview = Preview.instance(context); diags = JCDiagnostic.Factory.instance(context); annotate = Annotate.instance(context); typeAnnotations = TypeAnnotations.instance(context); @@ -152,6 +156,7 @@ typeEnvs = TypeEnvs.instance(context); dependencies = Dependencies.instance(context); argumentAttr = ArgumentAttr.instance(context); + matchBindingsComputer = MatchBindingsComputer.instance(context); Options options = Options.instance(context); @@ -161,6 +166,9 @@ allowLambda = Feature.LAMBDA.allowedInSource(source); allowDefaultMethods = Feature.DEFAULT_METHODS.allowedInSource(source); allowStaticInterfaceMethods = Feature.STATIC_INTERFACE_METHODS.allowedInSource(source); + allowReifiableTypesInInstanceof = + Feature.REIFIABLE_TYPES_INSTANCEOF.allowedInSource(source) && + (!preview.isPreview(Feature.REIFIABLE_TYPES_INSTANCEOF) || preview.isEnabled()); sourceName = source.name; useBeforeDeclarationWarning = options.isSet("useBeforeDeclarationWarning"); @@ -193,6 +201,10 @@ */ boolean allowStaticInterfaceMethods; + /** Switch: reifiable types in instanceof enabled? + */ + boolean allowReifiableTypesInInstanceof; + /** * Switch: warn about use of variable before declaration? * RFE: 6425594 @@ -292,6 +304,8 @@ isAssignableAsBlankFinal(v, env)))) { if (v.isResourceVariable()) { //TWR resource log.error(pos, Errors.TryResourceMayNotBeAssigned(v)); + } else if ((v.flags() & MATCH_BINDING) != 0) { + log.error(pos, Errors.PatternBindingMayNotBeAssigned(v)); } else { log.error(pos, Errors.CantAssignValToFinalVar(v)); } @@ -1298,29 +1312,73 @@ public void visitDoLoop(JCDoWhileLoop tree) { attribStat(tree.body, env.dup(tree)); attribExpr(tree.cond, env, syms.booleanType); + if (!breaksOutOf(tree, tree.body)) { + //include condition's body when false after the while, if cannot get out of the loop + List bindings = matchBindingsComputer.getMatchBindings(tree.cond, false); + + bindings.forEach(env.info.scope::enter); + bindings.forEach(BindingSymbol::preserveBinding); + } result = null; } public void visitWhileLoop(JCWhileLoop tree) { attribExpr(tree.cond, env, syms.booleanType); - attribStat(tree.body, env.dup(tree)); + // include condition's bindings when true in the body: + Env whileEnv = bindingEnv(env, matchBindingsComputer.getMatchBindings(tree.cond, true)); + try { + attribStat(tree.body, whileEnv.dup(tree)); + } finally { + whileEnv.info.scope.leave(); + } + if (!breaksOutOf(tree, tree.body)) { + //include condition's bindings when false after the while, if cannot get out of the loop + List bindings = + matchBindingsComputer.getMatchBindings(tree.cond, false); + + bindings.forEach(env.info.scope::enter); + bindings.forEach(BindingSymbol::preserveBinding); + } result = null; } + private boolean breaksOutOf(JCTree loop, JCTree body) { + preFlow(body); + return flow.breaksOutOf(env, loop, body, make); + } + public void visitForLoop(JCForLoop tree) { Env loopEnv = env.dup(env.tree, env.info.dup(env.info.scope.dup())); try { attribStats(tree.init, loopEnv); - if (tree.cond != null) attribExpr(tree.cond, loopEnv, syms.booleanType); - loopEnv.tree = tree; // before, we were not in loop! - attribStats(tree.step, loopEnv); - attribStat(tree.body, loopEnv); + List matchBindings = List.nil(); + if (tree.cond != null) { + attribExpr(tree.cond, loopEnv, syms.booleanType); + // include condition's bindings when true in the body and step: + matchBindings = matchBindingsComputer.getMatchBindings(tree.cond, true); + } + Env bodyEnv = bindingEnv(loopEnv, matchBindings); + try { + bodyEnv.tree = tree; // before, we were not in loop! + attribStats(tree.step, bodyEnv); + attribStat(tree.body, bodyEnv); + } finally { + bodyEnv.info.scope.leave(); + } result = null; } finally { loopEnv.info.scope.leave(); } + if (!breaksOutOf(tree, tree.body)) { + //include condition's body when false after the while, if cannot get out of the loop + List bindings = + matchBindingsComputer.getMatchBindings(tree.cond, false); + + bindings.forEach(env.info.scope::enter); + bindings.forEach(BindingSymbol::preserveBinding); + } } public void visitForeachLoop(JCEnhancedForLoop tree) { @@ -1673,8 +1731,26 @@ unknownExprInfo : resultInfo.dup(conditionalContext(resultInfo.checkContext)); - Type truetype = attribTree(tree.truepart, env, condInfo); - Type falsetype = attribTree(tree.falsepart, env, condInfo); + + // x ? y : z + // include x's bindings when true in y + // include x's bindings when false in z + + Type truetype; + Env trueEnv = bindingEnv(env, matchBindingsComputer.getMatchBindings(tree.cond, true)); + try { + truetype = attribTree(tree.truepart, trueEnv, condInfo); + } finally { + trueEnv.info.scope.leave(); + } + + Type falsetype; + Env falseEnv = bindingEnv(env, matchBindingsComputer.getMatchBindings(tree.cond, false)); + try { + falsetype = attribTree(tree.falsepart, falseEnv, condInfo); + } finally { + falseEnv.info.scope.leave(); + } Type owntype = (tree.polyKind == PolyKind.STANDALONE) ? condType(List.of(tree.truepart.pos(), tree.falsepart.pos()), @@ -1829,15 +1905,77 @@ BOOLEAN, }; + Env bindingEnv(Env env, List bindings) { + Env env1 = env.dup(env.tree, env.info.dup(env.info.scope.dup())); + bindings.forEach(env1.info.scope::enter); + return env1; + } + public void visitIf(JCIf tree) { attribExpr(tree.cond, env, syms.booleanType); - attribStat(tree.thenpart, env); - if (tree.elsepart != null) - attribStat(tree.elsepart, env); + + // if (x) { y } [ else z ] + // include x's bindings when true in y + // include x's bindings when false in z + + List thenBindings = matchBindingsComputer.getMatchBindings(tree.cond, true); + Env thenEnv = bindingEnv(env, thenBindings); + + try { + attribStat(tree.thenpart, thenEnv); + } finally { + thenEnv.info.scope.leave(); + } + + preFlow(tree.thenpart); + boolean aliveAfterThen = flow.aliveAfter(env, tree.thenpart, make); + boolean aliveAfterElse; + List elseBindings = matchBindingsComputer.getMatchBindings(tree.cond, false); + + if (tree.elsepart != null) { + Env elseEnv = bindingEnv(env, elseBindings); + try { + attribStat(tree.elsepart, elseEnv); + } finally { + elseEnv.info.scope.leave(); + } + preFlow(tree.elsepart); + aliveAfterElse = flow.aliveAfter(env, tree.elsepart, make); + } else { + aliveAfterElse = true; + } + chk.checkEmptyIf(tree); + + List afterIfBindings = List.nil(); + + if (aliveAfterThen && !aliveAfterElse) { + afterIfBindings = thenBindings; + } else if (aliveAfterElse && !aliveAfterThen) { + afterIfBindings = elseBindings; + } + + afterIfBindings.forEach(env.info.scope::enter); + afterIfBindings.forEach(BindingSymbol::preserveBinding); + result = null; } + void preFlow(JCTree tree) { + new PostAttrAnalyzer() { + @Override + public void scan(JCTree tree) { + if (tree == null || + (tree.type != null && + tree.type == Type.stuckType)) { + //don't touch stuck expressions! + return; + } + super.scan(tree); + } + }.scan(tree); + } + public void visitExec(JCExpressionStatement tree) { //a fresh environment is required for 292 inference to work properly --- //see Infer.instantiatePolymorphicSignatureInstance() @@ -3521,7 +3659,32 @@ public void visitBinary(JCBinary tree) { // Attribute arguments. Type left = chk.checkNonVoid(tree.lhs.pos(), attribExpr(tree.lhs, env)); - Type right = chk.checkNonVoid(tree.rhs.pos(), attribExpr(tree.rhs, env)); + // x && y + // include x's bindings when true in y + + // x || y + // include x's bindings when false in y + + List matchBindings; + switch (tree.getTag()) { + case AND: + matchBindings = matchBindingsComputer.getMatchBindings(tree.lhs, true); + break; + case OR: + matchBindings = matchBindingsComputer.getMatchBindings(tree.lhs, false); + break; + default: + matchBindings = List.nil(); + break; + } + Env rhsEnv = bindingEnv(env, matchBindings); + Type right; + try { + right = chk.checkNonVoid(tree.rhs.pos(), attribExpr(tree.rhs, rhsEnv)); + } finally { + rhsEnv.info.scope.leave(); + } + // Find operator. Symbol operator = tree.operator = operators.resolveBinary(tree, tree.getTag(), left, right); Type owntype = types.createErrorType(tree.type); @@ -3587,19 +3750,63 @@ public void visitTypeTest(JCInstanceOf tree) { Type exprtype = chk.checkNullOrRefType( tree.expr.pos(), attribExpr(tree.expr, env)); - Type clazztype = attribType(tree.clazz, env); + Type clazztype; + JCTree typeTree; + if (tree.pattern.getTag() == BINDINGPATTERN) { + attribTree(tree.pattern, env, unknownExprInfo); + clazztype = tree.pattern.type; + JCBindingPattern pattern = (JCBindingPattern) tree.pattern; + typeTree = pattern.vartype; + if (!clazztype.hasTag(TYPEVAR)) { + clazztype = chk.checkClassOrArrayType(pattern.vartype.pos(), clazztype); + } + } else { + clazztype = attribType(tree.pattern, env); + typeTree = tree.pattern; + } if (!clazztype.hasTag(TYPEVAR)) { - clazztype = chk.checkClassOrArrayType(tree.clazz.pos(), clazztype); + clazztype = chk.checkClassOrArrayType(typeTree.pos(), clazztype); } if (!clazztype.isErroneous() && !types.isReifiable(clazztype)) { - log.error(tree.clazz.pos(), Errors.IllegalGenericTypeForInstof); - clazztype = types.createErrorType(clazztype); - } - chk.validate(tree.clazz, env, false); + boolean valid = false; + if (allowReifiableTypesInInstanceof) { + if (preview.isPreview(Feature.REIFIABLE_TYPES_INSTANCEOF)) { + preview.warnPreview(tree.expr.pos(), Feature.REIFIABLE_TYPES_INSTANCEOF); + } + Warner warner = new Warner(); + if (!types.isCastable(exprtype, clazztype, warner)) { + chk.basicHandler.report(tree.expr.pos(), + diags.fragment(Fragments.InconvertibleTypes(exprtype, clazztype))); + } else if (warner.hasLint(LintCategory.UNCHECKED)) { + log.error(tree.expr.pos(), + Errors.InstanceofReifiableNotSafe(exprtype, clazztype)); + } else { + valid = true; + } + } else { + log.error(typeTree.pos(), Errors.IllegalGenericTypeForInstof); + } + if (!valid) { + clazztype = types.createErrorType(clazztype); + } + } + chk.validate(typeTree, env, false); chk.checkCastable(tree.expr.pos(), exprtype, clazztype); result = check(tree, syms.booleanType, KindSelector.VAL, resultInfo); } + public void visitBindingPattern(JCBindingPattern tree) { + ResultInfo varInfo = new ResultInfo(KindSelector.TYP, resultInfo.pt, resultInfo.checkContext); + tree.type = attribTree(tree.vartype, env, varInfo); + VarSymbol v = tree.symbol = new BindingSymbol(tree.name, tree.vartype.type, env.info.scope.owner); + if (chk.checkUnique(tree.pos(), v, env.info.scope)) { + chk.checkTransparentVar(tree.pos(), v, env.info.scope); + } + annotate.queueScanTreeAndTypeAnnotate(tree.vartype, env, v, tree.pos()); + annotate.flush(); + result = tree.type; + } + public void visitIndexed(JCArrayAccess tree) { Type owntype = types.createErrorType(tree.type); Type atype = attribExpr(tree.indexed, env); @@ -4991,8 +5198,8 @@ super.visitTypeCast(tree); } public void visitTypeTest(JCInstanceOf tree) { - if (tree.clazz != null && tree.clazz.type != null) - validateAnnotatedType(tree.clazz, tree.clazz.type); + if (tree.pattern != null && !(tree.pattern instanceof JCPattern) && tree.pattern.type != null) + validateAnnotatedType(tree.pattern, tree.pattern.type); super.visitTypeTest(tree); } public void visitNewClass(JCNewClass tree) { @@ -5253,6 +5460,15 @@ } @Override + public void visitBindingPattern(JCBindingPattern that) { + if (that.symbol == null) { + that.symbol = new BindingSymbol(that.name, that.type, syms.noSymbol); + that.symbol.adr = 0; + } + super.visitBindingPattern(that); + } + + @Override public void visitNewClass(JCNewClass that) { if (that.constructor == null) { that.constructor = new MethodSymbol(0, names.init, diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java Wed Nov 27 10:21:22 2019 +0100 @@ -3486,6 +3486,11 @@ duplicateErasureError(pos, sym, byName); sym.flags_field |= CLASH; return true; + } else if ((sym.flags() & MATCH_BINDING) != 0 && + (byName.flags() & MATCH_BINDING) != 0 && + (byName.flags() & MATCH_BINDING_TO_OUTER) == 0) { + //this error will be reported separatelly in MatchBindingsComputer + return false; } else { duplicateError(pos, byName); return false; diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/comp/CompileStates.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/CompileStates.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/CompileStates.java Wed Nov 27 10:21:22 2019 +0100 @@ -59,9 +59,10 @@ ATTR(4), FLOW(5), TRANSTYPES(6), - UNLAMBDA(7), - LOWER(8), - GENERATE(9); + TRANSPATTERNS(7), + UNLAMBDA(8), + LOWER(9), + GENERATE(10); CompileState(int value) { this.value = value; diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java Wed Nov 27 10:21:22 2019 +0100 @@ -255,6 +255,41 @@ } } + public boolean aliveAfter(Env env, JCTree that, TreeMaker make) { + //we need to disable diagnostics temporarily; the problem is that if + //"that" contains e.g. an unreachable statement, an error + //message will be reported and will cause compilation to skip the flow analyis + //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis + //related errors, which will allow for more errors to be detected + Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log); + try { + SnippetAliveAnalyzer analyzer = new SnippetAliveAnalyzer(); + + analyzer.analyzeTree(env, that, make); + return analyzer.isAlive(); + } finally { + log.popDiagnosticHandler(diagHandler); + } + } + + public boolean breaksOutOf(Env env, JCTree loop, JCTree body, TreeMaker make) { + //we need to disable diagnostics temporarily; the problem is that if + //"that" contains e.g. an unreachable statement, an error + //message will be reported and will cause compilation to skip the flow analyis + //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis + //related errors, which will allow for more errors to be detected + Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log); + try { + boolean[] breaksOut = new boolean[1]; + SnippetBreakAnalyzer analyzer = new SnippetBreakAnalyzer(loop); + + analyzer.analyzeTree(env, body, make); + return analyzer.breaksOut(); + } finally { + log.popDiagnosticHandler(diagHandler); + } + } + /** * Definite assignment scan mode */ @@ -1467,6 +1502,38 @@ } /** + * Determine if alive after the given tree. + */ + class SnippetAliveAnalyzer extends AliveAnalyzer { + @Override + public void visitClassDef(JCClassDecl tree) { + //skip + } + public boolean isAlive() { + return super.alive != Liveness.DEAD; + } + } + + class SnippetBreakAnalyzer extends AliveAnalyzer { + private final JCTree loop; + private boolean breaksOut; + + public SnippetBreakAnalyzer(JCTree loop) { + this.loop = loop; + } + + @Override + public void visitBreak(JCBreak tree) { + breaksOut |= (super.alive == Liveness.ALIVE && tree.target == loop); + super.visitBreak(tree); + } + + public boolean breaksOut() { + return breaksOut; + } + } + + /** * Specialized pass that performs DA/DU on a lambda */ class LambdaAssignAnalyzer extends AssignAnalyzer { diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MatchBindingsComputer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MatchBindingsComputer.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.tools.javac.comp; + +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.code.Symbol.VarSymbol; +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.code.Types; +import com.sun.tools.javac.resources.CompilerProperties.Errors; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCBinary; +import com.sun.tools.javac.tree.JCTree.JCConditional; +import com.sun.tools.javac.tree.JCTree.JCUnary; +import com.sun.tools.javac.tree.JCTree.JCBindingPattern; +import com.sun.tools.javac.tree.TreeScanner; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.Log; +import com.sun.tools.javac.util.Name; + + +public class MatchBindingsComputer extends TreeScanner { + protected static final Context.Key matchBindingsComputerKey = new Context.Key<>(); + + private final Log log; + private final Types types; + boolean whenTrue; + List bindings; + + public static MatchBindingsComputer instance(Context context) { + MatchBindingsComputer instance = context.get(matchBindingsComputerKey); + if (instance == null) + instance = new MatchBindingsComputer(context); + return instance; + } + + protected MatchBindingsComputer(Context context) { + this.log = Log.instance(context); + this.types = Types.instance(context); + } + + public List getMatchBindings(JCTree expression, boolean whenTrue) { + this.whenTrue = whenTrue; + this.bindings = List.nil(); + scan(expression); + return bindings; + } + + @Override + public void visitBindingPattern(JCBindingPattern tree) { + bindings = whenTrue ? List.of(tree.symbol) : List.nil(); + } + + @Override + public void visitBinary(JCBinary tree) { + switch (tree.getTag()) { + case AND: + // e.T = union(x.T, y.T) + // e.F = intersection(x.F, y.F) + scan(tree.lhs); + List lhsBindings = bindings; + scan(tree.rhs); + List rhsBindings = bindings; + bindings = whenTrue ? union(tree, lhsBindings, rhsBindings) : intersection(tree, lhsBindings, rhsBindings); + break; + case OR: + // e.T = intersection(x.T, y.T) + // e.F = union(x.F, y.F) + scan(tree.lhs); + lhsBindings = bindings; + scan(tree.rhs); + rhsBindings = bindings; + bindings = whenTrue ? intersection(tree, lhsBindings, rhsBindings) : union(tree, lhsBindings, rhsBindings); + break; + default: + super.visitBinary(tree); + break; + } + } + + @Override + public void visitUnary(JCUnary tree) { + switch (tree.getTag()) { + case NOT: + // e.T = x.F // flip 'em + // e.F = x.T + whenTrue = !whenTrue; + scan(tree.arg); + whenTrue = !whenTrue; + break; + default: + super.visitUnary(tree); + break; + } + } + + @Override + public void visitConditional(JCConditional tree) { + /* if e = "x ? y : z", then: + e.T = union(intersect(y.T, z.T), intersect(x.T, z.T), intersect(x.F, y.T)) + e.F = union(intersect(y.F, z.F), intersect(x.T, z.F), intersect(x.F, y.F)) + */ + if (whenTrue) { + List xT, yT, zT, xF; + scan(tree.cond); + xT = bindings; + scan(tree.truepart); + yT = bindings; + scan(tree.falsepart); + zT = bindings; + whenTrue = false; + scan(tree.cond); + xF = bindings; + whenTrue = true; + bindings = union(tree, intersection(tree, yT, zT), intersection(tree, xT, zT), intersection(tree, xF, yT)); + } else { + List xF, yF, zF, xT; + scan(tree.cond); + xF = bindings; + scan(tree.truepart); + yF = bindings; + scan(tree.falsepart); + zF = bindings; + whenTrue = true; + scan(tree.cond); + xT = bindings; + whenTrue = false; + bindings = union(tree, intersection(tree, yF, zF), intersection(tree, xT, zF), intersection(tree, xF, yF)); + } + } + + private List intersection(JCTree tree, List lhsBindings, List rhsBindings) { + // It is an error if, for intersection(a,b), if a and b contain the same variable name (may be eventually relaxed to merge variables of same type) + List list = List.nil(); + for (BindingSymbol v1 : lhsBindings) { + for (BindingSymbol v2 : rhsBindings) { + if (v1.name == v2.name) { + log.error(tree.pos(), Errors.MatchBindingExists); + list = list.append(v2); + } + } + } + return list; + } + + @SafeVarargs + private final List union(JCTree tree, List lhsBindings, List ... rhsBindings_s) { + // It is an error if for union(a,b), a and b contain the same name (disjoint union). + List list = lhsBindings; + for (List rhsBindings : rhsBindings_s) { + for (BindingSymbol v : rhsBindings) { + for (BindingSymbol ov : list) { + if (ov.name == v.name) { + log.error(tree.pos(), Errors.MatchBindingExists); + } + } + list = list.append(v); + } + } + return list; + } + + @Override + public void scan(JCTree tree) { + bindings = List.nil(); + super.scan(tree); + } + + public static class BindingSymbol extends VarSymbol { + + public BindingSymbol(Name name, Type type, Symbol owner) { + super(Flags.FINAL | Flags.HASINIT | Flags.MATCH_BINDING, name, type, owner); + } + + public boolean isAliasFor(BindingSymbol b) { + return aliases().containsAll(b.aliases()); + } + + List aliases() { + return List.of(this); + } + + public void preserveBinding() { + flags_field |= Flags.MATCH_BINDING_TO_OUTER; + } + + public boolean isPreserved() { + return (flags_field & Flags.MATCH_BINDING_TO_OUTER) != 0; + } + } + +} diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.tools.javac.comp; + +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.Symbol.VarSymbol; +import com.sun.tools.javac.code.Symtab; +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.code.Types; +import com.sun.tools.javac.comp.MatchBindingsComputer.BindingSymbol; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCAssign; +import com.sun.tools.javac.tree.JCTree.JCBinary; +import com.sun.tools.javac.tree.JCTree.JCConditional; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCForLoop; +import com.sun.tools.javac.tree.JCTree.JCIdent; +import com.sun.tools.javac.tree.JCTree.JCIf; +import com.sun.tools.javac.tree.JCTree.JCInstanceOf; +import com.sun.tools.javac.tree.JCTree.JCLabeledStatement; +import com.sun.tools.javac.tree.JCTree.JCMethodDecl; +import com.sun.tools.javac.tree.JCTree.JCStatement; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; +import com.sun.tools.javac.tree.JCTree.JCBindingPattern; +import com.sun.tools.javac.tree.JCTree.JCWhileLoop; +import com.sun.tools.javac.tree.JCTree.Tag; +import com.sun.tools.javac.tree.TreeMaker; +import com.sun.tools.javac.tree.TreeTranslator; +import com.sun.tools.javac.util.Assert; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.ListBuffer; +import com.sun.tools.javac.util.Log; +import com.sun.tools.javac.util.Names; +import com.sun.tools.javac.util.Options; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +import com.sun.tools.javac.code.Symbol.MethodSymbol; +import static com.sun.tools.javac.code.TypeTag.BOOLEAN; +import static com.sun.tools.javac.code.TypeTag.BOT; +import com.sun.tools.javac.comp.MatchBindingsComputer.BindingSymbol; +import com.sun.tools.javac.jvm.Target; +import com.sun.tools.javac.tree.JCTree.JCBlock; +import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop; +import com.sun.tools.javac.tree.JCTree.JCStatement; +import com.sun.tools.javac.tree.JCTree.LetExpr; +import com.sun.tools.javac.util.List; + +/** + * This pass translates pattern-matching constructs, such as instanceof . + */ +public class TransPatterns extends TreeTranslator { + + protected static final Context.Key transPatternsKey = new Context.Key<>(); + + public static TransPatterns instance(Context context) { + TransPatterns instance = context.get(transPatternsKey); + if (instance == null) + instance = new TransPatterns(context); + return instance; + } + + private final Symtab syms; + private final Types types; + private final Operators operators; + private final Log log; + private final ConstFold constFold; + private final Names names; + private final Target target; + private final MatchBindingsComputer matchBindingsComputer; + private TreeMaker make; + + BindingContext bindingContext = new BindingContext() { + @Override + VarSymbol getBindingFor(BindingSymbol varSymbol) { + return null; + } + + @Override + JCStatement decorateStatement(JCStatement stat) { + return stat; + } + + @Override + JCExpression decorateExpression(JCExpression expr) { + return expr; + } + + @Override + BindingContext pop() { + //do nothing + return this; + } + + @Override + boolean tryPrepend(BindingSymbol binding, JCVariableDecl var) { + return false; + } + }; + + JCLabeledStatement pendingMatchLabel = null; + + boolean debugTransPatterns; + + private MethodSymbol currentMethodSym = null; + + protected TransPatterns(Context context) { + context.put(transPatternsKey, this); + syms = Symtab.instance(context); + make = TreeMaker.instance(context); + types = Types.instance(context); + operators = Operators.instance(context); + log = Log.instance(context); + constFold = ConstFold.instance(context); + names = Names.instance(context); + target = Target.instance(context); + matchBindingsComputer = MatchBindingsComputer.instance(context); + debugTransPatterns = Options.instance(context).isSet("debug.patterns"); + } + + @Override + public void visitTypeTest(JCInstanceOf tree) { + if (tree.pattern.hasTag(Tag.BINDINGPATTERN)) { + //E instanceof T N + //=> + //(let T' N$temp = E; N$temp instanceof T && (N = (T) N$temp == (T) N$temp)) + JCBindingPattern patt = (JCBindingPattern)tree.pattern; + VarSymbol pattSym = patt.symbol; + Type tempType = tree.expr.type.hasTag(BOT) ? + syms.objectType + : tree.expr.type; + VarSymbol temp = new VarSymbol(pattSym.flags() | Flags.SYNTHETIC, + names.fromString(pattSym.name.toString() + target.syntheticNameChar() + "temp"), + tempType, + patt.symbol.owner); + JCExpression translatedExpr = translate(tree.expr); + Type castTargetType = types.boxedTypeOrType(pattSym.erasure(types)); + + result = makeTypeTest(make.Ident(temp), make.Type(castTargetType)); + + VarSymbol bindingVar = bindingContext.getBindingFor(patt.symbol); + if (bindingVar != null) { + JCAssign fakeInit = (JCAssign)make.at(tree.pos).Assign( + make.Ident(bindingVar), convert(make.Ident(temp), castTargetType)).setType(bindingVar.erasure(types)); + result = makeBinary(Tag.AND, (JCExpression)result, + makeBinary(Tag.EQ, fakeInit, convert(make.Ident(temp), castTargetType))); + } + result = make.at(tree.pos).LetExpr(make.VarDef(temp, translatedExpr), (JCExpression)result).setType(syms.booleanType); + ((LetExpr) result).needsCond = true; + } else { + super.visitTypeTest(tree); + } + } + + @Override + public void visitBinary(JCBinary tree) { + List matchBindings; + switch (tree.getTag()) { + case AND: + matchBindings = matchBindingsComputer.getMatchBindings(tree.lhs, true); + break; + case OR: + matchBindings = matchBindingsComputer.getMatchBindings(tree.lhs, false); + break; + default: + matchBindings = List.nil(); + break; + } + + bindingContext = new BasicBindingContext(matchBindings); + try { + super.visitBinary(tree); + result = bindingContext.decorateExpression(tree); + } finally { + bindingContext.pop(); + } + } + + @Override + public void visitConditional(JCConditional tree) { + bindingContext = new BasicBindingContext( + matchBindingsComputer.getMatchBindings(tree.cond, true) + .appendList(matchBindingsComputer.getMatchBindings(tree.cond, false))); + try { + super.visitConditional(tree); + result = bindingContext.decorateExpression(tree); + } finally { + bindingContext.pop(); + } + } + + @Override + public void visitIf(JCIf tree) { + bindingContext = new BasicBindingContext(getMatchBindings(tree.cond)); + try { + super.visitIf(tree); + result = bindingContext.decorateStatement(tree); + } finally { + bindingContext.pop(); + } + } + + @Override + public void visitForLoop(JCForLoop tree) { + bindingContext = new BasicBindingContext(getMatchBindings(tree.cond)); + try { + super.visitForLoop(tree); + result = bindingContext.decorateStatement(tree); + } finally { + bindingContext.pop(); + } + } + + @Override + public void visitWhileLoop(JCWhileLoop tree) { + bindingContext = new BasicBindingContext(getMatchBindings(tree.cond)); + try { + super.visitWhileLoop(tree); + result = bindingContext.decorateStatement(tree); + } finally { + bindingContext.pop(); + } + } + + @Override + public void visitDoLoop(JCDoWhileLoop tree) { + bindingContext = new BasicBindingContext(getMatchBindings(tree.cond)); + try { + super.visitDoLoop(tree); + result = bindingContext.decorateStatement(tree); + } finally { + bindingContext.pop(); + } + } + + @Override + public void visitMethodDef(JCMethodDecl tree) { + MethodSymbol prevMethodSym = currentMethodSym; + try { + currentMethodSym = tree.sym; + super.visitMethodDef(tree); + } finally { + currentMethodSym = prevMethodSym; + } + } + + @Override + public void visitIdent(JCIdent tree) { + VarSymbol bindingVar = null; + if ((tree.sym.flags() & Flags.MATCH_BINDING) != 0) { + bindingVar = bindingContext.getBindingFor((BindingSymbol)tree.sym); + } + if (bindingVar == null) { + super.visitIdent(tree); + } else { + result = make.at(tree.pos).Ident(bindingVar); + } + } + + @Override + public void visitBlock(JCBlock tree) { + ListBuffer statements = new ListBuffer<>(); + bindingContext = new BasicBindingContext(List.nil()) { + boolean tryPrepend(BindingSymbol binding, JCVariableDecl var) { + //{ + // if (E instanceof T N) { + // return ; + // } + // //use of N: + //} + //=> + //{ + // T N; + // if ((let T' N$temp = E; N$temp instanceof T && (N = (T) N$temp == (T) N$temp))) { + // return ; + // } + // //use of N: + //} + hoistedVarMap.put(binding, var.sym); + statements.append(var); + return true; + } + }; + try { + for (List l = tree.stats; l.nonEmpty(); l = l.tail) { + statements.append(translate(l.head)); + } + + tree.stats = statements.toList(); + result = tree; + } finally { + bindingContext.pop(); + } + } + + public JCTree translateTopLevelClass(Env env, JCTree cdef, TreeMaker make) { + try { + this.make = make; + translate(cdef); + } finally { + // note that recursive invocations of this method fail hard + this.make = null; + } + + return cdef; + } + + /** Make an instanceof expression. + * @param lhs The expression. + * @param type The type to be tested. + */ + + JCInstanceOf makeTypeTest(JCExpression lhs, JCExpression type) { + JCInstanceOf tree = make.TypeTest(lhs, type); + tree.type = syms.booleanType; + return tree; + } + + /** Make an attributed binary expression (copied from Lower). + * @param optag The operators tree tag. + * @param lhs The operator's left argument. + * @param rhs The operator's right argument. + */ + JCBinary makeBinary(JCTree.Tag optag, JCExpression lhs, JCExpression rhs) { + JCBinary tree = make.Binary(optag, lhs, rhs); + tree.operator = operators.resolveBinary(tree, optag, lhs.type, rhs.type); + tree.type = tree.operator.type.getReturnType(); + return tree; + } + + JCExpression convert(JCExpression expr, Type target) { + JCExpression result = make.at(expr.pos()).TypeCast(make.Type(target), expr); + result.type = target; + return result; + } + + private List getMatchBindings(JCExpression cond) { + return matchBindingsComputer.getMatchBindings(cond, true) + .appendList(matchBindingsComputer.getMatchBindings(cond, false)); + } + abstract class BindingContext { + abstract VarSymbol getBindingFor(BindingSymbol varSymbol); + abstract JCStatement decorateStatement(JCStatement stat); + abstract JCExpression decorateExpression(JCExpression expr); + abstract BindingContext pop(); + abstract boolean tryPrepend(BindingSymbol binding, JCVariableDecl var); + } + + class BasicBindingContext extends BindingContext { + List matchBindings; + Map hoistedVarMap; + BindingContext parent; + + public BasicBindingContext(List matchBindings) { + this.matchBindings = matchBindings; + this.parent = bindingContext; + this.hoistedVarMap = matchBindings.stream() + .filter(v -> parent.getBindingFor(v) == null) + .collect(Collectors.toMap(v -> v, v -> { + VarSymbol res = new VarSymbol(v.flags(), v.name, v.type, v.owner); + res.setTypeAttributes(v.getRawTypeAttributes()); + return res; + })); + } + + @Override + VarSymbol getBindingFor(BindingSymbol varSymbol) { + VarSymbol res = parent.getBindingFor(varSymbol); + if (res != null) { + return res; + } + return hoistedVarMap.entrySet().stream() + .filter(e -> e.getKey().isAliasFor(varSymbol)) + .findFirst() + .map(e -> e.getValue()).orElse(null); + } + + @Override + JCStatement decorateStatement(JCStatement stat) { + if (hoistedVarMap.isEmpty()) return stat; + //if (E instanceof T N) { + // //use N + //} + //=> + //{ + // T N; + // if ((let T' N$temp = E; N$temp instanceof T && (N = (T) N$temp == (T) N$temp))) { + // //use N + // } + //} + ListBuffer stats = new ListBuffer<>(); + for (Entry e : hoistedVarMap.entrySet()) { + JCVariableDecl decl = makeHoistedVarDecl(stat.pos, e.getValue()); + if (!e.getKey().isPreserved() || + !parent.tryPrepend(e.getKey(), decl)) { + stats.add(decl); + } + } + if (stats.nonEmpty()) { + stats.add(stat); + stat = make.at(stat.pos).Block(0, stats.toList()); + } + return stat; + } + + @Override + JCExpression decorateExpression(JCExpression expr) { + //E instanceof T N && /*use of N*/ + //=> + //(let T N; (let T' N$temp = E; N$temp instanceof T && (N = (T) N$temp == (T) N$temp)) && /*use of N*/) + for (VarSymbol vsym : hoistedVarMap.values()) { + expr = make.at(expr.pos).LetExpr(makeHoistedVarDecl(expr.pos, vsym), expr).setType(expr.type); + } + return expr; + } + + @Override + BindingContext pop() { + return bindingContext = parent; + } + + @Override + boolean tryPrepend(BindingSymbol binding, JCVariableDecl var) { + return false; + } + + private JCVariableDecl makeHoistedVarDecl(int pos, VarSymbol varSymbol) { + return make.at(pos).VarDef(varSymbol, null); + } + } +} diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java Wed Nov 27 10:21:22 2019 +0100 @@ -567,6 +567,13 @@ result = tree; } + public void visitBindingPattern(JCBindingPattern tree) { + if (tree.vartype != null) { + tree.vartype = translate(tree.vartype, null); + } + result = tree; + } + public void visitSwitchExpression(JCSwitchExpression tree) { Type selsuper = types.supertype(tree.selector.type); boolean enumSwitch = selsuper != null && @@ -780,7 +787,7 @@ public void visitTypeTest(JCInstanceOf tree) { tree.expr = translate(tree.expr, null); - tree.clazz = translate(tree.clazz, null); + tree.pattern = translate(tree.pattern, null); result = tree; } diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeDiffer.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeDiffer.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeDiffer.java Wed Nov 27 10:21:22 2019 +0100 @@ -35,6 +35,7 @@ import com.sun.tools.javac.tree.JCTree.JCAssign; import com.sun.tools.javac.tree.JCTree.JCAssignOp; import com.sun.tools.javac.tree.JCTree.JCBinary; +import com.sun.tools.javac.tree.JCTree.JCBindingPattern; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCBreak; import com.sun.tools.javac.tree.JCTree.JCCase; @@ -253,6 +254,18 @@ } @Override + public void visitBindingPattern(JCBindingPattern tree) { + JCBindingPattern that = (JCBindingPattern) parameter; + result = + scan(tree.vartype, that.vartype) + && tree.name == that.name; + if (!result) { + return; + } + equiv.put(tree.symbol, that.symbol); + } + + @Override public void visitBlock(JCBlock tree) { JCBlock that = (JCBlock) parameter; result = tree.flags == that.flags && scan(tree.stats, that.stats); @@ -591,7 +604,7 @@ @Override public void visitTypeTest(JCInstanceOf tree) { JCInstanceOf that = (JCInstanceOf) parameter; - result = scan(tree.expr, that.expr) && scan(tree.clazz, that.clazz); + result = scan(tree.expr, that.expr) && scan(tree.pattern, that.pattern); } @Override diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeHasher.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeHasher.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeHasher.java Wed Nov 27 10:21:22 2019 +0100 @@ -106,6 +106,12 @@ } @Override + public void visitBindingPattern(JCTree.JCBindingPattern tree) { + symbolHashes.computeIfAbsent(tree.symbol, k -> symbolHashes.size()); + super.visitBindingPattern(tree); + } + + @Override public void visitVarDef(JCVariableDecl tree) { symbolHashes.computeIfAbsent(tree.sym, k -> symbolHashes.size()); super.visitVarDef(tree); diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/CRTable.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/CRTable.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/CRTable.java Wed Nov 27 10:21:22 2019 +0100 @@ -473,7 +473,7 @@ public void visitTypeTest(JCInstanceOf tree) { SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); sr.mergeWith(csp(tree.expr)); - sr.mergeWith(csp(tree.clazz)); + sr.mergeWith(csp(tree.pattern)); result = sr; } diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java Wed Nov 27 10:21:22 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,6 +50,7 @@ import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_String; import static com.sun.tools.javac.jvm.UninitializedType.*; import static com.sun.tools.javac.jvm.ClassWriter.StackMapTableFrame; +import java.util.Arrays; /** An internal structure that corresponds to the code attribute of * methods in a classfile. The class also provides some utility operations to @@ -2075,6 +2076,7 @@ lvar[adr] = v.dup(); v.closeRange(length); putVar(v); + fillLocalVarPosition(v); } else { v.removeLastRange(); } @@ -2106,20 +2108,31 @@ private void fillLocalVarPosition(LocalVar lv) { if (lv == null || lv.sym == null || lv.sym.isExceptionParameter()|| !lv.sym.hasTypeAnnotations()) return; - LocalVar.Range widestRange = lv.getWidestRange(); + LocalVar.Range[] validRanges = lv.aliveRanges.stream().filter(r -> r.closed() && r.length > 0).toArray(s -> new LocalVar.Range[s]); + if (validRanges.length == 0) + return ; + int[] lvarOffset = Arrays.stream(validRanges).mapToInt(r -> r.start_pc).toArray(); + int[] lvarLength = Arrays.stream(validRanges).mapToInt(r -> r.length).toArray(); + int[] lvarIndex = Arrays.stream(validRanges).mapToInt(r -> lv.reg).toArray(); for (Attribute.TypeCompound ta : lv.sym.getRawTypeAttributes()) { TypeAnnotationPosition p = ta.position; - if (widestRange.closed() && widestRange.length > 0) { - p.lvarOffset = new int[] { (int)widestRange.start_pc }; - p.lvarLength = new int[] { (int)widestRange.length }; - p.lvarIndex = new int[] { (int)lv.reg }; - p.isValidOffset = true; - } else { - p.isValidOffset = false; - } + p.lvarOffset = appendArray(p.lvarOffset, lvarOffset); + p.lvarLength = appendArray(p.lvarLength, lvarLength); + p.lvarIndex = appendArray(p.lvarIndex, lvarIndex); + p.isValidOffset = true; } } + private int[] appendArray(int[] source, int[] append) { + if (source == null || source.length == 0) return append; + + int[] result = new int[source.length + append.length]; + + System.arraycopy(source, 0, result, 0, source.length); + System.arraycopy(append, 0, result, source.length, append.length); + return result; + } + // Method to be called after compressCatchTable to // fill in the exception table index for type // annotations on exception parameters. diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java Wed Nov 27 10:21:22 2019 +0100 @@ -2216,7 +2216,7 @@ public void visitTypeTest(JCInstanceOf tree) { genExpr(tree.expr, tree.expr.type).load(); setTypeAnnotationPositions(tree.pos); - code.emitop2(instanceof_, makeRef(tree.pos(), tree.clazz.type)); + code.emitop2(instanceof_, makeRef(tree.pos(), tree.pattern.type)); result = items.makeStackItem(syms.booleanType); } diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java Wed Nov 27 10:21:22 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1558,6 +1558,12 @@ env.tree = transTypes.translateTopLevelClass(env.tree, localMake); compileStates.put(env, CompileState.TRANSTYPES); + if (shouldStop(CompileState.TRANSPATTERNS)) + return; + + env.tree = TransPatterns.instance(context).translateTopLevelClass(env, env.tree, localMake); + compileStates.put(env, CompileState.TRANSPATTERNS); + if (Feature.LAMBDA.allowedInSource(source) && scanner.hasLambdas) { if (shouldStop(CompileState.UNLAMBDA)) return; diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java Wed Nov 27 10:21:22 2019 +0100 @@ -893,6 +893,7 @@ /* Expression2Rest = {infixop Expression3} * | Expression3 instanceof Type + * | Expression3 instanceof Pattern * infixop = "||" * | "&&" * | "|" @@ -915,13 +916,24 @@ Token topOp = Tokens.DUMMY; while (prec(token.kind) >= minprec) { opStack[top] = topOp; - top++; - topOp = token; - nextToken(); - odStack[top] = (topOp.kind == INSTANCEOF) ? parseType() : term3(); + + if (token.kind == INSTANCEOF) { + int pos = token.pos; + nextToken(); + JCTree pattern = parseType(); + if (token.kind == IDENTIFIER) { + checkSourceLevel(token.pos, Feature.PATTERN_MATCHING_IN_INSTANCEOF); + pattern = toP(F.at(token.pos).BindingPattern(ident(), pattern)); + } + odStack[top] = F.at(pos).TypeTest(odStack[top], pattern); + } else { + topOp = token; + nextToken(); + top++; + odStack[top] = term3(); + } while (top > 0 && prec(topOp.kind) >= prec(token.kind)) { - odStack[top-1] = makeOp(topOp.pos, topOp.kind, odStack[top-1], - odStack[top]); + odStack[top - 1] = F.at(topOp.pos).Binary(optag(topOp.kind), odStack[top - 1], odStack[top]); top--; topOp = opStack[top]; } @@ -938,19 +950,6 @@ return t; } //where - /** Construct a binary or type test node. - */ - private JCExpression makeOp(int pos, - TokenKind topOp, - JCExpression od1, - JCExpression od2) - { - if (topOp == INSTANCEOF) { - return F.at(pos).TypeTest(od1, od2); - } else { - return F.at(pos).Binary(optag(topOp), od1, od2); - } - } /** If tree is a concatenation of string literals, replace it * by a single literal representing the concatenated string. */ diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties Wed Nov 27 10:21:22 2019 +0100 @@ -546,6 +546,10 @@ auto-closeable resource {0} may not be assigned # 0: symbol +compiler.err.pattern.binding.may.not.be.assigned=\ + pattern binding {0} may not be assigned + +# 0: symbol compiler.err.multicatch.parameter.may.not.be.assigned=\ multi-catch parameter {0} may not be assigned @@ -1416,6 +1420,10 @@ compiler.misc.varargs.trustme.on.reifiable.varargs=\ Varargs element type {0} is reifiable. +# 0: type, 1: type +compiler.err.instanceof.reifiable.not.safe=\ + {0} cannot be safely cast to {1} + # 0: symbol compiler.misc.varargs.trustme.on.non.varargs.meth=\ Method {0} is not a varargs method. @@ -2909,6 +2917,12 @@ compiler.misc.feature.var.syntax.in.implicit.lambda=\ var syntax in implicit lambdas +compiler.misc.feature.pattern.matching.instanceof=\ + pattern matching in instanceof + +compiler.misc.feature.reifiable.types.instanceof=\ + reifiable types in instanceof + compiler.warn.underscore.as.identifier=\ as of release 9, ''_'' is a keyword, and may not be used as an identifier @@ -3399,6 +3413,9 @@ compiler.err.illegal.argument.for.option=\ illegal argument for {0}: {1} +compiler.err.match.binding.exists=\ + illegal attempt to redefine an existing match binding + compiler.err.switch.null.not.allowed=\ null label in case is not allowed diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java Wed Nov 27 10:21:22 2019 +0100 @@ -38,6 +38,7 @@ import com.sun.tools.javac.code.Directive.RequiresDirective; import com.sun.tools.javac.code.Scope.*; import com.sun.tools.javac.code.Symbol.*; +import com.sun.tools.javac.comp.MatchBindingsComputer.BindingSymbol; import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.DefinedBy.Api; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; @@ -238,6 +239,10 @@ */ TYPETEST, + /** Patterns. + */ + BINDINGPATTERN, + /** Indexed array expressions, of type Indexed. */ INDEXED, @@ -2135,10 +2140,10 @@ */ public static class JCInstanceOf extends JCExpression implements InstanceOfTree { public JCExpression expr; - public JCTree clazz; - protected JCInstanceOf(JCExpression expr, JCTree clazz) { + public JCTree pattern; + protected JCInstanceOf(JCExpression expr, JCTree pattern) { this.expr = expr; - this.clazz = clazz; + this.pattern = pattern; } @Override public void accept(Visitor v) { v.visitTypeTest(this); } @@ -2146,7 +2151,13 @@ @DefinedBy(Api.COMPILER_TREE) public Kind getKind() { return Kind.INSTANCE_OF; } @DefinedBy(Api.COMPILER_TREE) - public JCTree getType() { return clazz; } + public JCTree getType() { return pattern instanceof JCPattern ? pattern.hasTag(BINDINGPATTERN) ? ((JCBindingPattern) pattern).vartype : null : pattern; } + + @Override @DefinedBy(Api.COMPILER_TREE) + public JCPattern getPattern() { + return pattern instanceof JCPattern ? (JCPattern) pattern : null; + } + @DefinedBy(Api.COMPILER_TREE) public JCExpression getExpression() { return expr; } @Override @DefinedBy(Api.COMPILER_TREE) @@ -2160,6 +2171,60 @@ } /** + * Pattern matching forms. + */ + public static abstract class JCPattern extends JCTree + implements PatternTree { + public JCExpression constExpression() { + return null; + } + } + + public static class JCBindingPattern extends JCPattern + implements BindingPatternTree { + public Name name; + public BindingSymbol symbol; + public JCTree vartype; + + protected JCBindingPattern(Name name, BindingSymbol symbol, JCTree vartype) { + this.name = name; + this.symbol = symbol; + this.vartype = vartype; + } + + @DefinedBy(Api.COMPILER_TREE) + public Name getBinding() { + return name; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Tree getType() { + return vartype; + } + + @Override + public void accept(Visitor v) { + v.visitBindingPattern(this); + } + + @DefinedBy(Api.COMPILER_TREE) + public Kind getKind() { + return Kind.BINDING_PATTERN; + } + + @Override + @DefinedBy(Api.COMPILER_TREE) + public R accept(TreeVisitor v, D d) { + return v.visitBindingPattern(this, d); + } + + @Override + public Tag getTag() { + return BINDINGPATTERN; + } + } + + /** * An array selection */ public static class JCArrayAccess extends JCExpression implements ArrayAccessTree { @@ -3133,6 +3198,7 @@ JCBinary Binary(Tag opcode, JCExpression lhs, JCExpression rhs); JCTypeCast TypeCast(JCTree expr, JCExpression type); JCInstanceOf TypeTest(JCExpression expr, JCTree clazz); + JCBindingPattern BindingPattern(Name name, JCTree vartype); JCArrayAccess Indexed(JCExpression indexed, JCExpression index); JCFieldAccess Select(JCExpression selected, Name selector); JCIdent Ident(Name idname); @@ -3197,6 +3263,7 @@ public void visitBinary(JCBinary that) { visitTree(that); } public void visitTypeCast(JCTypeCast that) { visitTree(that); } public void visitTypeTest(JCInstanceOf that) { visitTree(that); } + public void visitBindingPattern(JCBindingPattern that) { visitTree(that); } public void visitIndexed(JCArrayAccess that) { visitTree(that); } public void visitSelect(JCFieldAccess that) { visitTree(that); } public void visitReference(JCMemberReference that) { visitTree(that); } diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java Wed Nov 27 10:21:22 2019 +0100 @@ -234,6 +234,14 @@ printExprs(trees, ", "); } + + /** Derived visitor method: print pattern. + */ + + public void printPattern(JCTree tree) throws IOException { + printExpr(tree); + } + /** Derived visitor method: print list of statements, each on a separate line. */ public void printStats(List trees) throws IOException { @@ -877,6 +885,16 @@ } } + public void visitBindingPattern(JCBindingPattern patt) { + try { + printExpr(patt.vartype); + print(" "); + print(patt.name); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + public void visitSynchronized(JCSynchronized tree) { try { print("synchronized "); @@ -1283,7 +1301,11 @@ open(prec, TreeInfo.ordPrec); printExpr(tree.expr, TreeInfo.ordPrec); print(" instanceof "); - printExpr(tree.clazz, TreeInfo.ordPrec + 1); + if (tree.pattern instanceof JCPattern) { + printPattern(tree.pattern); + } else { + printExpr(tree.getType(), TreeInfo.ordPrec + 1); + } close(prec, TreeInfo.ordPrec); } catch (IOException e) { throw new UncheckedIOException(e); diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java Wed Nov 27 10:21:22 2019 +0100 @@ -26,7 +26,6 @@ package com.sun.tools.javac.tree; import com.sun.source.tree.*; -import com.sun.source.tree.Tree.Kind; import com.sun.tools.javac.tree.JCTree.*; import com.sun.tools.javac.util.DefinedBy; import com.sun.tools.javac.util.DefinedBy.Api; @@ -481,8 +480,15 @@ public JCTree visitInstanceOf(InstanceOfTree node, P p) { JCInstanceOf t = (JCInstanceOf) node; JCExpression expr = copy(t.expr, p); - JCTree clazz = copy(t.clazz, p); - return M.at(t.pos).TypeTest(expr, clazz); + JCTree pattern = copy(t.pattern, p); + return M.at(t.pos).TypeTest(expr, pattern); + } + + @DefinedBy(Api.COMPILER_TREE) + public JCTree visitBindingPattern(BindingPatternTree node, P p) { + JCBindingPattern t = (JCBindingPattern) node; + JCTree vartype = copy(t.vartype, p); + return M.at(t.pos).BindingPattern(t.name, vartype); } @DefinedBy(Api.COMPILER_TREE) diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java Wed Nov 27 10:21:22 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -490,6 +490,10 @@ return getStartPos(node.vartype); } } + case BINDINGPATTERN: { + JCBindingPattern node = (JCBindingPattern)tree; + return getStartPos(node.vartype); + } case ERRONEOUS: { JCErroneous node = (JCErroneous)tree; if (node.errs != null && node.errs.nonEmpty()) @@ -574,7 +578,7 @@ case TYPECAST: return getEndPos(((JCTypeCast) tree).expr, endPosTable); case TYPETEST: - return getEndPos(((JCInstanceOf) tree).clazz, endPosTable); + return getEndPos(((JCInstanceOf) tree).pattern, endPosTable); case WHILELOOP: return getEndPos(((JCWhileLoop) tree).body, endPosTable); case ANNOTATED_TYPE: @@ -847,6 +851,8 @@ if (node.type != null) return node.type.tsym; return null; + case BINDINGPATTERN: + return ((JCBindingPattern) node).symbol; default: return null; } @@ -1225,4 +1231,5 @@ public static boolean isPackageInfo(JCCompilationUnit tree) { return tree.sourcefile.isNameCompatible("package-info", JavaFileObject.Kind.SOURCE); } + } diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java Wed Nov 27 10:21:22 2019 +0100 @@ -29,7 +29,6 @@ import com.sun.source.tree.CaseTree; import com.sun.source.tree.ModuleTree.ModuleKind; -import com.sun.source.tree.Tree.Kind; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Attribute.UnresolvedClass; import com.sun.tools.javac.code.Symbol.*; @@ -465,6 +464,12 @@ return tree; } + public JCBindingPattern BindingPattern(Name name, JCTree vartype) { + JCBindingPattern tree = new JCBindingPattern(name, null, vartype); + tree.pos = pos; + return tree; + } + public JCArrayAccess Indexed(JCExpression indexed, JCExpression index) { JCArrayAccess tree = new JCArrayAccess(indexed, index); tree.pos = pos; diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java Wed Nov 27 10:21:22 2019 +0100 @@ -299,7 +299,12 @@ public void visitTypeTest(JCInstanceOf tree) { scan(tree.expr); - scan(tree.clazz); + scan(tree.pattern); + } + + public void visitBindingPattern(JCBindingPattern tree) { + if (tree.vartype != null) + scan(tree.vartype); } public void visitIndexed(JCArrayAccess tree) { diff -r 3b1afeb49a20 -r c887851da33d src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeTranslator.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeTranslator.java Wed Nov 27 10:19:45 2019 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeTranslator.java Wed Nov 27 10:21:22 2019 +0100 @@ -354,7 +354,12 @@ public void visitTypeTest(JCInstanceOf tree) { tree.expr = translate(tree.expr); - tree.clazz = translate(tree.clazz); + tree.pattern = translate(tree.pattern); + result = tree; + } + + public void visitBindingPattern(JCBindingPattern tree) { + tree.vartype = translate(tree.vartype); result = tree; } diff -r 3b1afeb49a20 -r c887851da33d test/jdk/java/lang/invoke/CallSiteTest.java --- a/test/jdk/java/lang/invoke/CallSiteTest.java Wed Nov 27 10:19:45 2019 +0100 +++ b/test/jdk/java/lang/invoke/CallSiteTest.java Wed Nov 27 10:21:22 2019 +0100 @@ -24,10 +24,11 @@ /** * @test * @summary smoke tests for CallSite + * @library /test/lib * * @build indify.Indify * @compile CallSiteTest.java - * @run main/othervm/timeout=3600 -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies + * @run main/othervm/timeout=3600 -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies -Xbatch * indify.Indify * --expand-properties --classpath ${test.classes} * --java test.java.lang.invoke.CallSiteTest @@ -40,6 +41,7 @@ import java.lang.invoke.*; import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; +import static jdk.test.lib.Asserts.*; public class CallSiteTest { private static final Class CLASS = CallSiteTest.class; @@ -51,16 +53,19 @@ static { try { + mh_foo = lookup().findStatic(CLASS, "foo", methodType(int.class, int.class, int.class)); mh_bar = lookup().findStatic(CLASS, "bar", methodType(int.class, int.class, int.class)); mcs = new MutableCallSite(mh_foo); vcs = new VolatileCallSite(mh_foo); } catch (Exception e) { e.printStackTrace(); + throw new Error(e); } } public static void main(String... av) throws Throwable { + testConstantCallSite(); testMutableCallSite(); testVolatileCallSite(); } @@ -69,9 +74,61 @@ private static final int RESULT1 = 762786192; private static final int RESULT2 = -21474836; - private static void assertEquals(int expected, int actual) { - if (expected != actual) - throw new AssertionError("expected: " + expected + ", actual: " + actual); + static final CallSite MCS = new MutableCallSite(methodType(void.class)); + static final MethodHandle MCS_INVOKER = MCS.dynamicInvoker(); + + static void test(boolean shouldThrow) { + try { + MCS_INVOKER.invokeExact(); + if (shouldThrow) { + throw new AssertionError("should throw"); + } + } catch (IllegalStateException ise) { + if (!shouldThrow) { + throw new AssertionError("should not throw", ise); + } + } catch (Throwable e) { + throw new Error(e); + } + } + + static class MyCCS extends ConstantCallSite { + public MyCCS(MethodType targetType, MethodHandle createTargetHook) throws Throwable { + super(targetType, createTargetHook); + } + } + + private static MethodHandle testConstantCallSiteHandler(CallSite cs, CallSite[] holder) throws Throwable { + holder[0] = cs; // capture call site instance for subsequent checks + + MethodType csType = cs.type(); // should not throw on partially constructed instance + + // Truly dynamic invoker for constant call site + MethodHandle getTarget = lookup().findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class)) + .bindTo(cs); + MethodHandle invoker = MethodHandles.exactInvoker(csType); + MethodHandle target = MethodHandles.foldArguments(invoker, getTarget); + + MCS.setTarget(target); + // warmup + for (int i = 0; i < 20_000; i++) { + test(true); // should throw IllegalStateException + } + + return MethodHandles.empty(csType); // initialize cs with an empty method handle + } + + private static void testConstantCallSite() throws Throwable { + CallSite[] holder = new CallSite[1]; + MethodHandle handler = lookup().findStatic(CLASS, "testConstantCallSiteHandler", MethodType.methodType(MethodHandle.class, CallSite.class, CallSite[].class)); + handler = MethodHandles.insertArguments(handler, 1, new Object[] { holder } ); + + CallSite ccs = new MyCCS(MCS.type(), handler); // trigger call to handler + + if (ccs != holder[0]) { + throw new AssertionError("different call site instances"); + } + test(false); // should not throw } private static void testMutableCallSite() throws Throwable { @@ -83,11 +140,11 @@ for (int n = 0; n < 2; n++) { mcs.setTarget(mh_foo); for (int i = 0; i < 5; i++) { - assertEquals(RESULT1, runMutableCallSite()); + assertEQ(RESULT1, runMutableCallSite()); } mcs.setTarget(mh_bar); for (int i = 0; i < 5; i++) { - assertEquals(RESULT2, runMutableCallSite()); + assertEQ(RESULT2, runMutableCallSite()); } } } @@ -100,11 +157,11 @@ for (int n = 0; n < 2; n++) { vcs.setTarget(mh_foo); for (int i = 0; i < 5; i++) { - assertEquals(RESULT1, runVolatileCallSite()); + assertEQ(RESULT1, runVolatileCallSite()); } vcs.setTarget(mh_bar); for (int i = 0; i < 5; i++) { - assertEquals(RESULT2, runVolatileCallSite()); + assertEQ(RESULT2, runVolatileCallSite()); } } } diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/annotations/typeAnnotations/classfile/ClassfileTestHelper.java --- a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/ClassfileTestHelper.java Wed Nov 27 10:19:45 2019 +0100 +++ b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/ClassfileTestHelper.java Wed Nov 27 10:21:22 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,12 +27,14 @@ import java.util.List; import com.sun.tools.classfile.*; +import java.util.ArrayList; public class ClassfileTestHelper { int expected_tinvisibles = 0; int expected_tvisibles = 0; int expected_invisibles = 0; int expected_visibles = 0; + List extraOptions = List.of(); //Makes debugging much easier. Set to 'false' for less output. public Boolean verbose = true; @@ -48,8 +50,9 @@ } File compile(File f) { - int rc = com.sun.tools.javac.Main.compile(new String[] { - "-g", f.getPath() }); + List options = new ArrayList<>(List.of("-g", f.getPath())); + options.addAll(extraOptions); + int rc = com.sun.tools.javac.Main.compile(options.toArray(new String[0])); if (rc != 0) throw new Error("compilation failed. rc=" + rc); String path = f.getPath(); diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/annotations/typeAnnotations/classfile/CombinationsTargetTest2.java --- a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/CombinationsTargetTest2.java Wed Nov 27 10:19:45 2019 +0100 +++ b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/CombinationsTargetTest2.java Wed Nov 27 10:21:22 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,9 +30,13 @@ import com.sun.tools.classfile.*; import java.io.File; +import java.util.List; public class CombinationsTargetTest2 extends ClassfileTestHelper { + private static final String JDK_VERSION = + Integer.toString(Runtime.getRuntime().version().feature()); + // Test count helps identify test case in event of failure. int testcount = 0; @@ -45,7 +49,9 @@ src5("(repeating) type annotations on field in anonymous class", false), src6("(repeating) type annotations on void method declaration", false), src7("(repeating) type annotations in use of instanceof", true), - src8("(repeating) type annotations in use of instanceof in method", true); + src7p("(repeating) type annotations in use of instanceof with type test pattern", true), + src8("(repeating) type annotations in use of instanceof in method", true), + src8p("(repeating) type annotations in use of instanceof with type test pattern in method", true); String description; Boolean local; @@ -92,8 +98,12 @@ test( 0, 0, 0, 2, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src6); test( 2, 0, 0, 0, As, BDs, ABMix, "CLASS", et, ++testrun, srce.src7); test( 0, 2, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src7); + test( 2, 0, 0, 0, As, BDs, ABMix, "CLASS", et, ++testrun, srce.src7p); + test( 0, 2, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src7p); test( 4, 0, 0, 0, As, BDs, ABMix, "CLASS", et, ++testrun, srce.src8); test( 0, 4, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src8); + test( 4, 0, 0, 0, As, BDs, ABMix, "CLASS", et, ++testrun, srce.src8p); + test( 0, 4, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src8p); break; case "FIELD": test( 8, 0, 0, 0, As, BDs, ABMix, "CLASS", et, ++testrun, srce.src1); @@ -122,6 +132,7 @@ expected_tinvisibles = tinv; expected_visibles = vis; expected_invisibles = inv; + extraOptions = List.of(); File testFile = null; String tname="Test" + N.toString(); hasInnerClass=false; @@ -385,6 +396,24 @@ "\n\n"; hasInnerClass=false; break; + case src7p: // (repeating) type annotations in use of instanceof with type test pattern + /* + * class Test10{ + * String data = "test"; + * boolean dataIsString = ( data instanceof @A @B @A @B String str); + * } + */ + source = new String( source + + "// " + src.description + "\n" + + "class "+ testname + "{\n" + + " String data = \"test\";\n" + + " boolean dataIsString = ( data instanceof _As_ _Bs_ String str && str.isEmpty());\n" + + "}\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) + + "\n\n"; + extraOptions = List.of("--enable-preview", + "-source", JDK_VERSION); + hasInnerClass=false; + break; case src8: // (repeating) type annotations in use of instanceof /* * class Test20{ @@ -411,6 +440,34 @@ "\n\n"; hasInnerClass=false; break; + case src8p: // (repeating) type annotations in use of instanceof with type test pattern + /* + * class Test20{ + * String data = "test"; + * Boolean isString() { + * if( data instanceof @A @B @A @B String ) + * return true; + * else + * return( data instanceof @A @B @A @B String ); + * } + * } + */ + source = new String( source + + "// " + src.description + "\n" + + "class "+ testname + "{\n" + + " String data = \"test\";\n" + + " Boolean isString() { \n" + + " if( data instanceof _As_ _Bs_ String str)\n" + + " return true;\n" + + " else\n" + + " return( data instanceof _As_ _Bs_ String str && str.isEmpty());\n" + + " }\n" + + "}\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) + + "\n\n"; + extraOptions = List.of("--enable-preview", + "-source", JDK_VERSION); + hasInnerClass=false; + break; } return imports + source; diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/annotations/typeAnnotations/classfile/Patterns.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/Patterns.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Verify type annotation on binding patterns + * @library /tools/lib + * @modules java.compiler + * jdk.jdeps/com.sun.tools.javap + * @build toolbox.JavapTask + * @compile --enable-preview -source ${jdk.version} Patterns.java + * @run main/othervm --enable-preview Patterns + */ + +import java.lang.annotation.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import toolbox.JavapTask; +import toolbox.Task; +import toolbox.ToolBox; + +public class Patterns { + + private ToolBox tb = new ToolBox(); + + public static void main(String[] args) throws Exception { + new Patterns().run(); + } + + public void run() throws Exception { + String out = new JavapTask(tb) + .options("-private", + "-verbose") + .classpath(System.getProperty("test.classes")) + .classes("Patterns$SimpleBindingPattern") + .run() + .getOutputLines(Task.OutputKind.DIRECT) + .stream() + .collect(Collectors.joining("\n")); + + String constantPool = out.substring(0, out.indexOf('{')); + + out = out.replaceAll("(?ms) *Code:.*?\n( *RuntimeInvisibleTypeAnnotations:)", "$1"); + out = out.substring(out.indexOf('{')); + out = out.substring(0, out.lastIndexOf('}') + 1); + + String A = snipCPNumber(constantPool, "LPatterns$SimpleBindingPattern$A;"); + String CA = snipCPNumber(constantPool, "LPatterns$SimpleBindingPattern$CA;"); + String value = snipCPNumber(constantPool, "value"); + + String expected = """ + { + private static final java.lang.Object o; + descriptor: Ljava/lang/Object; + flags: (0x001a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL + + private static final boolean B1s; + descriptor: Z + flags: (0x001a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL + + private static final boolean B1m; + descriptor: Z + flags: (0x001a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL + + private final boolean B2s; + descriptor: Z + flags: (0x0012) ACC_PRIVATE, ACC_FINAL + + private final boolean B2m; + descriptor: Z + flags: (0x0012) ACC_PRIVATE, ACC_FINAL + + public Patterns$SimpleBindingPattern(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + RuntimeInvisibleTypeAnnotations: + 0: #_A_(): LOCAL_VARIABLE, {start_pc=257, length=18, index=2} + Patterns$SimpleBindingPattern$A + 1: #_CA_(#_value_=[@#_A_(),@#_A_()]): LOCAL_VARIABLE, {start_pc=297, length=19, index=3} + Patterns$SimpleBindingPattern$CA( + value=[@Patterns$SimpleBindingPattern$A,@Patterns$SimpleBindingPattern$A] + ) + 2: #_A_(): LOCAL_VARIABLE, {start_pc=22, length=18, index=1} + Patterns$SimpleBindingPattern$A + 3: #_CA_(#_value_=[@#_A_(),@#_A_()]): LOCAL_VARIABLE, {start_pc=62, length=18, index=1} + Patterns$SimpleBindingPattern$CA( + value=[@Patterns$SimpleBindingPattern$A,@Patterns$SimpleBindingPattern$A] + ) + 4: #_A_(): LOCAL_VARIABLE, {start_pc=101, length=18, index=2} + Patterns$SimpleBindingPattern$A + 5: #_CA_(#_value_=[@#_A_(),@#_A_()]): LOCAL_VARIABLE, {start_pc=141, length=19, index=3} + Patterns$SimpleBindingPattern$CA( + value=[@Patterns$SimpleBindingPattern$A,@Patterns$SimpleBindingPattern$A] + ) + 6: #_A_(): LOCAL_VARIABLE, {start_pc=179, length=18, index=2} + Patterns$SimpleBindingPattern$A + 7: #_CA_(#_value_=[@#_A_(),@#_A_()]): LOCAL_VARIABLE, {start_pc=219, length=19, index=3} + Patterns$SimpleBindingPattern$CA( + value=[@Patterns$SimpleBindingPattern$A,@Patterns$SimpleBindingPattern$A] + ) + + void testPatterns(); + descriptor: ()V + flags: (0x0000) + RuntimeInvisibleTypeAnnotations: + 0: #_A_(): LOCAL_VARIABLE, {start_pc=17, length=18, index=2} + Patterns$SimpleBindingPattern$A + 1: #_CA_(#_value_=[@#_A_(),@#_A_()]): LOCAL_VARIABLE, {start_pc=57, length=19, index=3} + Patterns$SimpleBindingPattern$CA( + value=[@Patterns$SimpleBindingPattern$A,@Patterns$SimpleBindingPattern$A] + ) + + void testPatternsDesugared(); + descriptor: ()V + flags: (0x0000) + RuntimeInvisibleTypeAnnotations: + 0: #_A_(): LOCAL_VARIABLE, {start_pc=17, length=15, index=1; start_pc=51, length=15, index=1} + Patterns$SimpleBindingPattern$A + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + RuntimeInvisibleTypeAnnotations: + 0: #_A_(): LOCAL_VARIABLE, {start_pc=22, length=18, index=0} + Patterns$SimpleBindingPattern$A + 1: #_CA_(#_value_=[@#_A_(),@#_A_()]): LOCAL_VARIABLE, {start_pc=61, length=18, index=0} + Patterns$SimpleBindingPattern$CA( + value=[@Patterns$SimpleBindingPattern$A,@Patterns$SimpleBindingPattern$A] + ) + 2: #_A_(): LOCAL_VARIABLE, {start_pc=100, length=18, index=1} + Patterns$SimpleBindingPattern$A + 3: #_CA_(#_value_=[@#_A_(),@#_A_()]): LOCAL_VARIABLE, {start_pc=137, length=18, index=2} + Patterns$SimpleBindingPattern$CA( + value=[@Patterns$SimpleBindingPattern$A,@Patterns$SimpleBindingPattern$A] + ) + }""".replace("_A_", A).replace("_CA_", CA).replace("_value_", value); + + if (!expected.equals(out)) { + throw new AssertionError("Unexpected output:\n" + out + "\nexpected:\n" + expected); + } + } + + private String snipCPNumber(String constantPool, String expectedConstant) { + Matcher m = Pattern.compile("#([0-9]+).*" + Pattern.quote(expectedConstant)) + .matcher(constantPool); + if (!m.find()) { + throw new AssertionError("Cannot find constant pool item"); + } + + return m.group(1); + } + + /*********************** Test class *************************/ + static class SimpleBindingPattern { + @Target(ElementType.TYPE_USE) + @Repeatable(CA.class) + @interface A {} + @Target(ElementType.TYPE_USE) + @interface CA { + public A[] value(); + } + + private static final Object o = ""; + private static final boolean B1s = o instanceof @A String s && s.isEmpty(); + private static final boolean B1m = o instanceof @A @A String s && s.isEmpty(); + private final boolean B2s = o instanceof @A String s && s.isEmpty(); + private final boolean B2m = o instanceof @A @A String s && s.isEmpty(); + + static { + boolean B3s = o instanceof @A String s && s.isEmpty(); + boolean B3m = o instanceof @A @A String s && s.isEmpty(); + } + + { + boolean B4s = o instanceof @A String s && s.isEmpty(); + boolean B4m = o instanceof @A @A String s && s.isEmpty(); + } + + { + boolean B5s = o instanceof @A String s && s.isEmpty(); + boolean B5m = o instanceof @A @A String s && s.isEmpty(); + } + + public SimpleBindingPattern() { + boolean B6s = o instanceof @A String s && s.isEmpty(); + boolean B6m = o instanceof @A @A String s && s.isEmpty(); + } + + void testPatterns() { + boolean B7s = o instanceof @A String s && s.isEmpty(); + boolean B7m = o instanceof @A @A String s && s.isEmpty(); + } + + void testPatternsDesugared() { + @A String s; + boolean B8s = o instanceof String && (s = (String) o) == s && s.isEmpty(); + boolean B8sx = o instanceof String && (s = (String) o) == s && s.isEmpty(); + } + } +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/api/TestGetElementReference.java --- a/test/langtools/tools/javac/api/TestGetElementReference.java Wed Nov 27 10:19:45 2019 +0100 +++ b/test/langtools/tools/javac/api/TestGetElementReference.java Wed Nov 27 10:21:22 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,6 +51,9 @@ public class TestGetElementReference { + private static final String JDK_VERSION = + Integer.toString(Runtime.getRuntime().version().feature()); + public static void main(String... args) throws IOException { analyze("TestGetElementReferenceData.java"); analyze("mod/module-info.java", "mod/api/pkg/Api.java"); @@ -66,7 +69,10 @@ } } DiagnosticCollector diagnostics = new DiagnosticCollector<>(); - JavacTask ct = (JavacTask) ToolProvider.getSystemJavaCompiler().getTask(null, null, diagnostics, Arrays.asList("-Xjcov"), null, files); + List options = List.of("-Xjcov", + "--enable-preview", + "-source", JDK_VERSION); + JavacTask ct = (JavacTask) ToolProvider.getSystemJavaCompiler().getTask(null, null, diagnostics, options, null, files); Trees trees = Trees.instance(ct); CompilationUnitTree cut = ct.parse().iterator().next(); diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/api/TestGetElementReferenceData.java --- a/test/langtools/tools/javac/api/TestGetElementReferenceData.java Wed Nov 27 10:19:45 2019 +0100 +++ b/test/langtools/tools/javac/api/TestGetElementReferenceData.java Wed Nov 27 10:21:22 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,8 @@ java.util.List< /*getElement:INTERFACE:java.util.List*/ String> l; utility/*getElement:METHOD:test.TestGetElementReferenceData.Base.utility()*/(); target(TestGetElementReferenceData :: test/*getElement:METHOD:test.TestGetElementReferenceData.test()*/); + Object/*getElement:CLASS:java.lang.Object*/ o = null; + if (o/*getElement:LOCAL_VARIABLE:o*/ instanceof String/*getElement:CLASS:java.lang.String*/ str/*getElement:LOCAL_VARIABLE:str*/) ; } private static void target(Runnable r) { r.run(); } public static class Base { diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/diags/examples/InstanceofReifiableNotSafe.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/diags/examples/InstanceofReifiableNotSafe.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// key: compiler.err.instanceof.reifiable.not.safe +// key: compiler.note.preview.filename +// key: compiler.note.preview.recompile +// options: --enable-preview -source ${jdk.version} + +import java.util.List; + +class InstanceofReifiableNotSafe { + boolean test(Object o) { + return o instanceof List l; + } +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/diags/examples/MatchBindingExists.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/diags/examples/MatchBindingExists.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// key: compiler.err.match.binding.exists +// key: compiler.note.preview.filename +// key: compiler.note.preview.recompile +// options: --enable-preview -source ${jdk.version} + +class MatchBindingExists { + public void test(Object o1, Object o2) { + if (o1 instanceof String k && o2 instanceof Integer k) {} + } +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/diags/examples/PatternBindingMayNotBeAssigned.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/diags/examples/PatternBindingMayNotBeAssigned.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// key: compiler.err.pattern.binding.may.not.be.assigned +// key: compiler.note.preview.filename +// key: compiler.note.preview.recompile +// options: --enable-preview -source ${jdk.version} + +class ResourceMayNotBeAssigned { + void m(Object o) { + if (o instanceof String s) { + s = ""; + } + } +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/diags/examples/PatternMatchingInstanceof.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/diags/examples/PatternMatchingInstanceof.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// key: compiler.misc.feature.pattern.matching.instanceof +// key: compiler.warn.preview.feature.use +// options: --enable-preview -source ${jdk.version} -Xlint:preview + +class PatternMatchingInstanceof { + boolean m(Object o) { + return o instanceof String s && s.isEmpty(); + } +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/diags/examples/ReifiableTypesInstanceof.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/diags/examples/ReifiableTypesInstanceof.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// key: compiler.misc.feature.reifiable.types.instanceof +// key: compiler.warn.preview.feature.use.plural +// options: --enable-preview -source ${jdk.version} -Xlint:preview + +class PatternMatchingInstanceof { + boolean m(I i) { + return i instanceof C; + } + interface I {} + class C implements I {} +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/lambda/deduplication/Deduplication.java --- a/test/langtools/tools/javac/lambda/deduplication/Deduplication.java Wed Nov 27 10:19:45 2019 +0100 +++ b/test/langtools/tools/javac/lambda/deduplication/Deduplication.java Wed Nov 27 10:21:22 2019 +0100 @@ -163,6 +163,9 @@ group((Function) x -> switch (x) { default: yield x; }, (Function) x -> switch (x) { default: yield x; }); + + group((Function) x -> x instanceof Integer i ? i : -1, + (Function) x -> x instanceof Integer i ? i : -1); } void f() {} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/lib/DPrinter.java --- a/test/langtools/tools/javac/lib/DPrinter.java Wed Nov 27 10:19:45 2019 +0100 +++ b/test/langtools/tools/javac/lib/DPrinter.java Wed Nov 27 10:21:22 2019 +0100 @@ -880,7 +880,7 @@ @Override public void visitTypeTest(JCInstanceOf tree) { printTree("expr", tree.expr); - printTree("clazz", tree.clazz); + printTree("pattern", tree.pattern); } @Override diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/BindingsExistTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/BindingsExistTest.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,30 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8231827 + * @summary Clashing bindings are reported correctly + * @compile/fail/ref=BindingsExistTest.out -XDrawDiagnostics --enable-preview -source ${jdk.version} BindingsExistTest.java + */ +public class BindingsExistTest { + public void t(Object o1, Object o2) { + if (o1 instanceof String k && o2 instanceof Integer k) {} + + if (o1 instanceof String k || o2 instanceof Integer k) {} + + if (!(o1 instanceof String k)) { + return ; + } + if (o1 instanceof Integer k) {} + + String s2 = ""; + if (o1 instanceof String s2) {} + + if (o1 instanceof String s3) { + String s3 = ""; + } + + if (!(o1 instanceof String s4)) { + return ; + } + String s4 = ""; + } +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/BindingsExistTest.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/BindingsExistTest.out Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,9 @@ +BindingsExistTest.java:9:36: compiler.err.match.binding.exists +BindingsExistTest.java:11:36: compiler.err.match.binding.exists +BindingsExistTest.java:16:35: compiler.err.already.defined: kindname.variable, k, kindname.method, t(java.lang.Object,java.lang.Object) +BindingsExistTest.java:19:34: compiler.err.already.defined: kindname.variable, s2, kindname.method, t(java.lang.Object,java.lang.Object) +BindingsExistTest.java:22:20: compiler.err.already.defined: kindname.variable, s3, kindname.method, t(java.lang.Object,java.lang.Object) +BindingsExistTest.java:28:16: compiler.err.already.defined: kindname.variable, s4, kindname.method, t(java.lang.Object,java.lang.Object) +- compiler.note.preview.filename: BindingsExistTest.java +- compiler.note.preview.recompile +6 errors diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/BindingsTest1.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/BindingsTest1.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8231827 + * @summary Basic tests for bindings from instanceof + * @compile --enable-preview -source ${jdk.version} BindingsTest1.java + * @run main/othervm --enable-preview BindingsTest1 + */ + +public class BindingsTest1 { + public static boolean Ktrue() { return true; } + public static void main(String[] args) { + Object o1 = "hello"; + Integer i = 42; + Object o2 = i; + Object o3 = "there"; + + + // Test for (e matches P).T = { binding variables in P } + if (o1 instanceof String s) { + s.length(); + } + + // Test for e1 && e2.T = union(e1.T, e2.T) + if (o1 instanceof String s && o2 instanceof Integer in) { + s.length(); + in.intValue(); + } + + // test for e1&&e2 - include e1.T in e2 + if (o1 instanceof String s && s.length()>0) { + System.out.print("done"); + } + + // Test for (e1 || e2).F = union(e1.F, e2.F) + if (!(o1 instanceof String s) || !(o3 instanceof Integer in)){ + } else { + s.length(); + i.intValue(); + } + + // Test for e1||e2 - include e1.F in e2 + + if (!(o1 instanceof String s) || s.length()>0) { + System.out.println("done"); + } + + // Test for e1 ? e2: e3 - include e1.T in e2 + if (o1 instanceof String s ? s.length()>0 : false) { + System.out.println("done"); + } + + // Test for e1 ? e2 : e3 - include e1.F in e3 + if (!(o1 instanceof String s) ? false : s.length()>0){ + System.out.println("done"); + } + + // Test for (!e).T = e.F + + if (!(!(o1 instanceof String s) || !(o3 instanceof Integer in))){ + s.length(); + i.intValue(); + } + + // Test for (!e).F = e.T + if (!(o1 instanceof String s)) { + + } else { + s.length(); + } + + L1: { + if (o1 instanceof String s) { + s.length(); + } else { + break L1; + } + s.length(); + } + + L2: { + if (!(o1 instanceof String s)) { + break L2; + } else { + s.length(); + } + s.length(); + } + + L4: { + if (!(o1 instanceof String s)) { + break L4; + } + s.length(); + } + + { + while (!(o1 instanceof String s)) { + } + + s.length(); + } + + L5: { + while (!(o1 instanceof String s)) { + } + + s.length(); + } + + { + L6: for ( ;!(o1 instanceof String s); ) { + + } + + s.length(); + } + + { + L7: do { + + } while (!(o1 instanceof String s)); + + s.length(); + } + + if (o1 instanceof String s) { + Runnable r1 = new Runnable() { + @Override + public void run() { + s.length(); + } + }; + r1.run(); + Runnable r2 = () -> { + s.length(); + }; + r2.run(); + String s2 = s; + } + + System.out.println("BindingsTest1 complete"); + } +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/BindingsTest1Merging.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/BindingsTest1Merging.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,72 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8231827 + * @summary Basic tests for bindings from instanceof - tests for merging pattern variables + * @compile/fail/ref=BindingsTest1Merging.out -XDrawDiagnostics --enable-preview -source ${jdk.version} BindingsTest1Merging.java + */ + +public class BindingsTest1Merging { + public static boolean Ktrue() { return true; } + public static void main(String[] args) { + Object o1 = "hello"; + Integer i = 42; + Object o2 = i; + Object o3 = "there"; + + // Test for e1 && e2.F = intersect(e1.F, e2.F) + if (!(o1 instanceof String s) && !(o1 instanceof String s)) { + + } else { + s.length(); + } + + // Test for (e1 || e2).T = intersect(e1.T, e2.T) + if (o1 instanceof String s || o3 instanceof String s){ + System.out.println(s); // ? + } + + // Test for (e1 ? e2 : e3).T contains intersect(e2.T, e3.T) + if (Ktrue() ? o2 instanceof Integer x : o2 instanceof Integer x) { + x.intValue(); + } + + // Test for (e1 ? e2 : e3).T contains intersect(e1.T, e3.T) + if (o1 instanceof String s ? true : o1 instanceof String s) { + s.length(); + } + + // Test for (e1 ? e2 : e3).T contains intersect(e1.F, e2.T) + if (!(o1 instanceof String s) ? (o1 instanceof String s) : true) { + s.length(); + } + + // Test for (e1 ? e2 : e3).F contains intersect(e2.F, e3.F) + if (Ktrue() ? !(o2 instanceof Integer x) : !(o2 instanceof Integer x)){ + } else { + x.intValue(); + } + + // Test for (e1 ? e2 : e3).F contains intersect(e1.T, e3.F) + if (o1 instanceof String s ? true : !(o1 instanceof String s)){ + } else { + s.length(); + } + + // Test for (e1 ? e2 : e3).F contains intersect(e1.F, e2.F) + if (!(o1 instanceof String s) ? !(o1 instanceof String s) : true){ + } else { + s.length(); + } + + L3: { + if ((o1 instanceof String s) || (o3 instanceof String s)) { + s.length(); + } else { + break L3; + } + s.length(); + } + + System.out.println("BindingsTest1Merging complete"); + } +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/BindingsTest1Merging.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/BindingsTest1Merging.out Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,12 @@ +BindingsTest1Merging.java:17:39: compiler.err.match.binding.exists +BindingsTest1Merging.java:24:36: compiler.err.match.binding.exists +BindingsTest1Merging.java:29:21: compiler.err.match.binding.exists +BindingsTest1Merging.java:34:36: compiler.err.match.binding.exists +BindingsTest1Merging.java:39:39: compiler.err.match.binding.exists +BindingsTest1Merging.java:44:21: compiler.err.match.binding.exists +BindingsTest1Merging.java:50:36: compiler.err.match.binding.exists +BindingsTest1Merging.java:56:39: compiler.err.match.binding.exists +BindingsTest1Merging.java:62:42: compiler.err.match.binding.exists +- compiler.note.preview.filename: BindingsTest1Merging.java +- compiler.note.preview.recompile +9 errors diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/BindingsTest2.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/BindingsTest2.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,191 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8231827 + * @summary Ensure that scopes arising from conditionalExpressions are handled corrected. + * @compile/fail/ref=BindingsTest2.out -XDrawDiagnostics -XDshould-stop.at=FLOW --enable-preview -source ${jdk.version} BindingsTest2.java + */ +public class BindingsTest2 { + public static boolean Ktrue() { return true; } + public static void main(String[] args) { + Object o1 = "hello"; + Integer in = 42; + Object o2 = in; + Object o3 = "there"; + + + if (Ktrue() ? o2 instanceof Integer x : o2 instanceof String x) { + x.intValue(); + } + if (Ktrue() ? o2 instanceof Integer x : true) { + x.intValue(); + } + + if (o1 instanceof String s ? true : true) { + s.length(); + } + if (o1 instanceof String s ? true : o2 instanceof Integer s) { + s.length(); + } + if (o1 instanceof String s ? true : o2 instanceof Integer i) { + s.length(); + } + + // Test for (e1 ? e2 : e3).T contains intersect(e1.F, e2.T) + if (!(o1 instanceof String s) ? true : true) { + s.length(); + } + if (!(o1 instanceof String s) ? (o2 instanceof Integer s) : true) { + s.length(); + } + if (!(o1 instanceof String s) ? (o2 instanceof Integer i) : true) { + s.length(); + i.intValue(); + } + if (!(o1 instanceof String s) ? (o1 instanceof String s2) : true) { + s.length(); + s2.length(); + } + + + // Test for (e1 ? e2 : e3).F contains intersect(e2.F, e3.F) + if (Ktrue() ? !(o2 instanceof Integer x) : !(o1 instanceof String x)){ + } else { + x.intValue(); + } + if (Ktrue() ? !(o2 instanceof Integer x) : !(o1 instanceof String s)){ + } else { + x.intValue(); + } + if (Ktrue() ? !(o2 instanceof Integer x) : !(o2 instanceof Integer x1)){ + } else { + x.intValue(); + x1.intValue(); + } + if (Ktrue() ? !(o2 instanceof Integer x) : false){ + } else { + x.intValue(); + } + + // Test for (e1 ? e2 : e3).F contains intersect(e1.T, e3.F) + if (o1 instanceof String s ? true : !(o2 instanceof Integer s)){ + } else { + s.length(); + } + if (o1 instanceof String s ? true : !(o2 instanceof Integer i)){ + } else { + s.length(); + i.intValue(); + } + if (o1 instanceof String s ? true : !(o2 instanceof String s1)){ + } else { + s.length(); + s1.length(); + } + // Test for (e1 ? e2 : e3).F contains intersect(e1.F, e2.F) + if (!(o1 instanceof String s) ? !(o1 instanceof String s1) : true){ + } else { + s.length(); + s1.length(); + } + if (!(o1 instanceof String s) ? !(o2 instanceof Integer s) : true){ + } else { + s.length(); + } + if (!(o1 instanceof String s) ? !(o2 instanceof Integer i) : true){ + } else { + s.length(); + i.intValue(); + } + + // Test for e1 ? e2: e3 - include e1.T in e2 + if (o1 instanceof String s ? false : s.length()>0) { + System.out.println("done"); + } + if (o1 instanceof String s ? false : s.intValue!=0) { + System.out.println("done"); + } + + // Test for e1 ? e2 : e3 - include e1.F in e3 + if (!(o1 instanceof String s) ? s.length()>0 : false){ + System.out.println("done"); + } + if (!(o1 instanceof String s) ? s.intValue>0 : false){ + System.out.println("done"); + } + + { + while (!(o1 instanceof String s)) { + break; + } + + s.length(); + } + + { + while (!(o1 instanceof String s)) { + if (false) break; + } + + s.length(); + } + + { + while (!(o1 instanceof String s)) { + while (true); + break; + } + + s.length(); + } + + { + for (; !(o1 instanceof String s); ) { + break; + } + + s.length(); + } + + { + for (; !(o1 instanceof String s); ) { + if (false) break; + } + + s.length(); + } + + { + for (; !(o1 instanceof String s); ) { + while (true); + break; + } + + s.length(); + } + + { + do { + break; + } while (!(o1 instanceof String s)); + + s.length(); + } + + { + do { + if (false) break; + } while (!(o1 instanceof String s)); + + s.length(); + } + + { + do { + while (true); + break; + } while (!(o1 instanceof String s)); + + s.length(); + } + } +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/BindingsTest2.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/BindingsTest2.out Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,48 @@ +BindingsTest2.java:16:21: compiler.err.match.binding.exists +BindingsTest2.java:17:14: compiler.err.cant.resolve.location.args: kindname.method, intValue, , , (compiler.misc.location.1: kindname.variable, x, java.lang.String) +BindingsTest2.java:20:13: compiler.err.cant.resolve.location: kindname.variable, x, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:24:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:26:36: compiler.err.match.binding.exists +BindingsTest2.java:27:14: compiler.err.cant.resolve.location.args: kindname.method, length, , , (compiler.misc.location.1: kindname.variable, s, java.lang.Integer) +BindingsTest2.java:30:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:35:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:37:39: compiler.err.match.binding.exists +BindingsTest2.java:38:14: compiler.err.cant.resolve.location.args: kindname.method, length, , , (compiler.misc.location.1: kindname.variable, s, java.lang.Integer) +BindingsTest2.java:41:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:42:13: compiler.err.cant.resolve.location: kindname.variable, i, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:45:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:46:13: compiler.err.cant.resolve.location: kindname.variable, s2, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:51:21: compiler.err.match.binding.exists +BindingsTest2.java:53:14: compiler.err.cant.resolve.location.args: kindname.method, intValue, , , (compiler.misc.location.1: kindname.variable, x, java.lang.String) +BindingsTest2.java:57:13: compiler.err.cant.resolve.location: kindname.variable, x, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:61:13: compiler.err.cant.resolve.location: kindname.variable, x, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:62:13: compiler.err.cant.resolve.location: kindname.variable, x1, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:66:13: compiler.err.cant.resolve.location: kindname.variable, x, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:70:36: compiler.err.match.binding.exists +BindingsTest2.java:72:14: compiler.err.cant.resolve.location.args: kindname.method, length, , , (compiler.misc.location.1: kindname.variable, s, java.lang.Integer) +BindingsTest2.java:76:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:77:13: compiler.err.cant.resolve.location: kindname.variable, i, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:81:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:82:13: compiler.err.cant.resolve.location: kindname.variable, s1, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:87:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:88:13: compiler.err.cant.resolve.location: kindname.variable, s1, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:90:39: compiler.err.match.binding.exists +BindingsTest2.java:92:14: compiler.err.cant.resolve.location.args: kindname.method, length, , , (compiler.misc.location.1: kindname.variable, s, java.lang.Integer) +BindingsTest2.java:96:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:97:13: compiler.err.cant.resolve.location: kindname.variable, i, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:101:46: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:104:46: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:109:41: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:112:41: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:121:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:129:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:146:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:154:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:171:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:179:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null) +BindingsTest2.java:135:17: compiler.err.unreachable.stmt +BindingsTest2.java:160:17: compiler.err.unreachable.stmt +BindingsTest2.java:185:17: compiler.err.unreachable.stmt +- compiler.note.preview.filename: BindingsTest2.java +- compiler.note.preview.recompile +45 errors \ No newline at end of file diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/CastConversionMatch.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/CastConversionMatch.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,18 @@ +/* + * @test /nodynamicopyright/ + * @bug 8231827 + * @summary Match which involves a cast conversion + * @compile/fail/ref=CastConversionMatch.out -XDrawDiagnostics --enable-preview -source ${jdk.version} CastConversionMatch.java + */ + +public class CastConversionMatch { + public static void main(String [] args) { + Object o = 42; + if (o instanceof int s) { + System.out.println("Okay"); + } else { + throw new AssertionError("broken"); + } + System.out.println(">Test complete"); + } +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/CastConversionMatch.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/CastConversionMatch.out Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,4 @@ +CastConversionMatch.java:11:26: compiler.err.type.found.req: int, (compiler.misc.type.req.class.array) +- compiler.note.preview.filename: CastConversionMatch.java +- compiler.note.preview.recompile +1 error diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/DuplicateBindingTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/DuplicateBindingTest.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,22 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8231827 + * @summary Basic pattern bindings scope test + * @compile/fail/ref=DuplicateBindingTest.out -XDrawDiagnostics --enable-preview -source ${jdk.version} DuplicateBindingTest.java + */ + +public class DuplicateBindingTest { + + int f; + + public static void main(String[] args) { + + if (args != null) { + int s; + if (args[0] instanceof String s) { // NOT OK. Redef same scope. + } + if (args[0] instanceof String f) { // OK to redef field. + } + } + } +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/DuplicateBindingTest.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/DuplicateBindingTest.out Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,4 @@ +DuplicateBindingTest.java:16:43: compiler.err.already.defined: kindname.variable, s, kindname.method, main(java.lang.String[]) +- compiler.note.preview.filename: DuplicateBindingTest.java +- compiler.note.preview.recompile +1 error diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/EnsureTypesOrderTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/EnsureTypesOrderTest.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,13 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8187420 8231827 + * @summary Error message mentions relevant types transposed + * @compile/fail/ref=EnsureTypesOrderTest.out -XDrawDiagnostics --enable-preview -source ${jdk.version} EnsureTypesOrderTest.java + */ +public class EnsureTypesOrderTest { + public static void main(String [] args) { + if (args instanceof String s) { + System.out.println("Broken"); + } + } +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/EnsureTypesOrderTest.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/EnsureTypesOrderTest.out Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,4 @@ +EnsureTypesOrderTest.java:9:13: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String[], java.lang.String) +- compiler.note.preview.filename: EnsureTypesOrderTest.java +- compiler.note.preview.recompile +1 error \ No newline at end of file diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/ExamplesFromProposal.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/ExamplesFromProposal.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8231827 + * @summary All example code from "Pattern Matching for Java" document, released April 2017, adjusted to current state (no switches, etc) + * @compile --enable-preview -source ${jdk.version} ExamplesFromProposal.java + * @run main/othervm --enable-preview ExamplesFromProposal + */ + +interface Node { +} + +class IntNode implements Node { + int value; + + IntNode(int value) { + this.value = value; + } +} + +class NegNode implements Node { + Node node; + + NegNode(Node node) { + this.node = node; + } +} + +class MulNode implements Node { + Node left, right; + + MulNode(Node left, Node right) { + this.left = left; + this.right = right; + } +} + +class AddNode implements Node { + Node left, right; + + AddNode(Node left, Node right) { + this.left = left; + this.right = right; + } +} + +public class ExamplesFromProposal { + + public static Object getSomething() { + return new Long(42); + } + + public static int eval(Node n) { + if (n instanceof IntNode in) return in.value; + else if (n instanceof NegNode nn) return -eval(nn.node); + else if (n instanceof AddNode an) return eval(an.left) + eval(an.right); + else if (n instanceof MulNode mn) return eval(mn.left) * eval(mn.right); + else { + // should never happen + throw new AssertionError("broken"); + } + } + + public static String toString(Node n) { + if (n instanceof IntNode in) return String.valueOf(in.value); + else if (n instanceof NegNode nn) return "-"+eval(nn.node); + else if (n instanceof AddNode an) return eval(an.left) + " + " + eval(an.right); + else if (n instanceof MulNode mn) return eval(mn.left) + " * " + eval(mn.right); + else { + // should never happen + throw new AssertionError("broken"); + } + } + + public static Node simplify(Node n) { + if (n instanceof IntNode in) { + return n; + } else if (n instanceof NegNode nn) { + return new NegNode(simplify(nn.node)); + } else if (n instanceof AddNode ad) { + n = simplify(ad.left); + if (n instanceof IntNode intn) { + if (intn.value == 0) + return simplify(ad.right); + else + return new AddNode(intn, simplify(ad.right)); + } else { + return new AddNode(simplify(ad.left), simplify(ad.right)); + } + } else if (n instanceof MulNode mn) { + return new MulNode(simplify(mn.left), simplify(mn.right)); + } else { + //should never happen + throw new AssertionError("broken"); + } + } + + public static void testNode(Node n, int expected) { + if (eval(n) != expected) + throw new AssertionError("broken"); + } + + public static void main(String[] args) { + Object x = new Integer(42); + + if (x instanceof Integer i) { + // can use i here + System.out.println(i.intValue()); + } + + Object obj = getSomething(); + + String formatted = "unknown"; + if (obj instanceof Integer i) { + formatted = String.format("int %d", i); + } + else if (obj instanceof Byte b) { + formatted = String.format("byte %d", b); + } + else if (obj instanceof Long l) { + formatted = String.format("long %d", l); + } + else if (obj instanceof Double d) { + formatted = String.format("double %f", d); + } + else if (obj instanceof String s) { + formatted = String.format("String %s", s); + } + System.out.println(formatted); + + if (obj instanceof Integer i) formatted = String.format("int %d", i); + else if (obj instanceof Byte b) formatted = String.format("byte %d", b); + else if (obj instanceof Long l) formatted = String.format("long %d", l); + else if (obj instanceof Double d) formatted = String.format("double %f", d); + else if (obj instanceof String s) formatted = String.format("String %s", s); + else formatted = String.format("Something else "+ obj.toString()); + System.out.println(formatted); + + Node zero = new IntNode(0); + Node one = new IntNode(1); + Node ft = new IntNode(42); + + Node temp = new AddNode(zero,ft); + + testNode(temp,42); + + + + if (toString(simplify(temp)).equals(toString(ft))) + System.out.println("Simplify worked!"); + else + throw new AssertionError("broken"); + + + if (toString(simplify(new AddNode(zero,temp))).equals(toString(ft))) + System.out.println("Simplify worked!"); + else + throw new AssertionError("broken"); + + + temp = new AddNode(zero,ft); + temp = new AddNode(one,temp); + temp = new AddNode(zero,temp); + + Node fortythree = new AddNode(one,ft); + + if (toString(simplify(temp)).equals(toString(fortythree))) + System.out.println("Simplify worked!"); + else + throw new AssertionError("broken"); + + + x = "Hello"; + + if (x instanceof String s1) { + System.out.println(s1); + } + if (x instanceof String s1 && s1.length() > 0) { + System.out.println(s1); + } + if (x instanceof String s1) { + System.out.println(s1 + " is a string"); + } else { + System.out.println("not a string"); + } + + if (!(x instanceof String s1)) { + System.out.println("not a string"); + } else { + System.out.println(s1 + " is a string"); + } + } +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/ImpossibleTypeTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/ImpossibleTypeTest.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,21 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8231827 + * @summary Ensure that in type test patterns, the predicate is not trivially provable false. + * @compile/fail/ref=ImpossibleTypeTest.out -XDrawDiagnostics --enable-preview -source ${jdk.version} ImpossibleTypeTest.java + */ +public class ImpossibleTypeTest { + + public static void main(String[] args) { + + int in = 42; + Integer i = 42; + + if (i instanceof String s ) { + System.out.println("Broken"); + } + if (i instanceof Undefined u ) { + System.out.println("Broken"); + } + } +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/ImpossibleTypeTest.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/ImpossibleTypeTest.out Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,5 @@ +ImpossibleTypeTest.java:14:13: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Integer, java.lang.String) +ImpossibleTypeTest.java:17:26: compiler.err.cant.resolve.location: kindname.class, Undefined, , , (compiler.misc.location: kindname.class, ImpossibleTypeTest, null) +- compiler.note.preview.filename: ImpossibleTypeTest.java +- compiler.note.preview.recompile +2 errors diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/LocalVariableTable.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/LocalVariableTable.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8231827 + * @summary Ensure the LV table entries are generated for bindings + * @modules jdk.jdeps/com.sun.tools.classfile + * @compile -g --enable-preview -source ${jdk.version} LocalVariableTable.java + * @run main/othervm --enable-preview LocalVariableTable + */ + +import java.io.*; +import java.lang.annotation.*; +import java.util.*; +import com.sun.tools.classfile.*; + +/* + * The test checks that a LocalVariableTable attribute is generated for the + * method bodies containing patterns, and checks that the expected + * set of entries is found in the attribute. + * + * The test looks for test cases represented by nested classes whose + * name begins with "Pattern". Each such class contains a method + * with patterns, and because the test is compiled + * with -g, these methods should have a LocalVariableTable. The set of + * expected names in the LVT is provided in an annotation on the class for + * the test case. + */ +//Copied from: test/langtools/tools/javac/lambda/LocalVariableTable.java +public class LocalVariableTable { + public static void main(String... args) throws Exception { + new LocalVariableTable().run(); + } + + void run() throws Exception { + // the declared classes are returned in an unspecified order, + // so for neatness, sort them by name before processing them + Class[] classes = getClass().getDeclaredClasses(); + Arrays.sort(classes, (c1, c2) -> c1.getName().compareTo(c2.getName())); + + for (Class c : classes) { + if (c.getSimpleName().startsWith("Pattern")) + check(c); + } + if (errors > 0) + throw new Exception(errors + " errors found"); + } + + /** Check an individual test case. */ + void check(Class c) throws Exception { + System.err.println("Checking " + c.getSimpleName()); + + Expect expect = c.getAnnotation(Expect.class); + if (expect == null) { + error("@Expect not found for class " + c.getSimpleName()); + return; + } + + ClassFile cf = ClassFile.read(getClass().getResource(c.getName() + ".class").openStream()); + Method m = getMethodByName(cf, c.getSimpleName().contains("Lambda") ? "lambda$" : "test"); + if (m == null) { + error("test method not found"); + return; + } + + Code_attribute code = (Code_attribute) m.attributes.get(Attribute.Code); + if (code == null) { + error("Code attribute not found"); + return; + } + + LocalVariableTable_attribute lvt = + (LocalVariableTable_attribute) code.attributes.get(Attribute.LocalVariableTable); + if (lvt == null) { + error("LocalVariableTable attribute not found"); + return; + } + + Set foundNames = new LinkedHashSet<>(); + for (LocalVariableTable_attribute.Entry e: lvt.local_variable_table) { + foundNames.add(cf.constant_pool.getUTF8Value(e.name_index)); + } + + Set expectNames = new LinkedHashSet<>(Arrays.asList(expect.value())); + if (!foundNames.equals(expectNames)) { + Set foundOnly = new LinkedHashSet<>(foundNames); + foundOnly.removeAll(expectNames); + for (String s: foundOnly) + error("Unexpected name found: " + s); + Set expectOnly = new LinkedHashSet<>(expectNames); + expectOnly.removeAll(foundNames); + for (String s: expectOnly) + error("Expected name not found: " + s); + } + } + + Method getMethodByName(ClassFile cf, String name) throws ConstantPoolException { + for (Method m: cf.methods) { + if (m.getName(cf.constant_pool).startsWith(name)) + return m; + } + return null; + } + + /** Report an error. */ + void error(String msg) { + System.err.println("Error: " + msg); + errors++; + } + + int errors; + + /** + * Annotation used to provide the set of names expected in the LVT attribute. + */ + @Retention(RetentionPolicy.RUNTIME) + @interface Expect { + String[] value(); + } + + /* + * ---------- Test cases --------------------------------------------------- + */ + + @Expect({ "o", "s" }) + static class Pattern_Simple { + public static void test(Object o) { + if (o instanceof String s) {} + } + } + + @Expect({ "s" }) + static class Pattern_Lambda { + public static void test(Object o) { + if (o instanceof String s) { + Runnable r = () -> { + s.length(); + }; + } + } + } + +} + diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/MatchBindingScopeTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/MatchBindingScopeTest.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,68 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8231827 + * @summary Basic pattern bindings scope test + * @compile/fail/ref=MatchBindingScopeTest.out -XDrawDiagnostics --enable-preview -source ${jdk.version} MatchBindingScopeTest.java + */ +public class MatchBindingScopeTest { + + static Integer i = 42; + static String s = "Hello"; + static Object o1 = s; + static Object o2 = i; + + public static void main(String[] args) { + + if (o1 instanceof String j && j.length() == 5) { // OK + System.out.println(j); // OK + } else { + System.out.println(j); // NOT OK + } + + // NOT OK, name reused. + if (o1 instanceof String j && o2 instanceof Integer j) { + } + + if (o1 instanceof String j && j.length() == 5 && o2 instanceof Integer k && k == 42) { // OK + System.out.println(j); // OK + System.out.println(k); // OK + } else { + System.out.println(j); // NOT OK + System.out.println(k); // NOT OK + } + + if (o1 instanceof String j || j.length() == 5) { // NOT OK + System.out.println(j); // NOT OK + } + + if (o1 instanceof String j || o2 instanceof Integer j) { // NOT OK, types differ + System.out.println(j); + } else { + System.out.println(j); // NOT OK. + } + + while (o1 instanceof String j && j.length() == 5) { // OK + System.out.println(j); // OK + } + + while (o1 instanceof String j || true) { + System.out.println(j); // Not OK + } + + for (; o1 instanceof String j; j.length()) { // OK + System.out.println(j); // OK + } + + for (; o1 instanceof String j || true; j.length()) { // NOT OK + System.out.println(j); // Not OK + } + + int x = o1 instanceof String j ? + j.length() : // OK. + j.length(); // NOT OK. + + x = !(o1 instanceof String j) ? + j.length() : // NOT OK. + j.length(); // OK. + } +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/MatchBindingScopeTest.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/MatchBindingScopeTest.out Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,16 @@ +MatchBindingScopeTest.java:19:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) +MatchBindingScopeTest.java:23:36: compiler.err.match.binding.exists +MatchBindingScopeTest.java:30:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) +MatchBindingScopeTest.java:31:32: compiler.err.cant.resolve.location: kindname.variable, k, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) +MatchBindingScopeTest.java:34:39: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) +MatchBindingScopeTest.java:35:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) +MatchBindingScopeTest.java:38:36: compiler.err.match.binding.exists +MatchBindingScopeTest.java:41:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) +MatchBindingScopeTest.java:49:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) +MatchBindingScopeTest.java:56:48: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) +MatchBindingScopeTest.java:57:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) +MatchBindingScopeTest.java:62:23: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) +MatchBindingScopeTest.java:65:23: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null) +- compiler.note.preview.filename: MatchBindingScopeTest.java +- compiler.note.preview.recompile +13 errors diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/NullsInPatterns.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/NullsInPatterns.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8231827 + * @summary Testing pattern matching against the null constant + * @compile --enable-preview -source ${jdk.version} NullsInPatterns.java + * @run main/othervm --enable-preview NullsInPatterns + */ +import java.util.List; + +public class NullsInPatterns { + + public static void main(String[] args) { + if (null instanceof List t) { + throw new AssertionError("broken"); + } else { + System.out.println("null does not match List type pattern"); + } + //reifiable types not allowed in type test patterns in instanceof: +// if (null instanceof List l) { +// throw new AssertionError("broken"); +// } else { +// System.out.println("null does not match List type pattern"); +// } + if (null instanceof List l) { + throw new AssertionError("broken"); + } else { + System.out.println("null does not match List type pattern"); + } + } +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/PatternMatchPosTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/PatternMatchPosTest.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8231827 + * @summary Check proper positions. + * @build PatternMatchPosTest + * @compile/ref=PatternMatchPosTest.out -processor PatternMatchPosTest -Xlint:unchecked -XDrawDiagnostics --enable-preview -source ${jdk.version} PatternMatchPosTestData.java + */ + +import java.io.IOException; +import java.util.Set; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.TypeElement; + +import com.sun.source.tree.IfTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.SourcePositions; +import com.sun.source.util.TreePath; +import com.sun.source.util.TreeScanner; +import com.sun.source.util.Trees; +import javax.tools.Diagnostic; + +@SupportedAnnotationTypes("*") +public class PatternMatchPosTest extends AbstractProcessor { + + int round; + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (round++ != 0) + return false; + + try { + TypeElement data = processingEnv.getElementUtils().getTypeElement("PatternMatchPosTestData"); + Trees trees = Trees.instance(processingEnv); + SourcePositions sp = trees.getSourcePositions(); + TreePath dataPath = trees.getPath(data); + String text = dataPath.getCompilationUnit().getSourceFile().getCharContent(true).toString(); + + new TreeScanner() { + boolean print; + @Override + public Void visitIf(IfTree node, Void p) { + boolean prevPrint = print; + try { + print = true; + scan(node.getCondition(), p); + } finally { + print = prevPrint; + } + scan(node.getThenStatement(), p); + scan(node.getElseStatement(), p); + return null; + } + @Override + public Void scan(Tree tree, Void p) { + if (tree == null) + return null; + if (print) { + int start = (int) sp.getStartPosition(dataPath.getCompilationUnit(), tree); + int end = (int) sp.getEndPosition(dataPath.getCompilationUnit(), tree); + processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, + text.substring(start, end)); + } + return super.scan(tree, p); + } + }.scan(dataPath.getLeaf(), null); + return false; + } catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latestSupported(); + } + +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/PatternMatchPosTest.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/PatternMatchPosTest.out Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,14 @@ +- compiler.note.proc.messager: (o instanceof String s) +- compiler.note.proc.messager: o instanceof String s +- compiler.note.proc.messager: o +- compiler.note.proc.messager: String s +- compiler.note.proc.messager: String +- compiler.note.proc.messager: (o instanceof java.lang.String s) +- compiler.note.proc.messager: o instanceof java.lang.String s +- compiler.note.proc.messager: o +- compiler.note.proc.messager: java.lang.String s +- compiler.note.proc.messager: java.lang.String +- compiler.note.proc.messager: java.lang +- compiler.note.proc.messager: java +- compiler.note.preview.filename: PatternMatchPosTestData.java +- compiler.note.preview.recompile diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/PatternMatchPosTestData.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/PatternMatchPosTestData.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +class PatternMatchPosTestData { + void data(Object o) { + if (o instanceof String s) { } + if (o instanceof java.lang.String s) { } + } +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/PatternTypeTest2.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/PatternTypeTest2.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8231827 + * @summary Basic pattern test + * @compile --enable-preview -source ${jdk.version} PatternTypeTest2.java + * @run main/othervm --enable-preview PatternTypeTest2 + */ +public class PatternTypeTest2 { + + public static void main(String[] args) { + + Integer i = 42; + String s = "Hello"; + Object o = i; + + if (o instanceof Integer j) { + System.out.println("It's an Integer"); + } else { + throw new AssertionError("Broken"); + } + } +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/PatternVariablesAreFinal.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/PatternVariablesAreFinal.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,16 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8231827 + * @summary Ensure that in type test patterns, the predicate is not trivially provable false. + * @compile/fail/ref=PatternVariablesAreFinal.out -XDrawDiagnostics --enable-preview -source ${jdk.version} PatternVariablesAreFinal.java + */ +public class PatternVariablesAreFinal { + public static void main(String[] args) { + Object o = 32; + if (o instanceof String s) { + s = "hello again"; + System.out.println(s); + } + System.out.println("test complete"); + } +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/PatternVariablesAreFinal.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/PatternVariablesAreFinal.out Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,4 @@ +PatternVariablesAreFinal.java:11:13: compiler.err.pattern.binding.may.not.be.assigned: s +- compiler.note.preview.filename: PatternVariablesAreFinal.java +- compiler.note.preview.recompile +1 error diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/PatternVariablesAreFinal2.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/PatternVariablesAreFinal2.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8231827 + * @summary Pattern variables are final so should be allowed to be referenced in an inner class + * @compile --enable-preview -source ${jdk.version} PatternVariablesAreFinal2.java + * @run main/othervm --enable-preview PatternVariablesAreFinal2 + */ +public class PatternVariablesAreFinal2 { + public static void main(String[] args) { + Object o = "42"; + if (o instanceof String s) { + new Object() { + void run() { System.err.println(s); } + }.run(); + } + } +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/PatternsSimpleVisitorTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/PatternsSimpleVisitorTest.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8231827 + * @summary Ensure SimpleTreeVisitor.visitBindingPattern and visitInstanceOf behaves as it should + * @modules jdk.compiler + */ + +import java.io.IOException; +import java.io.StringWriter; +import java.net.URI; +import java.util.Arrays; +import java.util.List; + +import javax.tools.*; + +import com.sun.source.tree.BindingPatternTree; +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.InstanceOfTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.JavacTask; +import com.sun.source.util.SimpleTreeVisitor; +import com.sun.source.util.TreePathScanner; + +public class PatternsSimpleVisitorTest { + + public static void main(String[] args) throws Exception { + new PatternsSimpleVisitorTest().run(); + } + + void run() throws Exception { + String code = "class Test {\n" + + " boolean t(Object o) {\n" + + " return o instanceof String s ? s.isEmpty() : false;\n" + + " }\n" + + "}\n"; + int[] callCount = new int[1]; + int[] instanceOfNodeCount = new int[1]; + int[] bindingPatternNodeCount = new int[1]; + new TreePathScanner() { + @Override + public Void visitInstanceOf(InstanceOfTree node, Void p) { + node.accept(new SimpleTreeVisitor() { + @Override + protected Void defaultAction(Tree defaultActionNode, Void p) { + callCount[0]++; + if (node == defaultActionNode) { + instanceOfNodeCount[0]++; + } + return null; + } + }, null); + return super.visitInstanceOf(node, p); + } + @Override + public Void visitBindingPattern(BindingPatternTree node, Void p) { + node.accept(new SimpleTreeVisitor() { + @Override + protected Void defaultAction(Tree defaultActionNode, Void p) { + callCount[0]++; + if (node == defaultActionNode) { + bindingPatternNodeCount[0]++; + } + return null; + } + }, null); + return super.visitBindingPattern(node, p); + } + }.scan(parse(code), null); + + if (callCount[0] != 2 || instanceOfNodeCount[0] != 1 || + bindingPatternNodeCount[0] != 1) { + throw new AssertionError("Unexpected counts; callCount=" + callCount[0] + + ", switchExprNodeCount=" + instanceOfNodeCount[0] + + ", yieldNodeCount=" + bindingPatternNodeCount[0]); + } + } + + private CompilationUnitTree parse(String code) throws IOException { + final JavaCompiler tool = ToolProvider.getSystemJavaCompiler(); + assert tool != null; + DiagnosticListener noErrors = d -> {}; + + StringWriter out = new StringWriter(); + JavacTask ct = (JavacTask) tool.getTask(out, null, noErrors, + List.of("--enable-preview", "-source", Integer.toString(Runtime.version().feature())), null, + Arrays.asList(new MyFileObject(code))); + return ct.parse().iterator().next(); + } + + static class MyFileObject extends SimpleJavaFileObject { + private String text; + + public MyFileObject(String text) { + super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); + this.text = text; + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) { + return text; + } + } +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/Reifiable.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/Reifiable.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,22 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8231827 + * @summary Verify behavior w.r.t. non-reifiable types and type test patterns in instanceof + * @compile/fail/ref=Reifiable.out --enable-preview -source ${jdk.version} -XDrawDiagnostics Reifiable.java + */ + +public class Reifiable implements ReifiableI { + private static boolean test(Object o, List l1, List l2) { + return o instanceof ListImpl li1 && + l1 instanceof ListImpl li2 && + l2 instanceof ListImpl li3 && + l2 instanceof ListImpl li4 && + l1 instanceof Unrelated li5; + } + + public class List {} + public class ListImpl extends List {} + public class Unrelated {} +} + +interface ReifiableI {} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/Reifiable.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/Reifiable.out Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,7 @@ +Reifiable.java:10:16: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, Reifiable.ListImpl +Reifiable.java:12:16: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: Reifiable.List, Reifiable.ListImpl) +Reifiable.java:13:39: compiler.err.not.within.bounds: java.lang.String, T +Reifiable.java:14:16: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: Reifiable.List, Reifiable.Unrelated) +- compiler.note.preview.filename: Reifiable.java +- compiler.note.preview.recompile +4 errors diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/ReifiableOld-old.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/ReifiableOld-old.out Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,7 @@ +ReifiableOld.java:12:37: compiler.err.illegal.generic.type.for.instof +ReifiableOld.java:13:38: compiler.err.illegal.generic.type.for.instof +ReifiableOld.java:14:38: compiler.err.illegal.generic.type.for.instof +ReifiableOld.java:15:38: compiler.err.illegal.generic.type.for.instof +ReifiableOld.java:15:39: compiler.err.not.within.bounds: java.lang.String, T +ReifiableOld.java:16:39: compiler.err.illegal.generic.type.for.instof +6 errors diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/ReifiableOld.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/ReifiableOld.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,24 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8231827 + * @summary Verify behavior w.r.t. non-reifiable types in instanceof + * @compile/fail/ref=ReifiableOld-old.out -source 13 -Xlint:-options -XDrawDiagnostics ReifiableOld.java + * @compile/fail/ref=ReifiableOld-old.out -source ${jdk.version} -XDrawDiagnostics ReifiableOld.java + * @compile/fail/ref=ReifiableOld.out --enable-preview -source ${jdk.version} -XDrawDiagnostics ReifiableOld.java + */ + +public class ReifiableOld implements ReifiableOldI { + private static boolean test(Object o, List l1, List l2) { + return o instanceof ListImpl && + l1 instanceof ListImpl && + l2 instanceof ListImpl && + l2 instanceof ListImpl && + l1 instanceof Unrelated; + } + + public class List {} + public class ListImpl extends List {} + public class Unrelated {} +} + +interface ReifiableOldI {} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/ReifiableOld.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/ReifiableOld.out Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,7 @@ +ReifiableOld.java:12:16: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, ReifiableOld.ListImpl +ReifiableOld.java:14:16: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: ReifiableOld.List, ReifiableOld.ListImpl) +ReifiableOld.java:15:39: compiler.err.not.within.bounds: java.lang.String, T +ReifiableOld.java:16:16: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: ReifiableOld.List, ReifiableOld.Unrelated) +- compiler.note.preview.filename: ReifiableOld.java +- compiler.note.preview.recompile +4 errors diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/UncheckedWarningOnMatchesTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/UncheckedWarningOnMatchesTest.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,18 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8187429 8231827 + * @summary Missing unchecked conversion warning + * @compile/fail/ref=UncheckedWarningOnMatchesTest.out -Xlint:unchecked -Werror -XDrawDiagnostics --enable-preview -source ${jdk.version} UncheckedWarningOnMatchesTest.java + */ +import java.util.ArrayList; + +public class UncheckedWarningOnMatchesTest { + + public static void main(String [] args) { + + Object o = new ArrayList(); + if (o instanceof ArrayList ai) { // unchecked conversion + System.out.println("Blah"); + } + } +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/UncheckedWarningOnMatchesTest.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/UncheckedWarningOnMatchesTest.out Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,4 @@ +UncheckedWarningOnMatchesTest.java:14:13: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, java.util.ArrayList +- compiler.note.preview.filename: UncheckedWarningOnMatchesTest.java +- compiler.note.preview.recompile +1 error diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/scope/ScopeTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/scope/ScopeTest.java Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.Test; +import tools.javac.combo.JavacTemplateTestBase; + +import static java.util.stream.Collectors.toList; + +@Test +public class ScopeTest extends JavacTemplateTestBase { + + private static String st_block(String... statements) { + return Arrays.stream(statements).collect(Collectors.joining("", "{", "}")); + } + + private static String st_if(String condition, String then, String els) { + return "if (" + condition + ") " + then + " else " + els; + } + + private static String st_while(String condition, String body) { + return "while (" + condition + ") " + body; + } + + private static String st_do_while(String body, String condition) { + return "do " + body + " while (" + condition + ");"; + } + + private static String st_for(String init, String condition, String update, String body) { + return "for (" + init + "; " + condition + "; " + update + ") " + body; + } + + private static String st_s_use() { + return "s.length();"; + } + + private static String st_break() { + return "break;"; + } + + private static String st_return() { + return "return;"; + } + + private static String st_noop() { + return ";"; + } + + private static String expr_empty() { + return ""; + } + + private static String expr_o_match_str() { + return "o instanceof String s"; + } + + private static String expr_not(String expr) { + return "!(" + expr + ")"; + } + + @AfterMethod + public void dumpTemplateIfError(ITestResult result) { + // Make sure offending template ends up in log file on failure + if (!result.isSuccess()) { + System.err.printf("Diagnostics: %s%nTemplate: %s%n", diags.errorKeys(), sourceFiles.stream().map(p -> p.snd).collect(toList())); + } + } + + private void program(String block) { + String s = "class C { void m(Object o) " + block + "}"; + addSourceFile("C.java", new StringTemplate(s)); + } + + private void assertOK(String block) { + String sourceVersion = Integer.toString(Runtime.version().feature()); + + reset(); + addCompileOptions("--enable-preview", "-source", sourceVersion); + program(block); + try { + compile(); + } + catch (IOException e) { + throw new RuntimeException(e); + } + assertCompileSucceeded(); + } + + private void assertFail(String expectedDiag, String block) { + String sourceVersion = Integer.toString(Runtime.version().feature()); + + reset(); + addCompileOptions("--enable-preview", "-source", sourceVersion); + program(block); + try { + compile(); + } + catch (IOException e) { + throw new RuntimeException(e); + } + assertCompileFailed(expectedDiag); + } + + public void testIf() { + assertOK(st_block(st_if(expr_o_match_str(), st_s_use(), st_return()), st_s_use())); + assertOK(st_block(st_if(expr_not(expr_o_match_str()), st_return(), st_s_use()), st_s_use())); + assertFail("compiler.err.cant.resolve.location", st_block(st_if(expr_o_match_str(), st_s_use(), st_noop()), st_s_use())); + assertFail("compiler.err.cant.resolve.location", st_block(st_if(expr_not(expr_o_match_str()), st_noop(), st_s_use()), st_s_use())); + } + + public void testWhile() { + assertOK(st_block(st_while(expr_not(expr_o_match_str()), st_noop()), st_s_use())); + assertFail("compiler.err.cant.resolve.location", st_block(st_while(expr_not(expr_o_match_str()), st_break()), st_s_use())); + } + + public void testDoWhile() { + assertOK(st_block(st_do_while(st_noop(), expr_not(expr_o_match_str())), st_s_use())); + assertFail("compiler.err.cant.resolve.location", st_block(st_do_while(st_break(), expr_not(expr_o_match_str())), st_s_use())); + } + + public void testFor() { + assertOK(st_block(st_for(expr_empty(), expr_not(expr_o_match_str()), expr_empty(), st_noop()), st_s_use())); + assertFail("compiler.err.cant.resolve.location", st_block(st_for(expr_empty(), expr_not(expr_o_match_str()), expr_empty(), st_break()), st_s_use())); + } + +} diff -r 3b1afeb49a20 -r c887851da33d test/langtools/tools/javac/patterns/scope/TEST.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/patterns/scope/TEST.properties Wed Nov 27 10:21:22 2019 +0100 @@ -0,0 +1,6 @@ +TestNG.dirs = . + +lib.dirs = /lib/combo + +modules = \ + jdk.compiler/com.sun.tools.javac.util