001 /* Copyright (c) 2005-2008, Torbjorn Ekman 002 * 2011-2015, Jesper Öqvist <jesper.oqvist@cs.lth.se> 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without 006 * modification, are permitted provided that the following conditions are met: 007 * 008 * 1. Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * 2. Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * 3. Neither the name of the copyright holder nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 021 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 022 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 023 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 024 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 025 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 026 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 027 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 028 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 029 * POSSIBILITY OF SUCH DAMAGE. 030 */ 031 032 /** 033 * Type inference for generic instance creation. 034 * 035 * According to the JLSv7 $15.9.1, type inference for generic 036 * instance creation uses the type inference for generic methods. 037 * 038 * Empty type argument lists (<>) are parsed as DiamondAccess 039 * nodes. The type of the DiamondAccess is computed by creating placeholder 040 * methods and using generic method type inference from Java1.5Frontend 041 * to find the inferred type arguments for the DiamondAccess. 042 */ 043 aspect Diamond { 044 045 public void StandInMethodDecl.nameCheck() { } 046 public void StandInMethodDecl.typeCheck() { } 047 public void StandInMethodDecl.exceptionHandling() { } 048 public void StandInMethodDecl.checkUnreachableStmt() { } 049 public void StandInMethodDecl.definiteAssignment() { } 050 public void StandInMethodDecl.checkModifiers() { } 051 052 /** 053 * If this DiamondAccess node constitutes a legal use of 054 * the diamond operator, the inferred generic type for the 055 * enclosing class instance expression is returned. 056 */ 057 eq DiamondAccess.type() { 058 TypeDecl accessType = getTypeAccess().type(); 059 060 if (isAnonymousDecl()) { 061 return accessType; 062 } 063 064 if (getClassInstanceExpr() == null) { 065 // It is an error if the DiamondAccess does not occurr 066 // within a class instance creation expression, but this 067 // error is handled in typeCheck. 068 return accessType; 069 } 070 071 if (!(accessType instanceof ParClassDecl)) { 072 // It is an error if the TypeDecl of a DiamondAccess is not 073 // a generic type, but this error is handled in typeCheck. 074 return accessType; 075 } 076 077 SimpleSet maxSpecific = chooseConstructor(); 078 079 if (maxSpecific.isEmpty()) { 080 return getTypeAccess().type(); 081 } 082 083 MethodDecl constructor = (MethodDecl) maxSpecific.iterator().next(); 084 return constructor.type(); 085 } 086 087 syn boolean Access.isDiamond() = false; 088 eq DiamondAccess.isDiamond() = true; 089 090 inh ClassInstanceExpr DiamondAccess.getClassInstanceExpr(); 091 eq ClassInstanceExpr.getAccess().getClassInstanceExpr() = this; 092 eq Program.getChild(int i).getClassInstanceExpr() = null; // TODO 093 094 protected static SimpleSet DiamondAccess.mostSpecific(SimpleSet maxSpecific, MethodDecl decl) { 095 if (maxSpecific.isEmpty()) { 096 maxSpecific = maxSpecific.add(decl); 097 } else { 098 MethodDecl other = (MethodDecl) maxSpecific.iterator().next(); 099 if (decl.moreSpecificThan(other)) { 100 maxSpecific = decl; 101 } else if (!other.moreSpecificThan(decl)) { 102 maxSpecific = maxSpecific.add(decl); 103 } 104 } 105 return maxSpecific; 106 } 107 108 /** 109 * Choose a constructor for the diamond operator using placeholder 110 * methods. 111 */ 112 protected SimpleSet DiamondAccess.chooseConstructor() { 113 ClassInstanceExpr instanceExpr = getClassInstanceExpr(); 114 TypeDecl type = getTypeAccess().type(); 115 116 assert instanceExpr != null; 117 assert type instanceof ParClassDecl; 118 119 GenericClassDecl genericType = (GenericClassDecl) ((ParClassDecl) type).genericDecl(); 120 121 List<StandInMethodDecl> placeholderMethods = genericType.getStandInMethodList(); 122 123 SimpleSet maxSpecific = SimpleSet.emptySet; 124 Collection<MethodDecl> potentiallyApplicable = potentiallyApplicable(placeholderMethods); 125 for (MethodDecl candidate : potentiallyApplicable) { 126 if (applicableBySubtyping(instanceExpr, candidate) 127 || applicableByMethodInvocationConversion(instanceExpr, candidate) 128 || applicableByVariableArity(instanceExpr, candidate)) { 129 maxSpecific = mostSpecific(maxSpecific, candidate); 130 } 131 } 132 return maxSpecific; 133 } 134 135 /** 136 * The placeholder method list for the constructors of this generic 137 * class. 138 * 139 * @return list of placeholder methods 140 */ 141 syn nta List<StandInMethodDecl> GenericClassDecl.getStandInMethodList() { 142 List<StandInMethodDecl> placeholderMethods = new List<StandInMethodDecl>(); 143 List<TypeVariable> typeParams = getTypeParameterList(); 144 List<TypeVariable> classTypeVars = new List<TypeVariable>(); 145 List<Access> typeArgs = new List<Access>(); 146 147 // Copy the list of type parameters. 148 int arg = 0; 149 for (Iterator iter = typeParams.iterator(); iter.hasNext(); ++arg) { 150 String substName = "#"+arg; 151 typeArgs.add(new TypeAccess(substName)); 152 153 TypeVariable typeVar = (TypeVariable) iter.next(); 154 List<Access> typeBounds = new List<Access>(); 155 for (Access typeBound : typeVar.getTypeBoundList()) { 156 typeBounds.add((Access) typeBound.treeCopyNoTransform()); 157 } 158 classTypeVars.add(new TypeVariable( 159 new Modifiers(), 160 substName, 161 new List<BodyDecl>(), 162 typeBounds)); 163 } 164 165 ParTypeAccess returnType = new ParTypeAccess( 166 createQualifiedAccess(), 167 typeArgs); 168 169 for (Iterator iter = constructors().iterator(); iter.hasNext(); ) { 170 ConstructorDecl decl = (ConstructorDecl) iter.next(); 171 if (decl instanceof ConstructorDeclSubstituted) { 172 decl = ((ConstructorDeclSubstituted) decl).getOriginal(); 173 } 174 175 // Filter accessible constructors. 176 if (!decl.accessibleFrom(hostType())) { 177 continue; 178 } 179 180 Collection<TypeVariable> originalTypeVars = new LinkedList<TypeVariable>(); 181 List<TypeVariable> typeVars = new List<TypeVariable>(); 182 for (TypeVariable typeVar : typeParams) { 183 originalTypeVars.add(typeVar); 184 } 185 for (TypeVariable typeVar : classTypeVars) { 186 typeVars.add((TypeVariable) typeVar.treeCopyNoTransform()); 187 } 188 189 if (decl.isGeneric()) { 190 GenericConstructorDecl genericDecl = decl.genericDecl(); 191 List<TypeVariable> typeVariables = new List<TypeVariable>(); 192 for (int i = 0; i < genericDecl.getNumTypeParameter(); ++i) { 193 String substName = "#" + (arg+i); 194 195 TypeVariable typeVar = genericDecl.getTypeParameter(i); 196 originalTypeVars.add(typeVar); 197 List<Access> typeBounds = new List<Access>(); 198 for (Access typeBound : typeVar.getTypeBoundList()) { 199 typeBounds.add((Access) typeBound.treeCopyNoTransform()); 200 } 201 typeVars.add(new TypeVariable( 202 new Modifiers(), 203 substName, 204 new List<BodyDecl>(), 205 typeBounds)); 206 } 207 } 208 209 List<ParameterDeclaration> substParameters = new List<ParameterDeclaration>(); 210 for (ParameterDeclaration param : decl.getParameterList()) { 211 substParameters.add(param.substituted(originalTypeVars, typeVars)); 212 } 213 214 List<Access> substExceptions = new List<Access>(); 215 for (Access exception : decl.getExceptionList()) { 216 substExceptions.add(exception.substituted(originalTypeVars, typeVars)); 217 } 218 219 StandInMethodDecl placeholderMethod = new StandInMethodDecl( 220 (Modifiers) decl.getModifiers().treeCopyNoTransform(), 221 (Access) returnType.treeCopyNoTransform(), 222 "#" + getID(), 223 substParameters, 224 substExceptions, 225 new Opt(new Block()), 226 typeVars); 227 228 placeholderMethods.add(placeholderMethod); 229 } 230 return placeholderMethods; 231 } 232 233 /** 234 * Select potentially applicable method declarations 235 * from a set of candidates. 236 * Type inference is applied to the (potentially) applicable candidates. 237 */ 238 protected Collection<MethodDecl> DiamondAccess.potentiallyApplicable( 239 List<StandInMethodDecl> candidates) { 240 Collection<MethodDecl> potentiallyApplicable = new LinkedList<MethodDecl>(); 241 for (GenericMethodDecl candidate : candidates) { 242 if (potentiallyApplicable(candidate)) { 243 MethodDecl decl = candidate.lookupParMethodDecl( 244 inferTypeArguments( 245 candidate.type(), 246 candidate.getParameterList(), 247 getClassInstanceExpr().getArgList(), 248 candidate.getTypeParameterList())); 249 potentiallyApplicable.add(decl); 250 } 251 } 252 return potentiallyApplicable; 253 } 254 255 /** 256 * Test if a method is applicable for this diamond access. 257 * @param candidate candidate method 258 * @return false if the candidate method is not applicable. 259 */ 260 protected boolean DiamondAccess.potentiallyApplicable( 261 GenericMethodDecl candidate) { 262 if (candidate.isVariableArity() && !(getClassInstanceExpr().arity() >= candidate.arity()-1)) { 263 return false; 264 } 265 if (!candidate.isVariableArity() && !(getClassInstanceExpr().arity() == candidate.arity())) { 266 return false; 267 } 268 269 java.util.List<TypeDecl> typeArgs = inferTypeArguments( 270 candidate.type(), 271 candidate.getParameterList(), 272 getClassInstanceExpr().getArgList(), 273 candidate.getTypeParameterList()); 274 Parameterization par = new SimpleParameterization(candidate.getTypeParameterList(), typeArgs); 275 if (typeArgs.size() != 0) { 276 if (candidate.getNumTypeParameter() != typeArgs.size()) { 277 return false; 278 } 279 for (int i = 0; i < candidate.getNumTypeParameter(); i++) { 280 if (!typeArgs.get(i).withinBounds(candidate.original().getTypeParameter(i), par)) { 281 return false; 282 } 283 } 284 } 285 return true; 286 } 287 288 /** 289 * @return true if the method is applicable by subtyping 290 */ 291 protected boolean DiamondAccess.applicableBySubtyping(ClassInstanceExpr expr, MethodDecl method) { 292 if (method.getNumParameter() != expr.getNumArg()) { 293 return false; 294 } 295 for (int i = 0; i < method.getNumParameter(); i++) { 296 if (!expr.getArg(i).type().instanceOf(method.getParameter(i).type())) { 297 return false; 298 } 299 } 300 return true; 301 } 302 303 /** 304 * @return true if the method is applicable by method invocation conversion 305 */ 306 protected boolean DiamondAccess.applicableByMethodInvocationConversion( 307 ClassInstanceExpr expr, MethodDecl method) { 308 if (method.getNumParameter() != expr.getNumArg()) { 309 return false; 310 } 311 for (int i = 0; i < method.getNumParameter(); i++) { 312 if (!expr.getArg(i).type().methodInvocationConversionTo( 313 method.getParameter(i).type())) { 314 return false; 315 } 316 } 317 return true; 318 } 319 320 /** 321 * @return true if the method is applicable by variable arity 322 */ 323 protected boolean DiamondAccess.applicableByVariableArity( 324 ClassInstanceExpr expr, MethodDecl method) { 325 for (int i = 0; i < method.getNumParameter() - 1; i++) { 326 if (!expr.getArg(i).type().methodInvocationConversionTo( 327 method.getParameter(i).type())) { 328 return false; 329 } 330 } 331 for (int i = method.getNumParameter() - 1; i < expr.getNumArg(); i++) { 332 if (!expr.getArg(i).type().methodInvocationConversionTo( 333 method.lastParameter().type().componentType())) { 334 return false; 335 } 336 } 337 return true; 338 } 339 340 /** 341 * Builds a copy of this ParameterDeclaration node where all occurrences 342 * of type variables in the original type parameter list have been replaced 343 * by the substitution type parameters. 344 * 345 * @return the substituted ParameterDeclaration node 346 */ 347 syn ParameterDeclaration ParameterDeclaration.substituted(Collection<TypeVariable> original, 348 List<TypeVariable> substitution) = 349 new ParameterDeclaration( 350 (Modifiers) getModifiers().treeCopyNoTransform(), 351 getTypeAccess().substituted(original, substitution), 352 getID()); 353 354 /** 355 * Builds a copy of this Access node where all occurrences 356 * of type variables in the original type parameter list have been replaced 357 * by the substitution type parameters. 358 * 359 * @return the substituted Access node 360 */ 361 syn Access Access.substituted(Collection<TypeVariable> original, 362 List<TypeVariable> substitution) = (Access) treeCopyNoTransform(); 363 364 /** 365 * Builds a copy of this TypeAccess node where all occurrences 366 * of type variables in the original type parameter list have been replaced 367 * by the substitution type parameters. 368 * 369 * @return the substituted TypeAccess node 370 */ 371 eq TypeAccess.substituted(Collection<TypeVariable> original, List<TypeVariable> substitution) { 372 TypeDecl decl = decl(); 373 int i = 0; 374 for (TypeVariable typeVar : original) { 375 if (typeVar == decl) { 376 return new TypeAccess(substitution.getChild(i).getID()); 377 } 378 i += 1; 379 } 380 return super.substituted(original, substitution); 381 } 382 383 /** 384 * Builds a copy of this ParTypeAccess node where all occurrences 385 * of type variables in the original type parameter list have been replaced 386 * by the substitution type parameters. 387 * 388 * @return the substituted ParTypeAccess node 389 */ 390 eq ParTypeAccess.substituted(Collection<TypeVariable> original, List<TypeVariable> substitution) { 391 List<Access> substArgs = new List<Access>(); 392 for (Access arg : getTypeArgumentList()) { 393 substArgs.add(arg.substituted(original, substitution)); 394 } 395 return new ParTypeAccess(getTypeAccess().substituted(original, substitution), substArgs); 396 } 397 398 /** 399 * @return true if this access is part of an anonymous class declaration 400 */ 401 inh boolean DiamondAccess.isAnonymousDecl(); 402 403 /** 404 * @return true if this ClassInstanceExpr is an anonymous class declaration 405 */ 406 eq ClassInstanceExpr.getAccess().isAnonymousDecl() = hasTypeDecl(); 407 408 /** 409 * @return false 410 */ 411 eq Program.getChild().isAnonymousDecl() = false; 412 413 /** 414 * @return true if the Access is part of a generic constructor invocation 415 * with explicit type arguments 416 */ 417 inh boolean DiamondAccess.isExplicitGenericConstructorAccess(); 418 419 /** 420 * @return true 421 */ 422 eq ParClassInstanceExpr.getAccess().isExplicitGenericConstructorAccess() = true; 423 424 /** 425 * @return false 426 */ 427 eq Program.getChild(int i).isExplicitGenericConstructorAccess() = false; 428 429 /** 430 * Checks if this diamond access is legal. 431 * The diamond access is not legal if it either is part of an inner class 432 * declaration, if it is used to access a non-generic type, or if it is 433 * part of a call to a generic constructor with explicit type arguments. 434 */ 435 public void DiamondAccess.typeCheck() { 436 if (isAnonymousDecl()) { 437 error("the diamond operator can not be used with anonymous classes"); 438 } 439 if (isExplicitGenericConstructorAccess()) { 440 error("the diamond operator may not be used with generic " 441 + "constructors with explicit type parameters"); 442 } 443 if (getClassInstanceExpr() == null) { 444 error("the diamond operator can only be used in class instance expressions"); 445 } 446 if (!(getTypeAccess().type() instanceof ParClassDecl)) { 447 error("the diamond operator can only be used to instantiate generic classes"); 448 } 449 } 450 451 }