{"action":"created","issue":{"url":"https://api.github.com/repos/facebook/hhvm/issues/2252","labels_url":"https://api.github.com/repos/facebook/hhvm/issues/2252/labels{/name}","comments_url":"https://api.github.com/repos/facebook/hhvm/issues/2252/comments","events_url":"https://api.github.com/repos/facebook/hhvm/issues/2252/events","html_url":"https://github.com/facebook/hhvm/issues/2252","id":30483464,"number":2252,"title":"RFC: Ability to mark functions as non-terminating/noreturn","user":{"login":"Aatch","id":342416,"avatar_url":"https://avatars.githubusercontent.com/u/342416?v=3","gravatar_id":"","url":"https://api.github.com/users/Aatch","html_url":"https://github.com/Aatch","followers_url":"https://api.github.com/users/Aatch/followers","following_url":"https://api.github.com/users/Aatch/following{/other_user}","gists_url":"https://api.github.com/users/Aatch/gists{/gist_id}","starred_url":"https://api.github.com/users/Aatch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Aatch/subscriptions","organizations_url":"https://api.github.com/users/Aatch/orgs","repos_url":"https://api.github.com/users/Aatch/repos","events_url":"https://api.github.com/users/Aatch/events{/privacy}","received_events_url":"https://api.github.com/users/Aatch/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/facebook/hhvm/labels/feature%20request","name":"feature request","color":"ededed"},{"url":"https://api.github.com/repos/facebook/hhvm/labels/hack","name":"hack","color":"fe8700"},{"url":"https://api.github.com/repos/facebook/hhvm/labels/mid-pri","name":"mid-pri","color":"fad8c7"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":10,"created_at":"2014-03-31T03:38:25Z","updated_at":"2015-01-29T19:54:25Z","closed_at":null,"body":"## Overview\r\n\r\nAllow developers to inform the Hack typechecker that a function never returns.\r\n\r\n## Motivation\r\n\r\nIt is not uncommon to have helper functions and methods that always cause execution to diverge, i.e. not follow normal flow. This can be through throwing an exception, raising an error or calling a function like `exit`. Right now these functions are treated like any other function in Hack, requiring code like this:\r\n\r\n```php\r\nfunction foo(bool $cond) {\r\n  if ($cond) {\r\n    do_error();\r\n    invariant_violation(\"unreachable\");\r\n  }\r\n}\r\n```\r\n\r\nWhile this isn't always a big deal, a function with a sequence of checks one after the other requires an `invariant_violation` call to be added for each check, adding significantly to the noise in a function.\r\n\r\nThis is because Hack only recognises `throw`, `return` and a handful of special-cased functions as affecting control flow.\r\n\r\n## Proposal\r\n\r\nThere are a few options for this.\r\n\r\n### User Attribute\r\n\r\nA user attribute, such as `<<__NoReturn>>` could be added to the function, marking it as a function that doesn't return. This has the advantage of not requiring any user-visible changes to HHVM or Hack. The disadvantage is that it relies on the programmer ensuring that it's true.\r\n\r\n### Special Type\r\n\r\nA special \"diverging\" type could be added. This type would be considered a subtype of all other types, would only be valid in the return position of a function or method and would indicate that the function diverges. This has the advantage of fitting in well with the typesystem, allowing for cases like `return do_fail()` to work seemlessly. It also allows functions to be better checked, if somebody marks a function as \"returning\" a diverging type and then does something like this: `return \"foo\";` then the type checker will catch this as `string` is not a subtype of this hypothetical type.\r\n\r\nIt would require changes to Hack and HHVM to handle the type annotation. Rust uses `!` as the diverging type, however this is relatively unimportant. Furthermore, this would require changes to the Hack typesystem and would link the typesystem and the terminal checker.\r\n\r\n### Inference\r\n\r\nKinda similar to the previous option, however instead of requiring an explicit type for functions that don't return, instead Hack could \"work out\" that a function never returns and mark that internally. This has the same advantage as the first option of not requiring any user-visible features. However, if a change is made to the function that causes the terminating inference to fail, it could cause many errors that would be indicated away from the true source of the issue. Also, there would be no indication in the source of the functions terminating status, which could cause some confusing messages.\r\n\r\n---\r\n\r\nI'm personally fond of the second option, due to it's relative simplicity.\r\n\r\nThis change would also allow the current hard-coded functions, `assert`, `invariant` and `invariant_violation` to be handled like normal functions, instead of hard coded into the Hack typechecker."},"comment":{"url":"https://api.github.com/repos/facebook/hhvm/issues/comments/72092471","html_url":"https://github.com/facebook/hhvm/issues/2252#issuecomment-72092471","issue_url":"https://api.github.com/repos/facebook/hhvm/issues/2252","id":72092471,"user":{"login":"fredemmott","id":360927,"avatar_url":"https://avatars.githubusercontent.com/u/360927?v=3","gravatar_id":"","url":"https://api.github.com/users/fredemmott","html_url":"https://github.com/fredemmott","followers_url":"https://api.github.com/users/fredemmott/followers","following_url":"https://api.github.com/users/fredemmott/following{/other_user}","gists_url":"https://api.github.com/users/fredemmott/gists{/gist_id}","starred_url":"https://api.github.com/users/fredemmott/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fredemmott/subscriptions","organizations_url":"https://api.github.com/users/fredemmott/orgs","repos_url":"https://api.github.com/users/fredemmott/repos","events_url":"https://api.github.com/users/fredemmott/events{/privacy}","received_events_url":"https://api.github.com/users/fredemmott/received_events","type":"User","site_admin":false},"created_at":"2015-01-29T19:54:25Z","updated_at":"2015-01-29T19:54:25Z","body":"You could mark the block // UNSAFE, or the expression // UNSAFE_EXPR"}}