usethesource / rascal

The implementation of the Rascal meta-programming language (including interpreter, type checker, parser generator, compiler and JVM based run-time system)
http://www.rascal-mpl.org
Other
405 stars 78 forks source link

TC allows void expressions in ifPart and elsePart of conditional expressions #1447

Closed jurgenvinju closed 4 years ago

jurgenvinju commented 4 years ago

Describe the bug

  void voidFunc() { return; }
  void f() { return 1 < 2 ? voidFunc() : voidFunc(); }

This does not trigger a TC error yet.

This more complex example was how a ran into this:

   &T c(type[&T] _, value x) = &T y := x ? y : voidFunc();

a little experiment shows that also the "elvis" default expression does not check for this:

(1:2)[1]?voidFunc();

also the pattern match operator:

int a := voidFunc();

there may be more. perhaps we should check all expression forms for this:

syntax Expression
    = nonEmptyBlock  : "{" Statement+ statements "}" 
    | bracket \bracket: "(" Expression expression ")" 
    | closure        : Type type Parameters parameters "{" Statement+ statements "}" 
    | stepRange      : "[" Expression first "," Expression second ".." Expression last "]" 
    | voidClosure    : Parameters parameters "{" Statement* statements0 "}" 
    | \visit          : Label label Visit visit 
    | reducer        : "(" Expression init "|" Expression result "|" {Expression ","}+ generators ")" 
    | reifiedType    : "type" "(" Expression symbol "," Expression definitions ")"  
    | callOrTree     : Expression!transitiveClosure!transitiveReflexiveClosure!isDefined expression "(" {Expression ","}* arguments KeywordArguments[Expression] keywordArguments ")"
    | literal        : Literal literal 
    | \any            : "any" "(" {Expression ","}+ generators ")" 
    | \all            : "all" "(" {Expression ","}+ generators ")" 
    | comprehension  : Comprehension comprehension 
    | \set            : "{" {Expression ","}* elements0 "}" 
    | \list           : "[" {Expression ","}* elements0 "]"
    | reifyType      : "#" Type type !>> "[" !selector
    | range          : "[" Expression first ".." Expression last "]"
    | \tuple          : "\<" {Expression ","}+ elements "\>" 
    | \map            : "(" {Mapping[Expression] ","}* mappings ")" 
    | \it             : [A-Z a-z _] !<< "it" !>> [A-Z a-z _]
    | qualifiedName  : QualifiedName qualifiedName 
    | subscript    : Expression expression!transitiveClosure!transitiveReflexiveClosure!isDefined "[" {Expression ","}+ subscripts "]"
    | slice        : Expression expression!transitiveClosure!transitiveReflexiveClosure!isDefined "[" OptionalExpression optFirst ".." OptionalExpression optLast "]" 
    | sliceStep    : Expression expression!transitiveClosure!transitiveReflexiveClosure!isDefined "[" OptionalExpression optFirst "," Expression second ".." OptionalExpression optLast "]" 
    | fieldAccess  : Expression expression "." Name field 
    | fieldUpdate  : Expression expression "[" Name key "=" Expression replacement "]" 
    | fieldProject : Expression expression!transitiveClosure!transitiveReflexiveClosure!isDefined "\<" {Field ","}+ fields "\>" 
    | setAnnotation: Expression expression "[" "@" Name name "=" Expression value "]" 
    | getAnnotation: Expression expression >> "@" "@" Name name 
    | is           : Expression expression "is" Name name
    | has          : Expression expression "has" Name name
    | transitiveClosure: Expression argument "+" !>> "="
    | transitiveReflexiveClosure: Expression argument "*" !>> "=" 
    > isDefined    : Expression argument "?" 
    > negation     : "!" Expression!match!noMatch argument 
    | negative     : "-" Expression argument 
    | non-assoc splice : "*" Expression argument
    | asType       : "[" Type type "]" Expression!match!noMatch argument
    > left composition: Expression lhs "o" Expression rhs 
    > left ( product: Expression lhs "*" () !>> "*" Expression!noMatch!match rhs  
           | \join   : Expression lhs "join" Expression rhs 
           | remainder: Expression lhs "%" Expression rhs
           | division: Expression lhs "/" Expression rhs 
         )
    > left intersection: Expression lhs "&" !>> "&" Expression rhs 
    > left ( addition   : Expression lhs "+" Expression!noMatch!match rhs  
           | subtraction: Expression!transitiveClosure!transitiveReflexiveClosure lhs "-" Expression rhs
           | appendAfter: Expression lhs "\<\<" !>> "=" Expression rhs
           | insertBefore: Expression lhs "\>\>" Expression rhs 
           )
    > left modulo: Expression lhs "mod" Expression rhs
    > non-assoc ( notIn: Expression lhs "notin" Expression rhs  
                | \in: Expression lhs "in" Expression rhs 
    )
    > non-assoc ( greaterThanOrEq: Expression lhs "\>=" Expression rhs  
                | lessThanOrEq   : Expression lhs "\<=" Expression rhs 
                | lessThan       : Expression lhs "\<" !>> "-" Expression rhs 
                | greaterThan    : Expression lhs "\>" Expression rhs 
                )
    > non-assoc ( equals         : Expression lhs "==" Expression rhs
                | nonEquals      : Expression lhs "!=" Expression rhs 
                )
    > non-assoc ifDefinedOtherwise: Expression lhs "?" Expression rhs
    > non-assoc ( noMatch: Pattern pattern "!:=" Expression expression  
                | match: Pattern pattern ":=" Expression expression 
                | enumerator: Pattern pattern "\<-" Expression expression 
                ) 
    > non-assoc ( implication: Expression lhs "==\>" Expression rhs  
                | equivalence: Expression lhs "\<==\>" Expression rhs 
                )
    > left and: Expression lhs "&&" Expression rhs 
    > left or: Expression lhs "||" Expression rhs 
    > right ifThenElse: Expression condition "?" Expression thenExp ":" Expression elseExp
    ; 
PaulKlint commented 4 years ago

Good find. The expressions _ ? : _, _?, _?_ and := did not check for void. Reason: almost all operators (except these) are handled by standard functions (unaryOP, binaryOp, ...) that already check this.

In theory, all alternatives for the Expression rule have these checks, but in practice there were clearly some cases missing.

This is now fixed.