Uploaded image for project: 'Swift'
  1. Swift
  2. SR-695

Loophole in Self-requirement leads to crash

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Resolved
    • Resolution: Done
    • Component/s: Compiler
    • Labels:
      None
    • Environment:

      Swift version 2.2-dev (LLVM 3ebdbb2c7e, Clang f66c5bb67b, Swift 42591f7cba)
      Apple Swift version 2.1.1 (swiftlang-700.1.101.15 clang-700.1.81)

      Description

      A StackOverflow question pointed out that there is a problem with Self-requirement in protocols that can lead to wrong results or crashes. Consider this example from the question:

      // The protocol with Self requirement
      protocol Narcissistic {
          func getFriend() -> Self
      }
      
      // Base class that adopts the protocol
      class Mario : Narcissistic  {
          func getFriend() -> Self {
              print("Mario.getFriend()")
              return self;
          }
      }
      
      // Intermediate class that eliminates the
      // Self requirement by specifying an explicit type
      // (Why does the compiler allow this?)
      class SuperMario : Mario {
          override func getFriend() -> SuperMario {
              print("SuperMario.getFriend()")
              return SuperMario();
          }
      }
      
      // Most specific class that defines a field whose
      // (polymorphic) access will cause the world to explode
      class FireFlowerMario : SuperMario {
          let fireballCount = 42
      
          func throwFireballs() {
              print("Throwing " + String(fireballCount) + " fireballs!")
          }
      }
      
      // Global generic function restricted to the protocol
      func queryFriend<T : Narcissistic>(narcissistic: T) -> T {
          return narcissistic.getFriend()
      }
      
      // Sample client code
      
      // Instantiate the most specific class
      let m = FireFlowerMario()
      
      // The call to the generic function is verified to return
      // the same type that went in -- 'FireFlowerMario' in this case.
      // But in reality, the method returns a 'SuperMario' and the
      // call to 'throwFireballs' will cause arbitrary
      // things to happen at runtime.
      queryFriend(m).throwFireballs()
      

      The problem here is that SuperMario.getFriend always returns a new instance of SuperMario which is correct at that point.

      But when FireFlowerMario derived from SuperMario it inherited the implementation of getFriend that returns a SuperMario instance! The compiler treats the instance as a FireFlowerMario instead and tries to access the nonexistent fireballCount. This either leads to garbage output or crashes.

      The compiler should be able to detect that SuperMario.getFriend does not behave as expected when subclassing and issue a warning about this.

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                greg Greg Titus
                Reporter:
                DarkDust Marc Haisenko
              • Votes:
                1 Vote for this issue
                Watchers:
                5 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved: