Skip to content

Preserve generics on static member variables translated to class properties. - #2815

Open
copybara-service[bot] wants to merge 1 commit into
masterfrom
test_952947498
Open

Preserve generics on static member variables translated to class properties.#2815
copybara-service[bot] wants to merge 1 commit into
masterfrom
test_952947498

Conversation

@copybara-service

@copybara-service copybara-service Bot commented Jul 23, 2026

Copy link
Copy Markdown

Preserve generics on static member variables translated to class properties.

J2ObjC was not preserving generic type information when translating static member variables to Objective-C class properties. This change updates PropertyGenerator to use the generic type declaration if generics are enabled.

Also updates HeaderImportCollector to collect generic parameters for fields, ensuring they are forward declared or imported in the generated headers when generics are enabled.
Generic parameters that are JRE types (e.g. java.lang.Boolean) are explicitly imported (#include) in the header instead of forward declared (@Class) to avoid Swift compiler errors (incomplete interface) when importing properties using these generic parameters.

To prevent circular dependency cycles when compiling the JRE emulation library (jre_emul) itself, HeaderImportCollector checks if the current compilation unit belongs to a core JRE package (java., and selected javax., android., org. packages that are part of jre_emul). If so, it bypasses the JRE force-import logic and falls back to default forward declarations for all generic parameters.
This core JRE check specifically excludes packages like javax.inject (jsr330) which are not part of jre_emul, preventing dependency violations in modular builds where headers would otherwise try to directly import javax/inject/Provider.h without the target declaring a direct dependency on the jsr330 module.

To resolve Objective-C compilation errors (incompatible pointer types) when passing generic arguments to generic methods (which are erased to bounds in ObjC, e.g. DXLTextProtocolNestedNestedModelType<id> * expected but DXLTextProtocolNestedNestedModelType<DXLVotingModelVotingChip *> * provided), this change makes all Objective-C lightweight generics covariant by prepending __covariant to generic type parameters in generated class declarations and extensions (TypeDeclarationGenerator and TypePrivateDeclarationGenerator).
This matches Java's type safety checks which were already performed by javac, and allows implicit conversions (e.g., from Generic<SubClass *> * to Generic<id> * or Generic *) in ObjC that would otherwise be rejected by strict invariant pointer checks.

To resolve similar pointer mismatch errors when assigning erased generic method returns (e.g. SettingKey returned by generic factory method) to specific generic static fields (e.g. SettingKey), this change updates CastResolver to perform a stricter assignability check that considers ObjC generic type parameters when options.asObjCGenericDecl() is enabled.
If the generic parameters do not match (taking covariance into account), CastResolver will now correctly insert a cast in the ObjC translation (which is generated as a raw class pointer cast, e.g. (SettingKey *), sufficient to satisfy the ObjC compiler).

Note on C function signatures: J2ObjC generated C functions (representing static methods and constructors) must remain erased (no ObjC generics) in their signatures. This is because C functions are global and do not have access to class-level or method-level generic type variables, and trying to use them (e.g. JavaLangEnum<E> *) would trigger ObjC compiler errors. Furthermore, enabling generics for C functions causes Swift compilation errors (generic argument mismatch) when passing these typed J2ObjC generics to native ObjC APIs that expect raw types (imported as <AnyObject> in Swift). Thus, C function signatures are kept raw, and tests are added to verify this erasure behavior.

Also updates TypeGenerator, TypeDeclarationGenerator, and TypeImplementationGenerator to support generic types in C-style accessors and global variable declarations for static fields (the default translation when class properties are disabled).

Generics support in TypeGenerator.getDeclarationType is restricted to static fields only, to avoid generating generic types for instance fields which can trigger ObjC compiler errors (incompatible pointer types) on generic method return assignments due to J2ObjC missing casts for ObjC generics.

To support generic types with multiple parameters (which contain commas) in C preprocessor macros (like J2OBJC_STATIC_FIELD_OBJ), TypeDeclarationGenerator now generates a typedef for these types in the header and uses the typedef name in the macro call and accessor declarations, avoiding macro argument parsing errors.

Adds test cases for:

  • Automatic class properties (setClassProperties=true).
  • Automatic class properties with @GenerateObjectiveCGenerics.
  • Explicit class properties via @Property.
  • Default static field translation (both options false) to verify C-style accessors and global variables are used, and they correctly preserve generics when enabled.
  • Default static field translation with multiple generic parameters (containing commas) to verify the typedef workaround.
  • HeaderImportCollector JRE generic parameters import behavior.
  • CastResolver generic method assignment cast behavior.
  • C function signatures for static methods being erased (no generics).
  • C function signatures for constructors being erased (no generics).

Updates existing GenerateObjectiveCGenericsTest cases to expect __covariant in generated class declarations and extensions.

@copybara-service
copybara-service Bot force-pushed the test_952947498 branch 14 times, most recently from 441ab7c to aff4f00 Compare July 24, 2026 07:25
…erties.

J2ObjC was not preserving generic type information when translating static member variables to Objective-C class properties. This change updates PropertyGenerator to use the generic type declaration if generics are enabled.

Also updates HeaderImportCollector to collect generic parameters for fields, ensuring they are forward declared or imported in the generated headers when generics are enabled.
Generic parameters that are JRE types (e.g. java.lang.Boolean) are explicitly imported (#include) in the header instead of forward declared (@Class) to avoid Swift compiler errors (incomplete interface) when importing properties using these generic parameters.

To prevent circular dependency cycles when compiling the JRE emulation library (jre_emul) itself, HeaderImportCollector checks if the current compilation unit belongs to a core JRE package (java.*, and selected javax.*, android.*, org.* packages that are part of jre_emul). If so, it bypasses the JRE force-import logic and falls back to default forward declarations for all generic parameters.
This core JRE check specifically excludes packages like javax.inject (jsr330) which are not part of jre_emul, preventing dependency violations in modular builds where headers would otherwise try to directly import javax/inject/Provider.h without the target declaring a direct dependency on the jsr330 module.

To resolve Objective-C compilation errors (incompatible pointer types) when passing generic arguments to generic methods (which are erased to bounds in ObjC, e.g. DXLTextProtocolNestedNestedModelType<id<DXLModelModel>> * expected but DXLTextProtocolNestedNestedModelType<DXLVotingModelVotingChip *> * provided), this change makes all Objective-C lightweight generics covariant by prepending `__covariant` to generic type parameters in generated class declarations and extensions (TypeDeclarationGenerator and TypePrivateDeclarationGenerator).
This matches Java's type safety checks which were already performed by javac, and allows implicit conversions (e.g., from Generic<SubClass *> * to Generic<id<Protocol>> * or Generic<id> *) in ObjC that would otherwise be rejected by strict invariant pointer checks.

To resolve similar pointer mismatch errors when assigning erased generic method returns (e.g. SettingKey<MessageLite> returned by generic factory method) to specific generic static fields (e.g. SettingKey<ActiveExperimentIds>), this change updates CastResolver to perform a stricter assignability check that considers ObjC generic type parameters when options.asObjCGenericDecl() is enabled.
If the generic parameters do not match (taking covariance into account), CastResolver will now correctly insert a cast in the ObjC translation (which is generated as a raw class pointer cast, e.g. `(SettingKey *)`, sufficient to satisfy the ObjC compiler).

Note on C function signatures: J2ObjC generated C functions (representing static methods and constructors) must remain erased (no ObjC generics) in their signatures. This is because C functions are global and do not have access to class-level or method-level generic type variables, and trying to use them (e.g. `JavaLangEnum<E> *`) would trigger ObjC compiler errors. Furthermore, enabling generics for C functions causes Swift compilation errors (generic argument mismatch) when passing these typed J2ObjC generics to native ObjC APIs that expect raw types (imported as `<AnyObject>` in Swift). Thus, C function signatures are kept raw, and tests are added to verify this erasure behavior.

Also updates TypeGenerator, TypeDeclarationGenerator, and TypeImplementationGenerator to support generic types in C-style accessors and global variable declarations for static fields (the default translation when class properties are disabled).

Generics support in TypeGenerator.getDeclarationType is restricted to static fields only, to avoid generating generic types for instance fields which can trigger ObjC compiler errors (incompatible pointer types) on generic method return assignments due to J2ObjC missing casts for ObjC generics.

To support generic types with multiple parameters (which contain commas) in C preprocessor macros (like J2OBJC_STATIC_FIELD_OBJ), TypeDeclarationGenerator now generates a typedef for these types in the header and uses the typedef name in the macro call and accessor declarations, avoiding macro argument parsing errors.

Adds test cases for:
- Automatic class properties (setClassProperties=true).
- Automatic class properties with @GenerateObjectiveCGenerics.
- Explicit class properties via @Property.
- Default static field translation (both options false) to verify C-style accessors and global variables are used, and they correctly preserve generics when enabled.
- Default static field translation with multiple generic parameters (containing commas) to verify the typedef workaround.
- HeaderImportCollector JRE generic parameters import behavior.
- CastResolver generic method assignment cast behavior.
- C function signatures for static methods being erased (no generics).
- C function signatures for constructors being erased (no generics).

Updates existing GenerateObjectiveCGenericsTest cases to expect `__covariant` in generated class declarations and extensions.

PiperOrigin-RevId: 952947498
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant