001 /* 002 * JastAddJ is covered by the modified BSD License. You should have received 003 * a copy of the modified BSD license with this compiler. 004 * 005 * Copyright (c) 2011, Jesper Öqvist <jesper.oqvist@cs.lth.se> 006 * All rights reserved. 007 */ 008 009 /** 010 * Type inference for generic instance creation. 011 * 012 * According to the JLSv7 $15.9.1, type inference for generic 013 * instance creation uses the type inference for generic methods. 014 * 015 * Empty type argument lists (<>) are parsed as DiamondAccess 016 * nodes. The type of the DiamondAccess is computed by creating placeholder 017 * methods and using generic method type inference from Java1.5Frontend 018 * to find the inferred type arguments for the DiamondAccess. 019 */ 020 aspect TypeInference { 021 022 public void PlaceholderMethodDecl.nameCheck() { } 023 public void PlaceholderMethodDecl.typeCheck() { } 024 public void PlaceholderMethodDecl.exceptionHandling() { } 025 public void PlaceholderMethodDecl.checkUnreachableStmt() { } 026 public void PlaceholderMethodDecl.definiteAssignment() { } 027 public void PlaceholderMethodDecl.checkModifiers() { } 028 029 /** 030 * Placeholder methods are not pretty printed. 031 */ 032 public void PlaceholderMethodDecl.toString(StringBuffer s) { } 033 034 /** 035 * If this DiamondAccess node constitutes a legal use of 036 * the diamond operator, the inferred generic type for the 037 * enclosing class instance expression is returned. 038 */ 039 eq DiamondAccess.type() { 040 TypeDecl accessType = getTypeAccess().type(); 041 042 if (isAnonymousDecl()) 043 return accessType; 044 045 if (getClassInstanceExpr() == null) 046 // it is an error if the DiamondAccess does not occurr 047 // within a class instance creation expression, but this 048 // error is handled in typeCheck 049 return accessType; 050 051 if (!(accessType instanceof ParClassDecl)) 052 // it is an error if the TypeDecl of a DiamondAccess is not 053 // a generic type, but this error is handled in typeCheck 054 return accessType; 055 056 SimpleSet maxSpecific = chooseConstructor(); 057 058 if (maxSpecific.isEmpty()) 059 return getTypeAccess().type(); 060 061 MethodDecl constructor = (MethodDecl) maxSpecific.iterator().next(); 062 return constructor.type(); 063 } 064 065 syn boolean Access.isDiamond() = false; 066 eq DiamondAccess.isDiamond() = true; 067 068 inh ClassInstanceExpr DiamondAccess.getClassInstanceExpr(); 069 eq ClassInstanceExpr.getAccess().getClassInstanceExpr() = this; 070 eq Program.getChild(int i).getClassInstanceExpr() = null;// TODO 071 072 protected static SimpleSet DiamondAccess.mostSpecific( 073 SimpleSet maxSpecific, MethodDecl decl) { 074 if (maxSpecific.isEmpty()) { 075 maxSpecific = maxSpecific.add(decl); 076 } else { 077 if (decl.moreSpecificThan( 078 (MethodDecl)maxSpecific.iterator().next())) 079 maxSpecific = SimpleSet.emptySet.add(decl); 080 else if (!((MethodDecl)maxSpecific.iterator().next()). 081 moreSpecificThan(decl)) 082 maxSpecific = maxSpecific.add(decl); 083 } 084 return maxSpecific; 085 } 086 087 /** 088 * Choose a constructor for the diamond operator using placeholder 089 * methods. 090 */ 091 protected SimpleSet DiamondAccess.chooseConstructor() { 092 ClassInstanceExpr instanceExpr = getClassInstanceExpr(); 093 TypeDecl type = getTypeAccess().type(); 094 095 assert instanceExpr != null; 096 assert type instanceof ParClassDecl; 097 098 GenericClassDecl genericType = 099 (GenericClassDecl) ((ParClassDecl)type).genericDecl(); 100 101 List<PlaceholderMethodDecl> placeholderMethods = 102 genericType.getPlaceholderMethodList(); 103 104 SimpleSet maxSpecific = SimpleSet.emptySet; 105 Collection<MethodDecl> potentiallyApplicable = 106 potentiallyApplicable(placeholderMethods); 107 for (MethodDecl candidate : potentiallyApplicable) { 108 if (applicableBySubtyping(instanceExpr, candidate) || 109 applicableByMethodInvocationConversion( 110 instanceExpr, candidate) || 111 applicableByVariableArity(instanceExpr, candidate)) 112 maxSpecific = mostSpecific(maxSpecific, candidate); 113 114 } 115 return maxSpecific; 116 } 117 118 /** 119 * The placeholder method list for the constructors of this generic 120 * class. 121 * 122 * @return list of placeholder methods 123 */ 124 syn nta List<PlaceholderMethodDecl> GenericClassDecl.getPlaceholderMethodList() { 125 List<PlaceholderMethodDecl> placeholderMethods = 126 new List<PlaceholderMethodDecl>(); 127 List<TypeVariable> typeParams = getTypeParameterList(); 128 List<TypeVariable> classTypeVars = new List<TypeVariable>(); 129 List<Access> typeArgs = new List<Access>(); 130 131 // copy the list of type parameters 132 int arg = 0; 133 for (Iterator iter = typeParams.iterator(); iter.hasNext(); ++arg) { 134 String substName = "#"+arg; 135 typeArgs.add(new TypeAccess(substName)); 136 137 TypeVariable typeVar = (TypeVariable) iter.next(); 138 List<Access> typeBounds = new List<Access>(); 139 for (Access typeBound : typeVar.getTypeBoundList()) 140 typeBounds.add((Access) typeBound.cloneSubtree()); 141 classTypeVars.add( 142 new TypeVariable( 143 new Modifiers(), 144 substName, 145 new List<BodyDecl>(), 146 typeBounds)); 147 } 148 149 ParTypeAccess returnType = new ParTypeAccess( 150 createQualifiedAccess(), 151 typeArgs); 152 153 for (Iterator iter = constructors().iterator(); iter.hasNext(); ) { 154 ConstructorDecl decl = (ConstructorDecl)iter.next(); 155 if (decl instanceof ConstructorDeclSubstituted) 156 decl = ((ConstructorDeclSubstituted) decl).getOriginal(); 157 158 // filter accessible constructors 159 if (!decl.accessibleFrom(hostType())) 160 continue; 161 162 Collection<TypeVariable> originalTypeVars = 163 new LinkedList<TypeVariable>(); 164 List<TypeVariable> typeVars = new List<TypeVariable>(); 165 for (TypeVariable typeVar : typeParams) 166 originalTypeVars.add(typeVar); 167 for (TypeVariable typeVar : classTypeVars) 168 typeVars.add((TypeVariable) typeVar.cloneSubtree()); 169 170 if (decl instanceof GenericConstructorDecl) { 171 GenericConstructorDecl genericDecl = 172 (GenericConstructorDecl) decl; 173 List<TypeVariable> typeVariables = new List<TypeVariable>(); 174 for (int i = 0; i < genericDecl.getNumTypeParameter(); ++i) { 175 String substName = "#" + (arg+i); 176 177 TypeVariable typeVar = genericDecl.getTypeParameter(i); 178 originalTypeVars.add(typeVar); 179 List<Access> typeBounds = new List<Access>(); 180 for (Access typeBound : typeVar.getTypeBoundList()) 181 typeBounds.add((Access) typeBound.cloneSubtree()); 182 typeVars.add( 183 new TypeVariable( 184 new Modifiers(), 185 substName, 186 new List<BodyDecl>(), 187 typeBounds)); 188 } 189 } 190 191 List<ParameterDeclaration> substParameters = 192 new List<ParameterDeclaration>(); 193 for (ParameterDeclaration param : decl.getParameterList()) { 194 substParameters.add(param.substituted( 195 originalTypeVars, typeVars)); 196 } 197 198 List<Access> substExceptions = new List<Access>(); 199 for (Access exception : decl.getExceptionList()) { 200 substExceptions.add(exception.substituted( 201 originalTypeVars, typeVars)); 202 } 203 204 PlaceholderMethodDecl placeholderMethod = 205 new PlaceholderMethodDecl( 206 (Modifiers) decl.getModifiers().cloneSubtree(), 207 (Access) returnType.cloneSubtree(), 208 "#"+getID(), 209 substParameters, 210 substExceptions, 211 new Opt(new Block()), 212 typeVars); 213 214 placeholderMethods.add(placeholderMethod); 215 } 216 return placeholderMethods; 217 } 218 219 /** 220 * Select potentially applicable method declarations 221 * from a set of candidates. 222 * Type inference is applied to the (potentially) applicable candidates. 223 */ 224 protected Collection<MethodDecl> DiamondAccess.potentiallyApplicable( 225 List<PlaceholderMethodDecl> candidates) { 226 Collection<MethodDecl> potentiallyApplicable = 227 new LinkedList<MethodDecl>(); 228 for (GenericMethodDecl candidate : candidates) { 229 if (potentiallyApplicable(candidate)) { 230 MethodDecl decl = candidate.lookupParMethodDecl( 231 typeArguments(candidate)); 232 potentiallyApplicable.add(decl); 233 } 234 } 235 return potentiallyApplicable; 236 } 237 238 /** 239 * @return false if the candidate method is not applicable. 240 */ 241 protected boolean DiamondAccess.potentiallyApplicable( 242 GenericMethodDecl candidate) { 243 if (candidate.isVariableArity() && 244 !(getClassInstanceExpr().arity() >= candidate.arity()-1)) 245 return false; 246 if (!candidate.isVariableArity() && 247 !(getClassInstanceExpr().arity() == candidate.arity())) 248 return false; 249 250 java.util.List<TypeDecl> typeArgs = typeArguments(candidate); 251 if (typeArgs.size() != 0) { 252 if (candidate.getNumTypeParameter() != typeArgs.size()) 253 return false; 254 for (int i = 0; i < candidate.getNumTypeParameter(); i++) 255 if (!typeArgs.get(i).subtype( 256 candidate.original().getTypeParameter(i))) 257 return false; 258 } 259 return true; 260 } 261 262 inh TypeDecl DiamondAccess.typeObject(); 263 264 /** 265 * Type inference for placeholder methods. 266 */ 267 syn lazy java.util.List<TypeDecl> DiamondAccess.typeArguments( 268 MethodDecl decl) { 269 java.util.List<TypeDecl> typeArguments = new LinkedList<TypeDecl>(); 270 if (decl instanceof GenericMethodDecl) { 271 GenericMethodDecl method = (GenericMethodDecl) decl; 272 Collection<TypeDecl> arguments = computeConstraints(method); 273 if (arguments.isEmpty()) 274 return typeArguments; 275 int i = 0; 276 for (TypeDecl argument : arguments) { 277 if (argument == null) { 278 TypeVariable v = method.original().getTypeParameter(i); 279 if (v.getNumTypeBound() == 0) 280 argument = typeObject(); 281 else if (v.getNumTypeBound() == 1) 282 argument = v.getTypeBound(0).type(); 283 else 284 argument = v.lubType(); 285 } 286 typeArguments.add(argument); 287 288 i += 1; 289 } 290 } 291 return typeArguments; 292 } 293 294 /** 295 * Diamond type inference. 296 */ 297 public Collection<TypeDecl> DiamondAccess.computeConstraints( 298 GenericMethodDecl decl) { 299 Constraints c = new Constraints(); 300 // store type parameters 301 for (int i = 0; i < decl.original().getNumTypeParameter(); i++) 302 c.addTypeVariable(decl.original().getTypeParameter(i)); 303 304 ClassInstanceExpr instanceExpr = getClassInstanceExpr(); 305 for (int i = 0; i < instanceExpr.getNumArg(); i++) { 306 TypeDecl A = instanceExpr.getArg(i).type(); 307 int index = i >= decl.getNumParameter() ? 308 decl.getNumParameter() - 1 : i; 309 TypeDecl F = decl.getParameter(index).type(); 310 if (decl.getParameter(index) instanceof 311 VariableArityParameterDeclaration && 312 (instanceExpr.getNumArg() != decl.getNumParameter() || 313 !A.isArrayDecl())) { 314 F = F.componentType(); 315 } 316 c.convertibleTo(A, F); 317 } 318 if (c.rawAccess) 319 return new ArrayList(); 320 321 c.resolveEqualityConstraints(); 322 c.resolveSupertypeConstraints(); 323 324 if (c.unresolvedTypeArguments()) { 325 TypeDecl S = assignConvertedType(); 326 if (S.isUnboxedPrimitive()) 327 S = S.boxed(); 328 TypeDecl R = decl.type(); 329 if (R.isVoid()) 330 R = typeObject(); 331 332 c.convertibleFrom(S, R); 333 c.resolveEqualityConstraints(); 334 c.resolveSupertypeConstraints(); 335 c.resolveSubtypeConstraints(); 336 } 337 338 return c.typeArguments(); 339 } 340 341 /** 342 * @return true if the method is applicable by subtyping 343 */ 344 protected boolean DiamondAccess.applicableBySubtyping( 345 ClassInstanceExpr expr, MethodDecl method) { 346 if (method.getNumParameter() != expr.getNumArg()) 347 return false; 348 for (int i = 0; i < method.getNumParameter(); i++) 349 if(!expr.getArg(i).type().instanceOf(method.getParameter(i).type())) 350 return false; 351 return true; 352 } 353 354 /** 355 * @return true if the method is applicable by method invocation conversion 356 */ 357 protected boolean DiamondAccess.applicableByMethodInvocationConversion( 358 ClassInstanceExpr expr, MethodDecl method) { 359 if (method.getNumParameter() != expr.getNumArg()) 360 return false; 361 for (int i = 0; i < method.getNumParameter(); i++) 362 if (!expr.getArg(i).type().methodInvocationConversionTo( 363 method.getParameter(i).type())) 364 return false; 365 return true; 366 } 367 368 /** 369 * @return true if the method is applicable by variable arity 370 */ 371 protected boolean DiamondAccess.applicableByVariableArity( 372 ClassInstanceExpr expr, MethodDecl method) { 373 for (int i = 0; i < method.getNumParameter() - 1; i++) 374 if(!expr.getArg(i).type().methodInvocationConversionTo( 375 method.getParameter(i).type())) 376 return false; 377 for (int i = method.getNumParameter() - 1; i < expr.getNumArg(); i++) 378 if (!expr.getArg(i).type().methodInvocationConversionTo( 379 method.lastParameter().type().componentType())) 380 return false; 381 return true; 382 } 383 384 /** 385 * Builds a copy of this ParameterDeclaration node where all occurrences 386 * of type variables in the original type parameter list have been replaced 387 * by the substitution type parameters. 388 * 389 * @return the substituted ParameterDeclaration node 390 */ 391 syn ParameterDeclaration ParameterDeclaration.substituted( 392 Collection<TypeVariable> original, 393 List<TypeVariable> substitution) = 394 new ParameterDeclaration( 395 (Modifiers) getModifiers().cloneSubtree(), 396 getTypeAccess().substituted(original, substitution), 397 getID()); 398 399 /** 400 * Builds a copy of this Access node where all occurrences 401 * of type variables in the original type parameter list have been replaced 402 * by the substitution type parameters. 403 * 404 * @return the substituted Access node 405 */ 406 syn Access Access.substituted( 407 Collection<TypeVariable> original, 408 List<TypeVariable> substitution) = 409 (Access) cloneSubtree(); 410 411 /** 412 * Builds a copy of this TypeAccess node where all occurrences 413 * of type variables in the original type parameter list have been replaced 414 * by the substitution type parameters. 415 * 416 * @return the substituted TypeAccess node 417 */ 418 eq TypeAccess.substituted( 419 Collection<TypeVariable> original, 420 List<TypeVariable> substitution) { 421 TypeDecl decl = decl(); 422 int i = 0; 423 for (TypeVariable typeVar : original) { 424 if (typeVar == decl) 425 return new TypeAccess(substitution.getChild(i).getID()); 426 i += 1; 427 } 428 return super.substituted(original, substitution); 429 } 430 431 /** 432 * Builds a copy of this ParTypeAccess node where all occurrences 433 * of type variables in the original type parameter list have been replaced 434 * by the substitution type parameters. 435 * 436 * @return the substituted ParTypeAccess node 437 */ 438 eq ParTypeAccess.substituted( 439 Collection<TypeVariable> original, 440 List<TypeVariable> substitution) { 441 List<Access> substArgs = new List<Access>(); 442 for (Access arg : getTypeArgumentList()) 443 substArgs.add(arg.substituted(original, substitution)); 444 return new ParTypeAccess( 445 getTypeAccess().substituted(original, substitution), 446 substArgs); 447 } 448 449 /** 450 * @return true if this access is part of an anonymous class declaration 451 */ 452 inh boolean DiamondAccess.isAnonymousDecl(); 453 454 /** 455 * @return true if this ClassInstanceExpr is an anonymous class declaration 456 */ 457 eq ClassInstanceExpr.getAccess().isAnonymousDecl() = hasTypeDecl(); 458 459 /** 460 * @return false 461 */ 462 eq Program.getChild().isAnonymousDecl() = false; 463 464 /** 465 * @return true if the Access is part of a generic constructor invocation 466 * with explicit type arguments 467 */ 468 inh boolean DiamondAccess.isExplicitGenericConstructorAccess(); 469 470 /** 471 * @return true 472 */ 473 eq ParClassInstanceExpr.getAccess().isExplicitGenericConstructorAccess() = 474 true; 475 476 /** 477 * @return false 478 */ 479 eq Program.getChild(int i).isExplicitGenericConstructorAccess() = false; 480 481 /** 482 * Checks if this diamond access is legal. 483 * The diamond access is not legal if it either is part of an inner class 484 * declaration, if it is used to access a non-generic type, or if it is 485 * part of a call to a generic constructor with explicit type arguments. 486 */ 487 public void DiamondAccess.typeCheck() { 488 if (isAnonymousDecl()) 489 error("the diamond operator can not be used with "+ 490 "anonymous classes"); 491 if (isExplicitGenericConstructorAccess()) 492 error("the diamond operator may not be used with generic "+ 493 "constructors with explicit type parameters"); 494 if (getClassInstanceExpr() == null) 495 error("the diamond operator can only be used in "+ 496 "class instance expressions"); 497 if (!(getTypeAccess().type() instanceof ParClassDecl)) 498 error("the diamond operator can only be used to "+ 499 "instantiate generic classes"); 500 } 501 502 /** 503 * Pretty printing of diamond access. 504 */ 505 public void DiamondAccess.toString(StringBuffer sb) { 506 getTypeAccess().toString(sb); 507 sb.append("<>"); 508 } 509 }