Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unwrapping nested generics yields incorrect result #5290

Closed
muglug opened this issue Feb 26, 2021 · 1 comment
Closed

Unwrapping nested generics yields incorrect result #5290

muglug opened this issue Feb 26, 2021 · 1 comment
Labels

Comments

@muglug
Copy link
Collaborator

muglug commented Feb 26, 2021

In the example below this works:

/**
 * @template TInnerPackage of InnerPackage
 * @template TGenericPackage of GenericPackage<TInnerPackage>
 * @param  class-string<TGenericPackage> $class  FQCN to be instantiated
 * @return TInnerPackage
 */
function loadWithDirectUnwrap(string $class) {
    $package = new $class();
    return $package->unwrap();
}

but the indirect unwrapping fails to produce the templated inner class

/**
 * @template TInnerPackage of InnerPackage
 * @template TGenericPackage of GenericPackage<TInnerPackage>
 * @param  class-string<TGenericPackage> $class  FQCN to be instantiated
 * @return TInnerPackage
 */
function loadWithIndirectUnwrap(string $class) {
    $package = new $class();
    return unwrapGeneric($package);
}

https://psalm.dev/r/abe64b4671

@psalm-github-bot
Copy link

psalm-github-bot bot commented Feb 26, 2021

I found these snippets:

https://psalm.dev/r/abe64b4671
<?php

interface BasePackage {}

interface InnerPackage extends BasePackage {}

/**
 * @template TInnerPackage of InnerPackage
 */
interface GenericPackage extends BasePackage {
    /** @return TInnerPackage */
    public function unwrap() : InnerPackage;
}

interface SomeInnerPackage extends InnerPackage {}

/**
 * @extends GenericPackage<SomeInnerPackage>
 */
interface SomePackage extends GenericPackage {}

/**
 * @template TInnerPackage of InnerPackage
 * @template TGenericPackage of GenericPackage<TInnerPackage>
 * @param TGenericPackage $package
 * @return TInnerPackage
 */
function unwrapGeneric(GenericPackage $package) {
    return $package->unwrap();
}

/**
 * @template TInnerPackage of InnerPackage
 * @template TGenericPackage of GenericPackage<TInnerPackage>
 * @param  class-string<TGenericPackage> $class  FQCN to be instantiated
 * @return TInnerPackage
 */
function loadWithDirectUnwrap(string $class) {
    $package = new $class();
    return $package->unwrap();
}

/**
 * @template TInnerPackage of InnerPackage
 * @template TGenericPackage of GenericPackage<TInnerPackage>
 * @param  class-string<TGenericPackage> $class  FQCN to be instantiated
 * @return TInnerPackage
 */
function loadWithIndirectUnwrap(string $class) {
    $package = new $class();
    return unwrapGeneric($package);
}

$result = loadWithDirectUnwrap(SomePackage::class);
/** @psalm-trace $result */
// expected: SomePackage|SomeInnerPackage
Psalm output (using commit bca09d7):

ERROR: InvalidReturnStatement - 51:12 - The inferred type 'InnerPackage' does not match the declared return type 'TInnerPackage:fn-loadwithindirectunwrap as InnerPackage' for loadWithIndirectUnwrap

ERROR: InvalidReturnType - 47:12 - The declared return type 'TInnerPackage:fn-loadwithindirectunwrap as InnerPackage' for loadWithIndirectUnwrap is incorrect, got 'InnerPackage'

ERROR: InvalidArgument - 54:32 - Argument 1 of loadWithDirectUnwrap expects class-string<GenericPackage<TInnerPackage>>, SomePackage::class provided

INFO: Trace - 56:42 - $result: InnerPackage

INFO: UnusedVariable - 54:1 - $result is never referenced or the value is not used

@muglug muglug added the bug label Feb 26, 2021
@muglug muglug closed this as completed in 44f8d71 Feb 27, 2021
This was referenced Mar 15, 2021
This was referenced Mar 17, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant