{"action":"created","issue":{"url":"https://api.github.com/repos/chapel-lang/chapel/issues/8938","repository_url":"https://api.github.com/repos/chapel-lang/chapel","labels_url":"https://api.github.com/repos/chapel-lang/chapel/issues/8938/labels{/name}","comments_url":"https://api.github.com/repos/chapel-lang/chapel/issues/8938/comments","events_url":"https://api.github.com/repos/chapel-lang/chapel/issues/8938/events","html_url":"https://github.com/chapel-lang/chapel/issues/8938","id":308031449,"number":8938,"title":"owned & borrowed syntax proposal: new returns borrow","user":{"login":"mppf","id":3653132,"avatar_url":"https://avatars3.githubusercontent.com/u/3653132?v=4","gravatar_id":"","url":"https://api.github.com/users/mppf","html_url":"https://github.com/mppf","followers_url":"https://api.github.com/users/mppf/followers","following_url":"https://api.github.com/users/mppf/following{/other_user}","gists_url":"https://api.github.com/users/mppf/gists{/gist_id}","starred_url":"https://api.github.com/users/mppf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mppf/subscriptions","organizations_url":"https://api.github.com/users/mppf/orgs","repos_url":"https://api.github.com/users/mppf/repos","events_url":"https://api.github.com/users/mppf/events{/privacy}","received_events_url":"https://api.github.com/users/mppf/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":15,"created_at":"2018-03-23T13:35:56Z","updated_at":"2018-04-17T23:05:16Z","closed_at":null,"author_association":"CONTRIBUTOR","body":"Previous: #8797. Next: none.\r\n\r\nThis is a variation on #8654.\r\n\r\nGeneral idea: `MyClass` refers to a borrow, `owned MyClass` refers to an owning pointer. `new` calls can be marked as `owned` or `unsafe`, but one that is not marked returns the result of `(new owned C(...)).borrow()`.  Additionally, when instantiating generics, Owned/Shared arguments are replaced by Borrows unless the generic explicitly opts in to accepting Owned/Shared. The default intent for Owned/Shared is to perform ownership transfer / sharing (ie `const in`).\r\n\r\nA generic argument or field can opt in to accepting owned/shared with the following generic types:\r\n * `managed` means must be owned/shared\r\n * `managed?` means it could be owned/shared/borrow\r\n * `owned` means it must be owned\r\n * `shared` means it must be shared\r\n\r\nThe general idea driving the special rule for the instantiation of owned/shared in this proposal is that it would be surprising for generic code (especially code that is not written with owned classes in mind) to have ownership transfers by default. However once a generic argument has a type or partially specified type, be it a class type (borrow) or owned or ..., it becomes clear that ownership transfer should or should not occur. So, this special rule need only apply to totally generic arguments (those with no type as in `proc f(x)` or those with a queried type like `proc g(x:?t)`.\r\n\r\nIt would be an error to call a type constructor for a record/class where the argument type for a generic field is owned but the field declaration is totally generic (and therefore converted to borrow).\r\n\r\nEdits to this issue:\r\n * updated to treat instantiations of generic functions with Owned arguments as borrow type by default, per discussion in #8618.\r\n * updated to imagine default intent of Owned/Shared is ownership transfer rather than borrow.\r\n * described type constructor error\r\n\r\n### Locales Primer Example\r\n``` chapel\r\nclass Node {\r\n  var data: real;\r\n  var next: owned Node;\r\n}\r\nvar head    = new Node(0); // this is a (new Owned(new Node(0)).borrow()\r\nvar current = head;\r\n\r\nfor i in 1..numLocales-1 do on Locales[i] {\r\n  current.next = new owned Node(i); // forgot 'owned'? error, borrow doesn't coerce to owned\r\n  current      = current.next.borrow();\r\n}\r\n```\r\n\r\n### Generic Collection Example\r\n``` chapel\r\nrecord Collection {\r\n  var element:managed?; // see below\r\n}\r\n// the managed? type annotation allows addElement to opt-in\r\n// to being instantiated with Owned. Otherwise it would\r\n// change the argument to a borrow on instantiation.\r\n// Note that the default intent for a managed pointer\r\n// is to transfer ownership, which is what is desired here.\r\nproc Collection.addElement(x:managed?) {\r\n  element = x;\r\n}\r\nproc test() {\r\n  var c: Collection(owned MyClass);\r\n  c.addElement(new owned MyClass(1)); // forgot 'owned'? error from lifetime checker\r\n  var d: Collection(int);\r\n  d.addElement(2);\r\n}\r\n```\r\n\r\n### Generic Function Example\r\n``` chapel\r\n// data is MyClass per special instantiation rules\r\n// default intent for that is 'const in' rather than 'const ref'\r\nproc process(data) {\r\n  var sum = 0;\r\n  for i in 1..n {\r\n    sum += data.value; // single-deref here for Owned actual\r\n  }\r\n}\r\nproc test() {\r\n  var data = new owned MyClass(1);\r\n  process(data);\r\n  // but for such an example, the version without owned\r\n  // would work too, and instantiate process with the borrow type.\r\n}\r\n```\r\n\r\n### Borrow into Argument Record\r\n``` chapel\r\nrecord Argument {\r\n  var data; // may not store owned, since it didn't opt-in with :managed/:managed? etc\r\n}\r\nproc process(arg:Argument) {\r\n  ... use arg.data ...\r\n}\r\nproc test() {\r\n  var data = new owned MyClass(1);\r\n  var arg = new Argument(data); // record stores borrow\r\n  process(arg);\r\n  // the above snippet could also be written as\r\n  // var data = new MyClass(1); // this is a borrow\r\n  // var arg = new Argument(data);\r\n  // process(arg);\r\n}\r\n```\r\n \r\n### Function returning newly allocated object\r\n``` chapel\r\nproc returnNewMyClass() : owned MyClass {\r\n  return new owned MyClass(1);\r\n  // forgot owned? error from type system or borrow checker\r\n}\r\nproc wrapReturnNewMyClass() {\r\n  return returnNewMyClass(); // return type inferred as owned MyClass\r\n}\r\n```\r\n\r\n### Function returning a borrow\r\n``` chapel\r\nproc returnBorrowGlobal() : MyClass {\r\n  return globalMyClass.borrow();\r\n}\r\nproc wrapReturnBorrowGlobal() {\r\n  return returnBorrowGlobal(); // return type inferred as MyClass\r\n}\r\n```\r\n\r\n### Does specifying type change new?\r\n``` chapel\r\nvar myC = new owned MyClass(1);\r\n// myC has type owned MyClass\r\nvar myC:owned MyClass = new owned MyClass(1);\r\n// myC has type owned MyClass\r\n\r\nvar myC = new MyClass(1);\r\n// myC has type MyClass (borrow)\r\nvar myC:MyClass = new MyClass(1);\r\n// myC has type MyClass (borrow)\r\n```"},"comment":{"url":"https://api.github.com/repos/chapel-lang/chapel/issues/comments/382184875","html_url":"https://github.com/chapel-lang/chapel/issues/8938#issuecomment-382184875","issue_url":"https://api.github.com/repos/chapel-lang/chapel/issues/8938","id":382184875,"user":{"login":"lydia-duncan","id":2454710,"avatar_url":"https://avatars2.githubusercontent.com/u/2454710?v=4","gravatar_id":"","url":"https://api.github.com/users/lydia-duncan","html_url":"https://github.com/lydia-duncan","followers_url":"https://api.github.com/users/lydia-duncan/followers","following_url":"https://api.github.com/users/lydia-duncan/following{/other_user}","gists_url":"https://api.github.com/users/lydia-duncan/gists{/gist_id}","starred_url":"https://api.github.com/users/lydia-duncan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lydia-duncan/subscriptions","organizations_url":"https://api.github.com/users/lydia-duncan/orgs","repos_url":"https://api.github.com/users/lydia-duncan/repos","events_url":"https://api.github.com/users/lydia-duncan/events{/privacy}","received_events_url":"https://api.github.com/users/lydia-duncan/received_events","type":"User","site_admin":false},"created_at":"2018-04-17T23:05:16Z","updated_at":"2018-04-17T23:05:16Z","author_association":"MEMBER","body":"I agree with @vasslitvinov's suggestion to only apply the rule to compiler-generated initializers.  Doing the same thing for explicit initializers is making an assumption about the use for the argument in question - we don't know it will be used as an initial value for a field and adjusting based on that seems confusing.  As an example:\r\n```chapel\r\nproc init(x) {\r\n  field = callOnX(x); // callOnX probably expects a borrow\r\n}\r\n```\r\nor\r\n```chapel\r\nproc init(a, b, c) {\r\n  field1 = a; // Maybe this should be owned\r\n  if (someComputationOnB(b)) { // But maybe we expect a borrow here\r\n     field2 = c; // Who even knows?\r\n  }\r\n}\r\n```\r\n  \r\n"}}
{"actor":{"display_login":"lydia-duncan"}}