• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pmd / pmd / #3722

pending completion
#3722

push

github actions

adangel
Suppress MissingOverride for Chars::isEmpty (#4291)

67270 of 127658 relevant lines covered (52.7%)

0.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.64
/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/Infer.java
1
/*
2
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3
 */
4

5
package net.sourceforge.pmd.lang.java.types.internal.infer;
6

7
import static net.sourceforge.pmd.lang.java.types.TypeConversion.capture;
8
import static net.sourceforge.pmd.lang.java.types.TypeConversion.isWilcardParameterized;
9
import static net.sourceforge.pmd.lang.java.types.TypeOps.asList;
10
import static net.sourceforge.pmd.lang.java.types.TypeOps.subst;
11
import static net.sourceforge.pmd.lang.java.types.internal.infer.ExprOps.isPertinentToApplicability;
12
import static net.sourceforge.pmd.lang.java.types.internal.infer.MethodResolutionPhase.INVOC_LOOSE;
13
import static net.sourceforge.pmd.util.CollectionUtil.listOf;
14
import static net.sourceforge.pmd.util.CollectionUtil.setOf;
15

16
import java.util.ArrayList;
17
import java.util.Collections;
18
import java.util.List;
19

20
import org.checkerframework.checker.nullness.qual.NonNull;
21
import org.checkerframework.checker.nullness.qual.Nullable;
22

23
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
24
import net.sourceforge.pmd.lang.java.types.JArrayType;
25
import net.sourceforge.pmd.lang.java.types.JClassType;
26
import net.sourceforge.pmd.lang.java.types.JMethodSig;
27
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
28
import net.sourceforge.pmd.lang.java.types.JTypeVar;
29
import net.sourceforge.pmd.lang.java.types.Substitution;
30
import net.sourceforge.pmd.lang.java.types.TypeOps;
31
import net.sourceforge.pmd.lang.java.types.TypeOps.Convertibility;
32
import net.sourceforge.pmd.lang.java.types.TypeSystem;
33
import net.sourceforge.pmd.lang.java.types.internal.infer.ExprCheckHelper.ExprChecker;
34
import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.CtorInvocationMirror;
35
import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.FunctionalExprMirror;
36
import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.InvocationMirror;
37
import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.InvocationMirror.MethodCtDecl;
38
import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.LambdaExprMirror;
39
import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.MethodRefMirror;
40
import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.PolyExprMirror;
41
import net.sourceforge.pmd.lang.java.types.internal.infer.InferenceVar.BoundKind;
42
import net.sourceforge.pmd.util.CollectionUtil;
43

44
/**
45
 * Main entry point for type inference.
46
 */
47
@SuppressWarnings({"PMD.FieldNamingConventions", "PMD.CompareObjectsWithEquals"})
1✔
48
public final class Infer {
49

50
    final ExprOps exprOps;
51

52
    public final TypeInferenceLogger LOG; // SUPPRESS CHECKSTYLE just easier to read I think
53

54
    private final boolean isPreJava8;
55
    private final TypeSystem ts;
56

57
    final MethodCtDecl NO_CTDECL; // SUPPRESS CHECKSTYLE same
58

59
    /** This is a sentinel for when the CTDecl was resolved, but invocation failed. */
60
    final MethodCtDecl FAILED_INVOCATION; // SUPPRESS CHECKSTYLE same
61

62
    private final SupertypeCheckCache supertypeCheckCache = new SupertypeCheckCache();
1✔
63

64
    /**
65
     * Creates a new instance.
66
     *
67
     * @param ts         Type system
68
     * @param jdkVersion JDK version to use. Type inference was changed
69
     *                   in Java 8 to propagate the context type.
70
     * @param logger     Strategy to log failures
71
     */
72
    public Infer(TypeSystem ts, int jdkVersion, TypeInferenceLogger logger) {
1✔
73
        this.ts = ts;
1✔
74
        this.isPreJava8 = jdkVersion < 8;
1✔
75
        this.LOG = logger;
1✔
76

77
        this.NO_CTDECL = MethodCtDecl.unresolved(ts);
1✔
78
        this.FAILED_INVOCATION = MethodCtDecl.unresolved(ts);
1✔
79

80
        this.exprOps = new ExprOps(this);
1✔
81
    }
1✔
82

83
    public boolean isPreJava8() {
84
        return isPreJava8;
1✔
85
    }
86

87
    public TypeSystem getTypeSystem() {
88
        return ts;
1✔
89
    }
90

91
    public TypeInferenceLogger getLogger() {
92
        return LOG;
×
93
    }
94

95
    public PolySite<FunctionalExprMirror> newFunctionalSite(FunctionalExprMirror mirror, @Nullable JTypeMirror expectedType) {
96
        return new PolySite<>(mirror, expectedType);
1✔
97
    }
98

99
    public MethodCallSite newCallSite(InvocationMirror expr, @Nullable JTypeMirror expectedType) {
100
        return newCallSite(expr, expectedType, null, null, false);
1✔
101
    }
102

103
    /** Site for a nested poly expr. */
104
    // package
105
    MethodCallSite newCallSite(InvocationMirror expr,
106
                               @Nullable JTypeMirror expectedType,
107
                               @Nullable MethodCallSite outerSite,
108
                               @Nullable InferenceContext outerCtx,
109
                               boolean isSpecificityCheck) {
110
        return new MethodCallSite(expr, expectedType, outerSite, outerCtx != null ? outerCtx : emptyContext(), isSpecificityCheck);
1✔
111
    }
112

113
    InferenceContext emptyContext() {
114
        return newContextFor(Collections.emptyList());
1✔
115
    }
116

117
    @NonNull
118
    InferenceContext newContextFor(JMethodSig m) {
119
        return newContextFor(m.getTypeParameters());
1✔
120
    }
121

122
    InferenceContext newContextFor(List<JTypeVar> tvars) {
123
        return new InferenceContext(ts, supertypeCheckCache, tvars, LOG);
1✔
124
    }
125

126
    /**
127
     * Infer lambdas and method references that have a target type: cast contexts,
128
     * and some assignment contexts (not inferred, not return from lambda).
129
     */
130
    public void inferFunctionalExprInUnambiguousContext(PolySite<FunctionalExprMirror> site) {
131
        FunctionalExprMirror expr = site.getExpr();
1✔
132
        JTypeMirror expected = site.getExpectedType();
1✔
133
        try {
134
            if (expected == null) {
1✔
135
                throw ResolutionFailedException.missingTargetTypeForFunctionalExpr(LOG, expr);
1✔
136
            }
137
            addBoundOrDefer(null, emptyContext(), INVOC_LOOSE, expr, expected);
1✔
138
        } catch (ResolutionFailedException rfe) {
1✔
139
            rfe.getFailure().addContext(null, site, null);
1✔
140
            LOG.logResolutionFail(rfe.getFailure());
1✔
141
            // here we set expected if not null, the lambda will have the target type
142
            expr.setInferredType(expected == null ? ts.UNKNOWN : expected);
1✔
143
            if (expr instanceof MethodRefMirror) {
1✔
144
                MethodRefMirror mref = (MethodRefMirror) expr;
1✔
145
                mref.setFunctionalMethod(ts.UNRESOLVED_METHOD);
1✔
146
                mref.setCompileTimeDecl(ts.UNRESOLVED_METHOD);
1✔
147
            } else {
1✔
148
                LambdaExprMirror lambda = (LambdaExprMirror) expr;
1✔
149
                lambda.setFunctionalMethod(ts.UNRESOLVED_METHOD);
1✔
150
            }
151
        }
1✔
152
    }
1✔
153

154

155
    /**
156
     * Determines the most specific applicable method for the given call site.
157
     *
158
     * <p>The returned method type may be {@link TypeSystem#UNRESOLVED_METHOD},
159
     * in which case no method is applicable (compile-time error).
160
     *
161
     * <p>The returned method type may contain un-instantiated inference
162
     * variables, which depend on the target type. In that case those
163
     * variables and their bounds will have been duplicated into the
164
     * inference context of the [site].
165
     *
166
     * <p>The given call site should mention information like the expected
167
     * return type, to help inference. This should be non-null if we're
168
     * in an invocation or assignment context, otherwise can be left blank.
169
     */
170
    public void inferInvocationRecursively(MethodCallSite site) {
171
        MethodCtDecl ctdecl = goToInvocationWithFallback(site);
1✔
172
        InvocationMirror expr = site.getExpr();
1✔
173
        expr.setCtDecl(ctdecl);
1✔
174
        if (ctdecl == NO_CTDECL) {
1✔
175
            expr.setInferredType(fallbackType(expr));
1✔
176
        } else {
177
            expr.setInferredType(ctdecl.getMethodType().getReturnType());
1✔
178
        }
179
    }
1✔
180

181
    private MethodCtDecl goToInvocationWithFallback(MethodCallSite site) {
182
        MethodCtDecl ctdecl = getCompileTimeDecl(site);
1✔
183
        if (ctdecl == NO_CTDECL) { // NOPMD CompareObjectsWithEquals
1✔
184
            return NO_CTDECL;
1✔
185
        }
186

187
        site.clearFailures();
1✔
188

189
        // do invocation
190

191
        { // reduce scope of invocType, outside of here it's failed
192
            final MethodCtDecl invocType = finishInstantiation(site, ctdecl);
1✔
193
            if (invocType != FAILED_INVOCATION) { // NOPMD CompareObjectsWithEquals
1✔
194
                return invocType;
1✔
195
            }
196
        }
197
        // ok we failed, we can still use some info from the ctdecl
198

199
        JMethodSig fallback = deleteTypeParams(ctdecl.getMethodType().internalApi().adaptedMethod());
1✔
200
        LOG.fallbackInvocation(fallback, site);
1✔
201

202
        return ctdecl.withMethod(fallback, true);
1✔
203
    }
204

205
    private JTypeMirror fallbackType(PolyExprMirror expr) {
206
        JTypeMirror t = expr.unresolvedType();
1✔
207
        return t == null ? ts.UNKNOWN : t;
1✔
208
    }
209

210
    // If the invocation fails, replace type parameters with a placeholder,
211
    // to not hide a bad failure, while preserving the method if possible
212
    private JMethodSig deleteTypeParams(JMethodSig m) {
213
        if (!m.isGeneric()) {
1✔
214
            return m;
×
215
        }
216
        List<JTypeVar> tparams = m.getTypeParameters();
1✔
217
        List<JTypeMirror> nErrors = Collections.nCopies(tparams.size(), ts.ERROR);
1✔
218
        return m.subst(Substitution.mapping(tparams, nErrors));
1✔
219
    }
220

221
    /**
222
     * Similar to {@link #inferInvocationRecursively(MethodCallSite)} for
223
     * subexpressions. This never returns a fallback method.
224
     *
225
     * <p>A return of {@link #NO_CTDECL} indicates no overload is applicable.
226
     * <p>A return of {@link #FAILED_INVOCATION} means there is a maximally
227
     * specific compile-time declaration, but it failed invocation, meaning,
228
     * it couldn't be linked to its context. If so, the outer inference process
229
     * must be terminated with a failure.
230
     * <p>This ne
231
     *
232
     * <p>The returned method type may contain un-instantiated inference
233
     * variables, which depend on the target type. In that case those
234
     * variables and their bounds will have been duplicated into the
235
     * inference context of the [site].
236
     */
237
    @NonNull MethodCtDecl determineInvocationTypeOrFail(MethodCallSite site) {
238
        MethodCtDecl ctdecl = getCompileTimeDecl(site);
1✔
239
        if (ctdecl == NO_CTDECL) { // NOPMD CompareObjectsWithEquals
1✔
240
            return ctdecl;
1✔
241
        }
242

243
        return finishInstantiation(site, ctdecl);
1✔
244
    }
245

246

247
    public @NonNull MethodCtDecl getCompileTimeDecl(MethodCallSite site) {
248
        if (site.getExpr().getCtDecl() == null) {
1✔
249
            MethodCtDecl ctdecl = computeCompileTimeDecl(site);
1✔
250
            site.getExpr().setCtDecl(ctdecl); // cache it for later
1✔
251
        }
252
        return site.getExpr().getCtDecl();
1✔
253
    }
254

255
    /**
256
     * Determines the most specific applicable method for the given call site.
257
     *
258
     * <p>The returned method type may be null, in which case no method is
259
     * applicable (compile-time error).
260
     */
261
    private @NonNull MethodCtDecl computeCompileTimeDecl(MethodCallSite site) {
262

263
        /*
264
         *  The process starts with a set of candidates and refines it
265
         *  iteratively. Applicability/best applicability are the only
266
         *  ones which needs inference.
267
         *
268
         *  visible ⊇ accessible ⊇ potentially applicable ⊇ applicable ⊇ best applicable
269
         */
270
        List<JMethodSig> potentiallyApplicable = new ArrayList<>();
1✔
271
        for (JMethodSig it : site.getExpr().getAccessibleCandidates()) {
1✔
272
            if (isPotentiallyApplicable(it, site.getExpr())) {
1✔
273
                potentiallyApplicable.add(it);
1✔
274
            }
275
        }
1✔
276

277
        if (potentiallyApplicable.isEmpty()) {
1✔
278
            LOG.noApplicableCandidates(site);
1✔
279
            return NO_CTDECL;
1✔
280
        }
281

282
        for (MethodResolutionPhase phase : MethodResolutionPhase.APPLICABILITY_TESTS) {
1✔
283
            PhaseOverloadSet applicable = new PhaseOverloadSet(this, phase, site);
1✔
284
            for (JMethodSig m : potentiallyApplicable) {
1✔
285
                site.resetInferenceData();
1✔
286

287
                MethodCtDecl candidate = logInference(site, phase, m);
1✔
288

289
                if (!candidate.isFailed()) {
1✔
290
                    applicable.add(candidate);
1✔
291
                }
292
            }
1✔
293

294
            if (applicable.nonEmpty()) {
1✔
295
                MethodCtDecl bestApplicable = applicable.getMostSpecificOrLogAmbiguity(LOG);
1✔
296
                JMethodSig adapted = ExprOps.adaptGetClass(bestApplicable.getMethodType(),
1✔
297
                                                           site.getExpr()::getErasedReceiverType);
1✔
298
                return bestApplicable.withMethod(adapted);
1✔
299
            }
300
        }
1✔
301

302

303
        LOG.noCompileTimeDeclaration(site);
1✔
304

305
        return NO_CTDECL;
1✔
306
    }
307

308
    @NonNull MethodCtDecl finishInstantiation(MethodCallSite site, MethodCtDecl ctdecl) {
309
        JMethodSig m = ctdecl.getMethodType();
1✔
310
        InvocationMirror expr = site.getExpr();
1✔
311

312
        site.loadInferenceData(ctdecl);
1✔
313
        site.setInInvocation();
1✔
314

315
        if (site.canSkipInvocation()) {
1✔
316
            assert assertReturnIsGround(m);
1✔
317

318
            expr.setInferredType(m.getReturnType());
1✔
319
            LOG.skipInstantiation(m, site);
1✔
320
            return ctdecl;
1✔
321
        }
322

323
        // start the inference over with the original method, including
324
        // arguments that are not pertinent to applicability (lambdas)
325
        // to instantiate all tvars
326

327
        return logInference(site,
1✔
328
                            ctdecl.getResolvePhase().asInvoc(),
1✔
329
                            ctdecl.getMethodType().internalApi().adaptedMethod());
1✔
330
    }
331

332
    // this is skipped when running without assertions
333
    private boolean assertReturnIsGround(JMethodSig t) {
334
        subst(t.getReturnType(), var -> {
1✔
335
            assert !(var instanceof InferenceVar)
1✔
336
                : "Expected a ground type " + t;
337
            assert !(var instanceof JTypeVar) || !t.getTypeParameters().contains(var)
1✔
338
                : "Some type parameters have not been instantiated";
339
            return var;
1✔
340
        });
341
        return true;
1✔
342
    }
343

344

345
    private @NonNull MethodCtDecl logInference(MethodCallSite site, MethodResolutionPhase phase, JMethodSig m) {
346
        LOG.startInference(m, site, phase);
1✔
347
        @Nullable JMethodSig candidate = instantiateMethodOrCtor(site, phase, m);
1✔
348
        LOG.endInference(candidate);
1✔
349

350
        if (candidate == null) {
1✔
351
            return FAILED_INVOCATION;
1✔
352
        } else {
353
            return new MethodCtDecl(candidate,
1✔
354
                                    phase,
355
                                    site.canSkipInvocation(),
1✔
356
                                    site.needsUncheckedConversion(),
1✔
357
                                    false);
358
        }
359
    }
360

361

362
    private @Nullable JMethodSig instantiateMethodOrCtor(MethodCallSite site, MethodResolutionPhase phase, JMethodSig m) {
363
        return site.getExpr() instanceof CtorInvocationMirror ? instantiateConstructor(m, site, phase)
1✔
364
                                                              : instantiateMethod(m, site, phase);
1✔
365
    }
366

367

368
    /**
369
     * Infer type arguments for the given method at the method call.
370
     * Returns null if no instantiations exist, ie the method is not
371
     * applicable.
372
     *
373
     * @param m     Candidate method
374
     * @param site  Descriptor of the context of the call.
375
     * @param phase Phase in which the method is reviewed
376
     */
377
    private @Nullable JMethodSig instantiateMethod(JMethodSig m,
378
                                                   MethodCallSite site,
379
                                                   MethodResolutionPhase phase) {
380
        if (phase.requiresVarargs() && !m.isVarargs()) {
1✔
381
            return null; // don't log such a dumb mistake
1✔
382
        }
383
        try {
384
            return instantiateMaybeNoInfer(m, site, phase);
1✔
385
        } catch (ResolutionFailedException e) {
1✔
386
            ResolutionFailure failure = e.getFailure();
1✔
387
            failure.addContext(m, site, phase);
1✔
388
            LOG.logResolutionFail(failure);
1✔
389
            return null;
1✔
390
        }
391
    }
392

393
    private @Nullable JMethodSig instantiateConstructor(JMethodSig cons,
394
                                                        MethodCallSite site,
395
                                                        MethodResolutionPhase phase) {
396

397
        CtorInvocationMirror expr = (CtorInvocationMirror) site.getExpr();
1✔
398

399
        JTypeMirror newTypeMaybeInvalid = expr.getNewType();
1✔
400
        if (!(newTypeMaybeInvalid instanceof JClassType)) {
1✔
401
            // no constructor, note also, that array type constructors
402
            // don't go through these routines because there's no overloading
403
            // of array ctors. They're handled entirely in LazyTypeResolver.
404
            return null;
×
405
        }
406

407
        JClassType newType = (JClassType) newTypeMaybeInvalid;
1✔
408
        boolean isAdapted = needsAdaptation(expr, newType);
1✔
409
        JMethodSig adapted = isAdapted
1✔
410
                             ? adaptGenericConstructor(cons, newType, expr)
1✔
411
                             : cons;
1✔
412

413
        site.maySkipInvocation(!isAdapted);
1✔
414

415
        @Nullable JMethodSig result = instantiateMethod(adapted, site, phase);
1✔
416
        if (isAdapted && result != null) {
1✔
417
            // undo the adaptation
418

419
            JTypeMirror rtype = result.getReturnType();
1✔
420
            if (!rtype.isInterface()) {
1✔
421
                // this is for anonymous class ctors
422
                // an interface cannot declare a constructor
423
                result = result.internalApi().withOwner(rtype);
1✔
424
            }
425
            return result.internalApi().withTypeParams(null);
1✔
426

427
        }
428
        return result;
1✔
429
    }
430

431
    private boolean needsAdaptation(CtorInvocationMirror expr, JClassType newType) {
432
        return expr.isDiamond()
1✔
433
            || newType.isParameterizedType() // ???
1✔
434
            || expr.isAnonymous();
1✔
435
    }
436

437
    /**
438
     * Transform the constructor of a generic class so that its type parameters
439
     * mention the type params of the declaring class. This enables diamond
440
     * inference, we just treat the class type params to infer as
441
     * additional inference variables.
442
     *
443
     * <p>E.g. for
444
     *
445
     * {@code class ArrayList<T> { ArrayList() {} } }
446
     *
447
     * the constructor is represented as a method type:
448
     *
449
     * {@code <T> ArrayList<T> new() }
450
     *
451
     * the return type being that of the created instance.
452
     */
453
    private static JMethodSig adaptGenericConstructor(JMethodSig cons, JClassType newType, CtorInvocationMirror expr) {
454
        assert cons.isConstructor() : cons + " should be a constructor";
1✔
455

456
        if (cons.getDeclaringType().isArray()) {
1✔
457
            // array methods do not need to be adapted and don't support it
458
            return cons;
×
459
        }
460

461
        // replace the return type so that anonymous class ctors return the supertype
462
        JMethodSig adaptedSig = cons.internalApi().withReturnType(newType).internalApi().markAsAdapted();
1✔
463

464
        List<JTypeVar> newTypeFormals = newType.getFormalTypeParams();
1✔
465
        if (newTypeFormals.isEmpty()) {
1✔
466
            // non-generic type
467
            return adaptedSig;
1✔
468
        } else {
469
            // else transform the constructor to add the type parameters
470
            // of the constructed type
471
            List<JTypeVar> consParams = cons.getTypeParameters();
1✔
472
            if (consParams.size() > cons.getSymbol().getTypeParameterCount()) {
1✔
473
                // it's already been adapted
474
                assert consParams.equals(CollectionUtil.concatView(cons.getSymbol().getTypeParameters(), newTypeFormals));
1✔
475
                return adaptedSig;
1✔
476
            } else if (!expr.isDiamond()) {
1✔
477
                // it doesn't need adaptation, we're not doing diamond inference
478
                return adaptedSig;
1✔
479
            }
480

481
            List<JTypeVar> tparams = CollectionUtil.concatView(consParams, newTypeFormals);
1✔
482

483
            // type parameters are not part of the adapted signature, so that when we reset
484
            // the signature for invocation inference, we don't duplicate new type parameters
485
            return adaptedSig.internalApi().withTypeParams(tparams).internalApi().markAsAdapted();
1✔
486
        }
487
    }
488

489
    /**
490
     * Catch the easy cases before starting inference.
491
     */
492
    private JMethodSig instantiateMaybeNoInfer(JMethodSig m, MethodCallSite site, MethodResolutionPhase phase) {
493

494
        if (!m.isGeneric()) {
1✔
495
            // non-generic methods may mention explicit type arguments
496
            // for compatibility, they must be ignored.
497

498
            // check that the arguments are conformant
499
            // the inference context is empty because all param types are ground.
500
            addArgsConstraints(emptyContext(), m, site, phase);
1✔
501
            return m;
1✔
502
        }
503

504
        InvocationMirror expr = site.getExpr();
1✔
505
        List<JTypeMirror> explicitTargs = expr.getExplicitTypeArguments();
1✔
506

507
        if (!explicitTargs.isEmpty()) {
1✔
508
            // we have explicit type arguments
509
            List<JTypeVar> tparams = m.getTypeParameters();
1✔
510

511
            if (tparams.size() != explicitTargs.size()) {
1✔
512
                // normally checked by isPotentiallyApplicable
513
                throw ResolutionFailedException.incompatibleTypeParamCount(LOG, site.getExpr(), m, explicitTargs.size(), tparams.size());
×
514
            }
515

516
            Substitution explicitSubst = Substitution.mapping(tparams, explicitTargs);
1✔
517

518
            for (int i = 0; i < tparams.size(); i++) {
1✔
519
                JTypeMirror explicit = explicitTargs.get(i);
1✔
520
                JTypeMirror upperBound = tparams.get(i).getUpperBound().subst(explicitSubst);
1✔
521

522
                if (explicit.isConvertibleTo(upperBound).never()) {
1✔
523
                    throw ResolutionFailedException.incompatibleBound(LOG, explicit, upperBound, expr.getExplicitTargLoc(i));
×
524
                }
525
            }
526

527

528
            JMethodSig subst = m.subst(explicitSubst);
1✔
529

530
            // check that the arguments are conformant
531
            // the inference context is empty because all param types are ground.
532
            addArgsConstraints(emptyContext(), subst, site, phase);
1✔
533

534
            return subst;
1✔
535
        }
536

537

538
        site.maySkipInvocation(!ExprOps.isContextDependent(m) && site.getOuterCtx().isGround(m.getReturnType()));
1✔
539

540
        return instantiateImpl(m, site, phase);
1✔
541
    }
542

543
    /**
544
     * Perform actual inference. If the method is return-type-polymorphic,
545
     * then we delegate the solving to the call site's inference context,
546
     * which knows more, however we add inference vars and their constraints
547
     * to it.
548
     */
549
    private JMethodSig instantiateImpl(JMethodSig m, MethodCallSite site, MethodResolutionPhase phase) {
550

551
        InferenceContext infCtx = newContextFor(m); // b0
1✔
552
        LOG.ctxInitialization(infCtx, m);
1✔
553

554
        try {
555

556
            if (phase.isInvocation() && !isPreJava8) {
1✔
557
                m = doReturnChecksAndChangeReturnType(m, site, infCtx);
1✔
558
            }
559

560
            addArgsConstraints(infCtx, m, site, phase); // c
1✔
561
            infCtx.incorporate(); // b2
1✔
562

563
            if (phase.isInvocation()) {
1✔
564

565
                boolean shouldPropagate = shouldPropagateOutwards(m.getReturnType(), site, infCtx);
1✔
566

567
                //propagate outwards if needed
568
                if (shouldPropagate) {
1✔
569
                    // propagate inference context outwards and exit
570
                    // the outer context will solve the variables and call listeners
571
                    // of this context
572
                    LOG.propagateAndAbort(infCtx, site.getOuterCtx());
1✔
573
                    infCtx.duplicateInto(site.getOuterCtx());
1✔
574
                    return infCtx.mapToIVars(m);
1✔
575
                }
576
            }
577

578
            // this may throw for incompatible bounds
579
            boolean isDone = infCtx.solve(/*onlyBoundedVars:*/isPreJava8());
1✔
580

581
            if (isPreJava8() && !isDone) {
1✔
582
                // this means we're not in an invocation context,
583
                // if we are, we must ignore it in java 7
584
                if (site.getOuterCtx().isEmpty()) {
1✔
585
                    // Then add the return contraints late
586
                    // Java 7 only uses the context type if the arguments are not enough
587
                    // https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12.2.8
588
                    m = doReturnChecksAndChangeReturnType(m, site, infCtx);
1✔
589
                }
590
                // otherwise force solving remaining vars
591
                infCtx.solve();
1✔
592
            }
593

594
            if (infCtx.needsUncheckedConversion()) {
1✔
595
                site.setNeedsUncheckedConversion();
1✔
596
            }
597

598
            // instantiate vars and return
599
            return InferenceContext.finalGround(infCtx.mapToIVars(m));
1✔
600
        } finally {
601
            // Note that even if solve succeeded, listeners checking deferred
602
            // bounds may still throw ResolutionFailedException, in which case
603
            // by the laws of finally, this exception will be thrown and the
604
            // return value will be ignored.
605
            infCtx.callListeners();
1✔
606
        }
607
    }
608

609
    private JMethodSig doReturnChecksAndChangeReturnType(JMethodSig m, MethodCallSite site, InferenceContext infCtx) {
610
        LOG.startReturnChecks();
1✔
611
        JTypeMirror actualResType = addReturnConstraints(infCtx, m, site); // b3
1✔
612
        LOG.endReturnChecks();
1✔
613
        m = m.internalApi().withReturnType(actualResType);
1✔
614
        return m;
1✔
615
    }
616

617

618
    private boolean shouldPropagateOutwards(JTypeMirror resultType, MethodCallSite target, InferenceContext inferenceContext) {
619
        return !isPreJava8
1✔
620
            && !target.getOuterCtx().isEmpty()  //enclosing context is a generic method
1✔
621
            && !inferenceContext.isGround(resultType)   //return type contains inference vars
1✔
622
            && !(resultType instanceof InferenceVar    //no eager instantiation is required (as per 18.5.2)
623
            && needsEagerInstantiation((InferenceVar) resultType, target.getExpectedType(), inferenceContext));
1✔
624
    }
625

626
    /**
627
     * Add more constraints on the inference vars based on the expected
628
     * return type at the call site. This is described in
629
     *
630
     * https://docs.oracle.com/javase/specs/jls/se9/html/jls-18.html#jls-18.5.2.1
631
     *
632
     * under "Let B3 be the bound set derived from B2 as follows."
633
     *
634
     * <p>This binds the ivars of this context to those of the outer context.
635
     */
636
    private JTypeMirror addReturnConstraints(InferenceContext infCtx, JMethodSig m, MethodCallSite site) {
637

638
        /*
639
            Remember: calling stuff like isConvertible or isSubtype
640
            adds constraints on the type variables that are found there.
641
         */
642

643
        JTypeMirror resultType = m.getReturnType();
1✔
644
        if (site.needsUncheckedConversion()) {
1✔
645
            // if unchecked conversion is necessary, the result type,
646
            // and all thrown exception types, are erased.
647
            resultType = resultType.getErasure();
1✔
648
        }
649
        resultType = infCtx.mapToIVars(resultType);
1✔
650
        InferenceContext outerInfCtx = site.getOuterCtx();
1✔
651

652
        if (!infCtx.isGround(resultType) && !outerInfCtx.isEmpty() && resultType instanceof JClassType) {
1✔
653
            JClassType resClass = capture((JClassType) resultType);
1✔
654
            resultType = resClass;
1✔
655

656
            for (JTypeMirror targ : resClass.getTypeArgs()) {
1✔
657
                if (targ instanceof JTypeVar && ((JTypeVar) targ).isCaptured()) {
1✔
658
                    infCtx.addVar((JTypeVar) targ);
1✔
659
                }
660
            }
1✔
661
            resultType = infCtx.mapToIVars(resultType);
1✔
662
        }
663

664
        JTypeMirror actualRes = site.getExpectedType();
1✔
665
        if (actualRes == null) {
1✔
666
            actualRes = ts.OBJECT;
1✔
667
        }
668

669
        if (resultType instanceof InferenceVar) {
1✔
670
            InferenceVar retVar = (InferenceVar) resultType;
1✔
671
            if (needsEagerInstantiation(retVar, actualRes, infCtx)) {
1✔
672
                infCtx.solve(retVar);
×
673
                infCtx.callListeners();
×
674
                if (isConvertible(retVar.getInst(), actualRes, true).never()) {
×
675
                    actualRes = ts.OBJECT;
×
676
                }
677
            } else if (actualRes.isPrimitive()) {
1✔
678
                actualRes = actualRes.box();
1✔
679
            }
680
        }
681

682
        if (isConvertible(resultType, outerInfCtx.mapToIVars(actualRes), true).never()) {
1✔
683
            throw ResolutionFailedException.incompatibleReturn(LOG, site.getExpr(), resultType, actualRes);
1✔
684
        }
685

686
        return resultType;
1✔
687
    }
688

689

690
    /**
691
     * Returns true if the inference var needs to be instantiated eagerly,
692
     * as described in JLS§18.5.2.1. (Poly Method Invocation Compatibility)
693
     *
694
     * https://docs.oracle.com/javase/specs/jls/se9/html/jls-18.html#jls-18.5.2.1
695
     *
696
     * @param alpha  Inference var
697
     * @param t      Target type of the invocation
698
     * @param infCtx Inference context
699
     */
700
    private boolean needsEagerInstantiation(InferenceVar alpha, JTypeMirror t, InferenceContext infCtx) {
701
        if (t == null) {
1✔
702
            return false;
×
703
        }
704

705
        if (t.isPrimitive()) {
1✔
706
            // T is a primitive type, and one of the primitive wrapper classes is an instantiation,
707
            // upper bound, or lower bound for alpha in B2.
708

709
            for (JTypeMirror b : alpha.getBounds(BoundKind.ALL)) {
1✔
710
                if (b.isBoxedPrimitive()) {
1✔
711
                    return true;
×
712
                }
713
            }
1✔
714
            return false;
1✔
715
        }
716

717
        // T is a reference type, but is not a wildcard-parameterized type, and either
718

719
        if (!t.isPrimitive() && !isWilcardParameterized(t)) {
1✔
720
            // i) B2 contains a bound of one of the forms alpha = S or S <: alpha,
721
            //    where S is a wildcard-parameterized type, or
722
            for (JTypeMirror s : alpha.getBounds(BoundKind.EQ_LOWER)) {
1✔
723
                if (isWilcardParameterized(s)) {
1✔
724
                    return true;
×
725
                }
726
            }
1✔
727

728
            // ii) B2 contains two bounds of the forms S1 <: alpha and S2 <: alpha,
729
            // where S1 and S2 have supertypes that are two different
730
            // parameterizations of the same generic class or interface.
731

732
            for (JTypeMirror aLowerBound : alpha.getBounds(BoundKind.LOWER)) {
1✔
733
                for (JTypeMirror anotherLowerBound : alpha.getBounds(BoundKind.LOWER)) {
1✔
734
                    if (aLowerBound != anotherLowerBound // NOPMD CompareObjectsWithEquals
1✔
735
                        && infCtx.isGround(aLowerBound)
×
736
                        && infCtx.isGround(anotherLowerBound)
×
737
                        && commonSuperWithDiffParameterization(aLowerBound, anotherLowerBound)) {
×
738
                        return true;
×
739
                    }
740
                }
1✔
741
            }
1✔
742
        }
743

744
        // T is a parameterization of a generic class or interface, G,
745
        // and B2 contains a bound of one of the forms alpha = S or S <: alpha,
746
        // where there exists no type of the form G<...> that is a
747
        // supertype of S, but the raw type G is a supertype of S
748

749
        if (t.isParameterizedType()) {
1✔
750
            for (JTypeMirror b : alpha.getBounds(BoundKind.EQ_LOWER)) {
1✔
751
                JTypeMirror sup = b.getAsSuper(((JClassType) t).getSymbol());
1✔
752
                if (sup != null && sup.isRaw()) {
1✔
753
                    return true;
×
754
                }
755
            }
1✔
756
        }
757
        return false;
1✔
758
    }
759

760
    private boolean commonSuperWithDiffParameterization(JTypeMirror t, JTypeMirror s) {
761
        JTypeMirror lubResult = ts.lub(listOf(t, s));
×
762
        if (lubResult.isBottom() || lubResult.isTop()) {
×
763
            return false;
×
764
        }
765
        for (JTypeMirror sup : asList(lubResult)) {
×
766
            if (sup.isParameterizedType()) {
×
767
                JClassSymbol sym = ((JClassType) sup).getSymbol();
×
768
                JTypeMirror asSuperOfT = t.getAsSuper(sym);
×
769
                JTypeMirror asSuperOfS = s.getAsSuper(sym);
×
770
                if (!asSuperOfS.equals(asSuperOfT)) {
×
771
                    return true;
×
772
                }
773
            }
774
        }
×
775
        return false;
×
776
    }
777

778
    /**
779
     * Generate bounds on the ivars based on the expected/actual types
780
     * of the arguments to the call. This is described in
781
     *
782
     * https://docs.oracle.com/javase/specs/jls/se9/html/jls-18.html#jls-18.5.1
783
     *
784
     * as being the set C.
785
     *
786
     * <p>For invocation applicability inference (phases {@link MethodResolutionPhase#STRICT STRICT}
787
     * through {@link MethodResolutionPhase#VARARGS VARARGS}), only arguments
788
     * that are {@linkplain ExprOps#isPertinentToApplicability(ExprMirror, JMethodSig, JTypeMirror, InvocationMirror)
789
     * pertinent to applicability}
790
     * are considered. Arguments like lambdas do not influence the applicability
791
     * check beyond checking their basic 'shape' (number of params)
792
     * to check that the method is {@linkplain #isPotentiallyApplicable(JMethodSig, InvocationMirror) potentially
793
     * applicable}, which is done very much earlier.
794
     * So they don't add constraints during those first phases.
795
     *
796
     * <p>When we have found an applicable method and are instantiating it
797
     * (phases {@link MethodResolutionPhase#INVOC_STRICT INVOC_STRICT} through {@link
798
     * MethodResolutionPhase#INVOC_VARARGS INVOC_VARARGS}),
799
     * all arguments are considered so as to yield sharper bounds.
800
     *
801
     * @param infCtx Inference context
802
     * @param m      Tested method
803
     * @param site   Invocation expression
804
     * @param phase  Phase (determines what constraints are allowed)
805
     */
806
    private void addArgsConstraints(InferenceContext infCtx, JMethodSig m, MethodCallSite site, MethodResolutionPhase phase) {
807
        LOG.startArgsChecks();
1✔
808

809
        InvocationMirror expr = site.getExpr();
1✔
810

811
        boolean varargsRequired = phase.requiresVarargs();
1✔
812

813
        if (!varargsRequired && m.getArity() != expr.getArgumentCount()) {
1✔
814
            throw ResolutionFailedException.incompatibleArity(LOG, expr.getArgumentCount(), m.getArity(), expr);
1✔
815
        }
816

817
        List<JTypeMirror> fs = m.getFormalParameters();
1✔
818

819
        @Nullable
820
        JArrayType varargsParam = varargsRequired && m.isVarargs() ? (JArrayType) fs.get(fs.size() - 1) : null;
1✔
821
        int lastP = varargsParam == null ? fs.size() : fs.size() - 1;
1✔
822

823
        List<ExprMirror> args = expr.getArgumentExpressions();
1✔
824

825
        for (int i = 0; i < lastP; i++) {
1✔
826
            ExprMirror ei = args.get(i);
1✔
827

828

829
            if (phase.isInvocation() || isPertinentToApplicability(ei, m, fs.get(i), expr)) {
1✔
830
                JTypeMirror stdType = ei.getStandaloneType();
1✔
831
                JTypeMirror fi = infCtx.mapToIVars(fs.get(i));
1✔
832

833
                LOG.startArg(i, ei, fi);
1✔
834

835
                if (!phase.canBox()) {
1✔
836
                    // these are cases where applicability is impossible (in strict ctx)
837
                    if (stdType != null && stdType.isPrimitive() != fi.isPrimitive() && stdType != ts.UNKNOWN) {
1✔
838
                        throw ResolutionFailedException.incompatibleFormal(LOG, ei, stdType, fi);
1✔
839
                    }
840
                }
841

842
                addBoundOrDefer(site, infCtx, phase, ei, fi);
1✔
843

844
                LOG.endArg();
1✔
845
            } else {
1✔
846
                // then the final reinvocation is necessary
847
                site.maySkipInvocation(false);
1✔
848
                LOG.skipArgAsNonPertinent(i, ei);
1✔
849
            }
850
        }
851

852
        if (varargsRequired && varargsParam != null) {
1✔
853
            JTypeMirror varargsComponent = infCtx.mapToIVars(varargsParam.getComponentType());
1✔
854

855
            // possibly some varargs arguments left
856
            for (int i = lastP; i < args.size(); i++) {
1✔
857
                ExprMirror ei = args.get(i);
1✔
858

859
                if (phase.isInvocation() || isPertinentToApplicability(ei, m, varargsComponent, expr)) {
1✔
860
                    LOG.startArg(i, ei, varargsComponent);
1✔
861
                    addBoundOrDefer(site, infCtx, phase, ei, varargsComponent);
1✔
862
                    LOG.endArg();
1✔
863
                } else {
864
                    site.maySkipInvocation(false);
×
865
                    LOG.skipArgAsNonPertinent(i, ei);
×
866
                }
867
            }
868
        }
869
        LOG.endArgsChecks();
1✔
870
    }
1✔
871

872
    /**
873
     * This corresponds to the attribution of expression compatibility
874
     * constraints in https://docs.oracle.com/javase/specs/jls/se9/html/jls-18.html#jls-18.2.1
875
     * although it's not implemented as described.
876
     *
877
     * See {@link ExprCheckHelper#isCompatible(JTypeMirror, ExprMirror)}.
878
     */
879
    private void addBoundOrDefer(@Nullable MethodCallSite site, InferenceContext infCtx, MethodResolutionPhase phase, @NonNull ExprMirror arg, @NonNull JTypeMirror formalType) {
880
        ExprChecker exprChecker =
1✔
881
            (ctx, exprType, formalType1) -> checkConvertibleOrDefer(ctx, exprType, formalType1, arg, phase, site);
1✔
882

883
        ExprCheckHelper helper = new ExprCheckHelper(infCtx, phase, exprChecker, site, this);
1✔
884
        if (!helper.isCompatible(formalType, arg)) {
1✔
885
            throw ResolutionFailedException.incompatibleFormalExprNoReason(LOG, arg, formalType);
1✔
886
        }
887
    }
1✔
888

889
    /**
890
     * Add a compatibility constraint between an exprType and a formalType.
891
     * This asserts {@code exprType <: formalType}, the arg parameter is only
892
     * used for reporting.
893
     *
894
     * <p>This method is called back to by {@link ExprCheckHelper#isCompatible(JTypeMirror, ExprMirror)}.
895
     */
896
    void checkConvertibleOrDefer(InferenceContext infCtx, JTypeMirror exprType, JTypeMirror formalType, ExprMirror arg, MethodResolutionPhase phase, @Nullable MethodCallSite site) {
897
        if (!infCtx.isGround(formalType) || !infCtx.isGround(exprType)) {
1✔
898
            // defer the check
899
            infCtx.addInstantiationListener(setOf(formalType, exprType), solvedCtx -> checkConvertibleOrDefer(solvedCtx, exprType, formalType, arg, phase, site));
1✔
900
        }
901

902
        JTypeMirror groundE = infCtx.ground(exprType);
1✔
903
        JTypeMirror groundF = infCtx.ground(formalType);
1✔
904

905
        // This method call does all the work of adding constraints
906
        // If groundE or groundF are in fact not ground, then constraints
907
        // on the ivars that appear within them are implicitly added during
908
        // the subtyping check. The call then returns true and we return
909
        // normally
910

911
        // If they are ground, then they must conform to each other else
912
        // the exception stops the resolution process.
913
        Convertibility isConvertible = isConvertible(groundE, groundF, phase.canBox());
1✔
914
        if (isConvertible.never()) {
1✔
915
            throw ResolutionFailedException.incompatibleFormal(LOG, arg, groundE, groundF);
1✔
916
        } else if (isConvertible.withUncheckedWarning() && site != null) {
1✔
917
            site.setNeedsUncheckedConversion();
1✔
918
        }
919
    }
1✔
920

921
    /**
922
     * Convertibility in *invocation* context.
923
     *
924
     * https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.3
925
     */
926
    static Convertibility isConvertible(JTypeMirror exprType, JTypeMirror formalType, boolean canBox) {
927
        if (exprType == formalType) { // NOPMD CompareObjectsWithEquals
1✔
928
            // fast path
929
            return Convertibility.SUBTYPING;
1✔
930
        }
931

932
        if (canBox && exprType.isPrimitive() ^ formalType.isPrimitive()) {
1✔
933
            // then boxing conversions may be useful
934
            Convertibility result = TypeOps.isConvertible(exprType.box(), formalType.box());
1✔
935
            if (!result.never()) {
1✔
936
                return result;
1✔
937
            } else {
938
                return TypeOps.isConvertible(exprType.unbox(), formalType.unbox());
1✔
939
            }
940
        }
941

942
        return TypeOps.isConvertible(exprType, formalType);
1✔
943
    }
944

945
    /**
946
     * Returns true if the method is potentially applicable to the invocation
947
     * expression expr, as specified in JLS§15.12.2.1.
948
     *
949
     * https://docs.oracle.com/javase/specs/jls/se9/html/jls-15.html#jls-15.12.2.1
950
     *
951
     * <p>This assumes the name of the method matches the expression, and
952
     * the method is accessible.
953
     *
954
     * @param m    Method to test
955
     * @param expr Invocation expression
956
     */
957
    private boolean isPotentiallyApplicable(JMethodSig m, InvocationMirror expr) {
958

959
        if (m.isGeneric()
1✔
960
            && !expr.getExplicitTypeArguments().isEmpty()
1✔
961
            && expr.getExplicitTypeArguments().size() != m.getTypeParameters().size()) {
1✔
962
            return false;
×
963
        }
964

965
        List<ExprMirror> args = expr.getArgumentExpressions();
1✔
966

967
        if (!m.isVarargs()) {
1✔
968
            // we can avoid computing formal parameters by using getArity here
969
            if (args.size() != m.getArity()) {
1✔
970
                return false;
1✔
971
            }
972

973
            List<JTypeMirror> fs = m.getFormalParameters();
1✔
974
            for (int i = 0; i < args.size(); i++) {
1✔
975
                if (!exprOps.isPotentiallyCompatible(m, args.get(i), fs.get(i))) {
1✔
976
                    return false;
1✔
977
                }
978
            }
979

980
        } else {
1✔
981
            List<JTypeMirror> fs = m.getFormalParameters();
1✔
982

983
            // test first n-1 params
984
            int varargIdx = fs.size() - 1;
1✔
985
            for (int i = 0; i < varargIdx; i++) {
1✔
986
                if (i >= args.size()) {
1✔
987
                    // not enough arguments
988
                    return false;
1✔
989
                }
990

991
                if (!exprOps.isPotentiallyCompatible(m, args.get(i), fs.get(i))) {
1✔
992
                    return false;
1✔
993
                }
994
            }
995

996
            if (args.size() == varargIdx - 1) {
1✔
997
                return true;
×
998
            }
999

1000
            if (args.size() == fs.size()) {
1✔
1001
                ExprMirror last = args.get(varargIdx);
1✔
1002
                JArrayType t = (JArrayType) fs.get(varargIdx);
1✔
1003

1004
                return exprOps.isPotentiallyCompatible(m, last, t)
1✔
1005
                    || exprOps.isPotentiallyCompatible(m, last, t.getComponentType());
1✔
1006
            }
1007

1008
            if (args.size() > fs.size()) {
1✔
1009
                JTypeMirror t = ((JArrayType) fs.get(varargIdx)).getComponentType();
1✔
1010
                for (int i = varargIdx; i < args.size(); i++) {
1✔
1011
                    if (!exprOps.isPotentiallyCompatible(m, args.get(i), t)) {
1✔
1012
                        return false;
×
1013
                    }
1014
                }
1015
            }
1016
        }
1017

1018
        return true;
1✔
1019
    }
1020

1021

1022
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc