Uploaded image for project: 'Groovy'
  1. Groovy
  2. GROOVY-9902

Generic typecheck in @DelegatesTo doesn't work

    XMLWordPrintableJSON

    Details

      Description

      I found an edge case where @CompileStatic doesn't raise an error when calling a method with incompatible types.

      Example code that reproduces the problem:

       https://groovyconsole.appspot.com/script/5098548152500224

      import groovy.transform.CompileStatic
      
      /**
       * For the bug to be visible this class has to have a generic type
       * Even if in this case the generic seems pointless.
       */
      @CompileStatic
      class Holder<D> {
        TypedProperty<String, D> stringProperty = prop(String)
        TypedProperty<Long, D> longProperty = prop(Long)
      
        def <T> TypedProperty<T, D> prop(Class<T> clazz) {
          return new TypedProperty<T, D>(clazz: clazz)
        }
      
        /**
         * This method is also necessary to trigger the bug.
         * Seems like because of the missing <D> in the @DelegatesTo the typechecker is tripped up?
         * In the original method in our codebase the signature contains <D> as well.
         */
        def <T> T of(@DelegatesTo(value = Holder, strategy = Closure.DELEGATE_FIRST) Closure<T> c) {
          c.delegate = this
          c.resolveStrategy = Closure.DELEGATE_FIRST
          c()
        }
      }
      
      @CompileStatic
      class TypedProperty<T, D> {
        Class<T> clazz
      
        void eq(T t) {
          // The code fails here, expected String but got GString
          // This should have been catched by the typechecker
          // And/Or the typechecker should have automatically converted GString to String
          assert t.class == clazz, "t.class is ${t.class} not ${clazz}"
        }
      }
      
      @CompileStatic
      class Test {
        static void test() {
          Holder<Object> q = new Holder<Object>()
          // Works:
          // Typechecker catches this: Cannot call TypedProperty <String, Object>#eq(java.lang.String) with arguments [groovy.lang.GString]
          // q.stringProperty.eq("${0}")
          
          // Doesn't work because of delegation
          q.of {
            // Typechecker should be able to catch this as well
            // But instead it yields a runtime problem because TypedProperty is called with GString and assert fails
            stringProperty.eq("${0}")
            
            // Doesn't get catched by the typecherk as well - completely different types Long and String
            longProperty.eq("foo")
          }
        }
      }
      
      Test.test()
      

       

        Attachments

          Activity

            People

            • Assignee:
              emilles Eric Milles
              Reporter:
              fesc Felix Scheinost
            • Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Time Tracking

                Estimated:
                Original Estimate - Not Specified
                Not Specified
                Remaining:
                Remaining Estimate - 0h
                0h
                Logged:
                Time Spent - 1h
                1h