@CompileStatic automatic type conversion failure - Gradle 2.8 Regression

This is a regression between Gradle 2.7 and 2.8 that caused the J2ObjC Gradle Plugin to fail. The plugin liberally uses @CompileStatic to enforce compile time checks. In this case, it appears that @CompileStatic is allowing code to fail at runtime that should have failed at compile time. The failure occurs with this line:

String getPodMethodName() {
    return "j2objc_$projectName".replaceAll(/[^a-zA-Z0-9]/, '_')
}

The error was reported as:

Caused by: java.lang.NoSuchMethodError:
org.codehaus.groovy.runtime.StringGroovyMethods.replaceAll(
    Ljava/lang/CharSequence;
    Ljava/lang/CharSequence;
    Ljava/lang/CharSequence;)
    Ljava/lang/CharSequence;

Note that replaceAll is a String method which StringGroovyMethods doesn’t have. If you remove @CompileStatic from the class, then it runs just fine, presumably doing automatic type conversion from GroovyString to regular String. When you add @CompileStatic and make the type conversion explicit by adding toString() then it also runs fine…

return "j2objc_$projectName".toString().replaceAll(/[^a-zA-Z0-9]/, '_')

So the issue is with @CompileStatic, why didn’t fail at compile time when toString() was missing? That’s the crux of this issue. I don’t have good insights beyond this. Nonetheless, since it’s a regression between Gradle 2.7 and 2.8, it deserves to be better understood.

Original bug report from plugin:

My guess is that this is due to the upgrade to Groovy 2.4.4 in Gradle 2.8: https://docs.gradle.org/current/release-notes#upgraded-to-groovy-2.4.4

Hopefully @CedricChampeau can give a bit more insight.

Looks like the missing method

was changed to this (return type changed to String)

for https://issues.apache.org/jira/browse/GROOVY-7071

I don’t see anything easy we can change on our side.

daz, sterling - thanks for your insights. I think it’s appropriate to let this one go and not to do anything about it.

If I compile the plugin with Gradle 2.8 and then run a build using the plugin with 2.8, then it all works fine. So it’s only when you compile the plugin with 2.4 and then do the build with 2.8 do you have an issue. After adding the toString(), then the build works fine with 2.4 and 2.8.

I think that’s good enough.

Hi Bruno,

I made some researches, and it’s a binary incompatibility that was introduced, ironically, introduced in Groovy 2.3.6 and 2.4.0 to improve @CompileStatic support: https://github.com/apache/incubator-groovy/commit/49bf44f95f878850e15433e47e334794eec99d14

To be clear, it’s something that should not have been done in Groovy. The irony is that it’s only because we wanted to improve static compilation that this happens: sticking with dynamic Groovy, there wouldn’t have been an issue! I will send an email to the Groovy dev list to warn about such changes. We already have a task in the Groovy build that checks for binary incompatibilities, not enforced yet because the tool we use is not 100% reliable.

Thanks for letting us know.

Thanks Cedric and to everyone else that looked in to this. I’m glad that it
was only 1 line out of 16,500 that broke!

Might be worth giving a heads up to any of your customers that heavily
depend on plugins, e.g. NetFlix or LInkedIn.