X10
  1. X10
  2. XTENLANG-2732

Can subvert the typesystem by using generics

    Details

    • Number of attachments :
      0

      Description

      Our code generation for generics strips the constraints off the types. So, the following code does not behave as expected. This needs to be documented explicitly.

      public class XTENLANG_2732 {
          public static def _instanceof_[T](x:Any):Boolean {
              return x instanceof T;
          }
          public static def _as_[T](x:Any):T {
              return x as T;
          }
          public static class X(p:Int) { }
          public static def main(Array[String]) {
              val t:X = new X(1);
              Console.OUT.println(t instanceof X{self.p==1});
              Console.OUT.println(_instanceof_[X{self.p==1}](t));
              Console.OUT.println(t instanceof X{self.p==2});
              Console.OUT.println(_instanceof_[X{self.p==2}](t));
              try {
                  val r = t as X{self.p==1};
                  Console.OUT.println("Cast succeeded; check="+(r.p==1));
              } catch (ClassCastException) {
                  Console.OUT.println("Cast failed");
              }
              try {
                  val r = _as_[X{self.p==1}](t);
                  Console.OUT.println("Cast succeeded; check="+(r.p==1));
              } catch (ClassCastException) {
                  Console.OUT.println("Cast failed");
              }
              try {
                  val r = t as X{self.p==2};
                  Console.OUT.println("Cast succeeded; check="+(r.p==2));
              } catch (ClassCastException) {
                  Console.OUT.println("Cast failed");
              }
              try {
                  val r = _as_[X{self.p==2}](t);
                  Console.OUT.println("Cast succeeded; check="+(r.p==2));
              } catch (ClassCastException) {
                  Console.OUT.println("Cast failed");
              }
          }
      }
      

      The above prints:

      true
      true
      false
      true
      Cast succeeded; check=true
      Cast succeeded; check=true
      Cast failed
      Cast succeeded; check=false
      

        Activity

        Hide
        Bard Bloom added a comment -

        By "does not behave as expected" you actually mean the considerably more alarming "some variables have the wrong types". With a bit of fiddling on Igor's example, we can get code of the form:

          val x:T = e;
          assert !(x instanceof T);
        

        and that's not a trick about nulls and instanceof, it's a genuine constraint failure:

             val like2: Pea{p==2} = Generic.like[Pea{p==2}](pea);
             assert !(like2 instanceof Pea{p==2});
             assert like2.p == 1;
        

        Here's the full code, phrased to make it more spec-worthy:

        class Generic {
          public static def inst[T](x:Any):Boolean = x instanceof T;
          public static def like[T](x:Any):T       = x as T;
        }
        
        class Pea(p:Int) {}
        
        class Example{
          static def example() {
             val pea : Pea = new Pea(1);
             // These are what you'd expect: 
             assert (pea instanceof Pea{p==1});
             assert (pea as Pea{p==1}).p == 1;
             assert ! (pea instanceof Pea{p==2}); 
             // 'val x = pea as Pea{p==2};' throws a FailedDynamicCheckException.
        
             // But the genericized version don't do the same thing
             assert Generic.inst[Pea{p==1}](pea);
             assert Generic.inst[Pea{p==2}](pea);
             // No exception here
             val like1: Pea{p==1} = Generic.like[Pea{p==1}](pea);
             val like2: Pea{p==2} = Generic.like[Pea{p==2}](pea);
             assert like2.p == 1;
             assert !(like2 instanceof Pea{p==2});
             
          }
        }
        
        public class Stripped {
          public static def main(argv:Array[String](1)) {
            Example.example();
            Console.OUT.println("Done!");
          }
        }
        
        Show
        Bard Bloom added a comment - By "does not behave as expected" you actually mean the considerably more alarming "some variables have the wrong types". With a bit of fiddling on Igor's example, we can get code of the form: val x:T = e; assert !(x instanceof T); and that's not a trick about nulls and instanceof, it's a genuine constraint failure: val like2: Pea{p==2} = Generic.like[Pea{p==2}](pea); assert !(like2 instanceof Pea{p==2}); assert like2.p == 1; Here's the full code, phrased to make it more spec-worthy: class Generic { public static def inst[T](x:Any): Boolean = x instanceof T; public static def like[T](x:Any):T = x as T; } class Pea(p:Int) {} class Example{ static def example() { val pea : Pea = new Pea(1); // These are what you'd expect: assert (pea instanceof Pea{p==1}); assert (pea as Pea{p==1}).p == 1; assert ! (pea instanceof Pea{p==2}); // 'val x = pea as Pea{p==2};' throws a FailedDynamicCheckException. // But the genericized version don't do the same thing assert Generic.inst[Pea{p==1}](pea); assert Generic.inst[Pea{p==2}](pea); // No exception here val like1: Pea{p==1} = Generic.like[Pea{p==1}](pea); val like2: Pea{p==2} = Generic.like[Pea{p==2}](pea); assert like2.p == 1; assert !(like2 instanceof Pea{p==2}); } } public class Stripped { public static def main(argv:Array[ String ](1)) { Example.example(); Console.OUT.println( "Done!" ); } }
        Hide
        Bard Bloom added a comment - - edited

        In the previous examples, the issue seems to be casting to a type variable. We forbid casting to an instantiation of a generic type already. Would it suffice to forbid casting to a type variable as well – and, I guess, a constrained type variable?

        Or (if that's too restrictive) is there some other discipline or pattern which programmers could follow which would guarantee type safety here?

        Show
        Bard Bloom added a comment - - edited In the previous examples, the issue seems to be casting to a type variable. We forbid casting to an instantiation of a generic type already. Would it suffice to forbid casting to a type variable as well – and, I guess, a constrained type variable? Or (if that's too restrictive) is there some other discipline or pattern which programmers could follow which would guarantee type safety here?
        Hide
        Bard Bloom added a comment -

        bard_todo
        If this hole in the type system stays, we have to add it to the spec in the type-unsoundness section.

        Show
        Bard Bloom added a comment - bard_todo If this hole in the type system stays, we have to add it to the spec in the type-unsoundness section.
        Hide
        Yoav Zibin added a comment -

        fixed in revision 21824.
        we produce an unsound-cast-warning if you cast to a type parameter.
        This affects only "as", not "instanceof" (but only "as" is not type-safe).

        Show
        Yoav Zibin added a comment - fixed in revision 21824. we produce an unsound-cast-warning if you cast to a type parameter. This affects only "as", not "instanceof" (but only "as" is not type-safe).
        Hide
        David Grove added a comment -

        bulk close of all 2.2.0 resolved issues.

        Show
        David Grove added a comment - bulk close of all 2.2.0 resolved issues.

          People

          • Assignee:
            Yoav Zibin
            Reporter:
            Igor Peshansky
          • Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: