aoqi@0: /* aoqi@0: * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: /* aoqi@0: * @test aoqi@0: * @bug 7118412 aoqi@0: * @summary Shadowing of type-variables vs. member types aoqi@0: */ aoqi@0: import java.io.File; aoqi@0: import java.io.FileWriter; aoqi@0: import java.io.IOException; aoqi@0: import java.io.PrintWriter; aoqi@0: import java.io.StringWriter; aoqi@0: aoqi@0: public class ShadowingTest { aoqi@0: aoqi@0: // We generate a method "test" that tries to call T.. This controls whether aoqi@0: // "test" is static or not. aoqi@0: private enum MethodContext { aoqi@0: STATIC("static "), aoqi@0: INSTANCE(""); aoqi@0: aoqi@0: public final String methodcontext; aoqi@0: aoqi@0: MethodContext(final String methodcontext) { aoqi@0: this.methodcontext = methodcontext; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: // These control whether or not a type parameter, method type aoqi@0: // parameter, or inner class get declared (and in the case of aoqi@0: // inner classes, whether it's static or not. aoqi@0: aoqi@0: private enum MethodTypeParameterDecl { aoqi@0: NO(""), aoqi@0: YES(" "); aoqi@0: aoqi@0: public final String tyvar; aoqi@0: aoqi@0: MethodTypeParameterDecl(final String tyvar) { aoqi@0: this.tyvar = tyvar; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private enum InsideDef { aoqi@0: NONE(""), aoqi@0: STATIC("static class T { public void inner() {} }\n"), aoqi@0: INSTANCE("class T { public void inner() {} }\n"); aoqi@0: aoqi@0: public final String instancedef; aoqi@0: aoqi@0: InsideDef(final String instancedef) { aoqi@0: this.instancedef = instancedef; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private enum TypeParameterDecl { aoqi@0: NO(""), aoqi@0: YES(""); aoqi@0: aoqi@0: public final String tyvar; aoqi@0: aoqi@0: TypeParameterDecl(final String tyvar) { aoqi@0: this.tyvar = tyvar; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: // Represents what method we try to call. This is a way of aoqi@0: // checking which T we're seeing. aoqi@0: private enum MethodCall { aoqi@0: // Method type variables extend Number, so we have intValue aoqi@0: METHOD_TYPEVAR("intValue"), aoqi@0: // The inner class declaration has a method called "inner" aoqi@0: INNER_CLASS("inner"), aoqi@0: // The class type variables extend Collection, so we call iterator aoqi@0: TYPEVAR("iterator"), aoqi@0: // The outer class declaration has a method called "outer" aoqi@0: OUTER_CLASS("outer"); aoqi@0: aoqi@0: public final String methodcall; aoqi@0: aoqi@0: MethodCall(final String methodcall) { aoqi@0: this.methodcall = methodcall; aoqi@0: } aoqi@0: aoqi@0: } aoqi@0: aoqi@0: public boolean succeeds(final MethodCall call, aoqi@0: final MethodTypeParameterDecl mtyvar, aoqi@0: final MethodContext ctx, aoqi@0: final InsideDef inside, aoqi@0: final TypeParameterDecl tyvar) { aoqi@0: switch(call) { aoqi@0: // We want to resolve to the method type variable aoqi@0: case METHOD_TYPEVAR: switch(mtyvar) { aoqi@0: // If the method type variable exists, then T will aoqi@0: // resolve to it, and we'll have intValue. aoqi@0: case YES: return true; aoqi@0: // Otherwise, this cannot succeed. aoqi@0: default: return false; aoqi@0: } aoqi@0: // We want to resolve to the inner class aoqi@0: case INNER_CLASS: switch(mtyvar) { aoqi@0: // The method type parameter will shadow the inner aoqi@0: // class, so there can't be one. aoqi@0: case NO: switch(ctx) { aoqi@0: // If we're not static, then either one should succeed. aoqi@0: case INSTANCE: switch(inside) { aoqi@0: case INSTANCE: aoqi@0: case STATIC: aoqi@0: return true; aoqi@0: default: return false; aoqi@0: } aoqi@0: case STATIC: switch(inside) { aoqi@0: // If we are static, and the inner class is aoqi@0: // static, then we also succeed, because we aoqi@0: // can't see the type variable. aoqi@0: case STATIC: return true; aoqi@0: case INSTANCE: switch(tyvar) { aoqi@0: // If we're calling from a non-static aoqi@0: // context, there can't be a class type aoqi@0: // variable, because that will shadow the aoqi@0: // static inner class definition. aoqi@0: case NO: return true; aoqi@0: default: return false; aoqi@0: } aoqi@0: // If the inner class isn't declared, we can't aoqi@0: // see it. aoqi@0: default: return false; aoqi@0: } aoqi@0: // Can't get here. aoqi@0: default: return false; aoqi@0: } aoqi@0: default: return false; aoqi@0: } aoqi@0: // We want to resolve to the class type parameter aoqi@0: case TYPEVAR: switch(mtyvar) { aoqi@0: // We can't have a method type parameter, as that would aoqi@0: // shadow the class type parameter aoqi@0: case NO: switch(ctx) { aoqi@0: case INSTANCE: switch(inside) { aoqi@0: // We have to be in an instance context. If aoqi@0: // we're static, we can't see the type aoqi@0: // variable. aoqi@0: case NONE: switch(tyvar) { aoqi@0: // Obviously, the type parameter has to be declared. aoqi@0: case YES: return true; aoqi@0: default: return false; aoqi@0: } aoqi@0: default: return false; aoqi@0: } aoqi@0: default: return false; aoqi@0: } aoqi@0: default: return false; aoqi@0: } aoqi@0: // We want to resolve to the outer class aoqi@0: case OUTER_CLASS: switch(mtyvar) { aoqi@0: case NO: switch(inside) { aoqi@0: case NONE: switch(tyvar) { aoqi@0: // Basically, nothing else can be declared, or aoqi@0: // else we can't see it. Even if our context aoqi@0: // is static, the compiler will complain if aoqi@0: // non-static T's exist, because they will aoqi@0: // shadow the outer class. aoqi@0: case NO: return true; aoqi@0: default: return false; aoqi@0: } aoqi@0: default: return false; aoqi@0: } aoqi@0: default: return false; aoqi@0: } aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: private static final File classesdir = new File("7118412"); aoqi@0: aoqi@0: private int errors = 0; aoqi@0: aoqi@0: private int dirnum = 0; aoqi@0: aoqi@0: private void doTest(final MethodTypeParameterDecl mtyvar, aoqi@0: final TypeParameterDecl tyvar, aoqi@0: final InsideDef insidedef, final MethodContext ctx, aoqi@0: final MethodCall call) aoqi@0: throws IOException { aoqi@0: final String content = "import java.util.Collection;\n" + aoqi@0: "class Test" + tyvar.tyvar + " {\n" + aoqi@0: " " + insidedef.instancedef + aoqi@0: " " + ctx.methodcontext + mtyvar.tyvar + "void test(T t) { t." + aoqi@0: call.methodcall + "(); }\n" + aoqi@0: "}\n" + aoqi@0: "class T { void outer() {} }\n"; aoqi@0: final File dir = new File(classesdir, "" + dirnum); aoqi@0: final File Test_java = writeFile(dir, "Test.java", content); aoqi@0: dirnum++; aoqi@0: if(succeeds(call, mtyvar, ctx, insidedef, tyvar)) { aoqi@0: if(!assert_compile_succeed(Test_java)) aoqi@0: System.err.println("Failed file:\n" + content); aoqi@0: } aoqi@0: else { aoqi@0: if(!assert_compile_fail(Test_java)) aoqi@0: System.err.println("Failed file:\n" + content); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private void run() throws Exception { aoqi@0: classesdir.mkdir(); aoqi@0: for(MethodTypeParameterDecl mtyvar : MethodTypeParameterDecl.values()) aoqi@0: for(TypeParameterDecl tyvar : TypeParameterDecl.values()) aoqi@0: for(InsideDef insidedef : InsideDef.values()) aoqi@0: for(MethodContext ctx : MethodContext.values()) aoqi@0: for(MethodCall methodcall : MethodCall.values()) aoqi@0: doTest(mtyvar, tyvar, insidedef, ctx, methodcall); aoqi@0: if (errors != 0) aoqi@0: throw new Exception("ShadowingTest test failed with " + aoqi@0: errors + " errors."); aoqi@0: } aoqi@0: aoqi@0: private boolean assert_compile_fail(final File file) { aoqi@0: final String filename = file.getPath(); aoqi@0: final String[] args = { filename }; aoqi@0: final StringWriter sw = new StringWriter(); aoqi@0: final PrintWriter pw = new PrintWriter(sw); aoqi@0: final int rc = com.sun.tools.javac.Main.compile(args, pw); aoqi@0: pw.close(); aoqi@0: if (rc == 0) { aoqi@0: System.err.println("Compilation of " + file.getName() + aoqi@0: " didn't fail as expected."); aoqi@0: errors++; aoqi@0: return false; aoqi@0: } else return true; aoqi@0: } aoqi@0: aoqi@0: private boolean assert_compile_succeed(final File file) { aoqi@0: final String filename = file.getPath(); aoqi@0: final String[] args = { filename }; aoqi@0: final StringWriter sw = new StringWriter(); aoqi@0: final PrintWriter pw = new PrintWriter(sw); aoqi@0: final int rc = com.sun.tools.javac.Main.compile(args, pw); aoqi@0: pw.close(); aoqi@0: if (rc != 0) { aoqi@0: System.err.println("Compilation of " + file.getName() + aoqi@0: " didn't succeed as expected. Output:"); aoqi@0: System.err.println(sw.toString()); aoqi@0: errors++; aoqi@0: return false; aoqi@0: } else return true; aoqi@0: } aoqi@0: aoqi@0: private File writeFile(final File dir, aoqi@0: final String path, aoqi@0: final String body) throws IOException { aoqi@0: final File f = new File(dir, path); aoqi@0: f.getParentFile().mkdirs(); aoqi@0: final FileWriter out = new FileWriter(f); aoqi@0: out.write(body); aoqi@0: out.close(); aoqi@0: return f; aoqi@0: } aoqi@0: aoqi@0: public static void main(String... args) throws Exception { aoqi@0: new ShadowingTest().run(); aoqi@0: } aoqi@0: aoqi@0: }