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

Generated 'from' method has trouble with parameterized interfaces that declare properties #1506

Open
stevenschlansker opened this issue Mar 18, 2024 · 0 comments

Comments

@stevenschlansker
Copy link
Contributor

stevenschlansker commented Mar 18, 2024

Hi, we use Immutables and love it, but recently ran into what feels like a bug.
We declare some properties on mixin interfaces so they can be shared among related types, and use from(otherImpl) liberally to copy all relevant properties between the different sub-types. In some cases, we parameterize the types Mixin<Self extends Mixin<Self>> in order to give proper return types to Self withProperty() methods.

However, this seems to break the from() method in some cases. Consider:

interface LastModified<Self extends LastModified<Self>> {
    Instant lastModified();
    Self withLastModified(Instant lastModified);
}

interface Property {
    @Nullable
    String property();
}

@Value.Immutable
interface ImplA extends Property, LastModified<ImplA> {}

@Value.Immutable
interface ImplB extends Property, LastModified<ImplB> {}

public class TestImmutableFrom {
    @Test
    public void testImmutableFrom() {
        final ImmutableImplA a = ImmutableImplA.builder()
                .lastModified(Instant.EPOCH)
                .build();
        ImmutableImplB.builder()
                .from(a)
                .build();
    }
}

Expected:
Since both ImplA and ImplB extend LastModified, and while the parameterization of the type is different, it does not affect the property in any way, I would expect the from method to copy the property.

Actual:
The generated ImmutableImplBBuilder.from method unfortunately seems to not be correct.

private void from(short _unused, Object object) {
  @Var long bits = 0;
  if (object instanceof Property) {
    Property instance = (Property) object;
    if ((bits & 0x1L) == 0) {
      @Nullable String propertyValue = instance.property();
      if (propertyValue != null) {
        property(propertyValue);
      }
      bits |= 0x1L;
    }
  }
  if (object instanceof ImplB) {
    ImplB instance = (ImplB) object;
    if ((bits & 0x1L) == 0) {
      @Nullable String propertyValue = instance.property();
      if (propertyValue != null) {
        property(propertyValue);
      }
      bits |= 0x1L;
    }
    lastModified(instance.lastModified());
  }
}

Since ImplB already implements Property, the two instanceof clauses are redundant. The second check should probably check for object instanceof LastModified.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant