Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LLVM Crash with using a private type as default value outside of the package #4130

Closed
sgebbie opened this issue Jun 4, 2022 · 10 comments
Closed
Labels
bug Something isn't working good first issue Good for newcomers help wanted Extra attention is needed

Comments

@sgebbie
Copy link
Contributor

sgebbie commented Jun 4, 2022

When declaring a type as private to a package, but then using that value as the default value poncy crashes (SIGSEGV) within llvm with the following message:

  • PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.

Code to reproduce the error

// inside the "useful" package

primitive _PrivateDefault

actor Useful[A: Any val]
  fun tag config(value: (A | _PrivateDefault) = _PrivateDefault): Useful[A] => this
// inside "main"

use "useful"

primitive This
primitive That

type Stuff is (This | That)

actor Main
  new create(env: Env) =>
    let u = Useful[Stuff].config()

Commentary indicating the possible source of the problem

See: method_def in check_partial_function_call ends up being null.

  // Look up the original method definition for this method call.
  deferred_reification_t* method_def = lookup(opt, receiver, ast_type(receiver),
    ast_name(method));
  ast_t* method_ast = method_def->ast;
  deferred_reify_free(method_def);

See also

@ponylang-main ponylang-main added the discuss during sync Should be discussed during an upcoming sync label Jun 4, 2022
@SeanTAllen SeanTAllen added bug Something isn't working help wanted Extra attention is needed good first issue Good for newcomers labels Jun 4, 2022
@ergl
Copy link
Member

ergl commented Jun 6, 2022

The code in OP fails even if one doesn't use a union type when calling the method. Here's a more minimal example:

// In the "lib" pacakge

primitive _Private

primitive Public
  fun apply[T](v: (T | _Private) = _Private): None => None

// In main
use lib = "lib"

actor Main
  new create(env: Env) =>
    let p = lib.Public.apply[U8]()
    env.out.print(p.string())

The problem here is that lookup, by default, doesn't allow one to include symbols that are package-private. See here:

if(is_name_private(type_name) && (from != NULL) && (opt != NULL)
&& !allow_private)
{
if(ast_nearest(def, TK_PACKAGE) != t->frame->package)
{
if(errors)
{
ast_error(opt->check.errors, from, "can't lookup fields or methods "
"on private types from other packages");
}
return NULL;
}
}

Since the _PrivateDefault type is private, lookup will return NULL, which leads to a segfault. Solution here, depending on what the correct behaviour should be:

  1. If the code in OP should be accepted, then we should use lookup_try, which allows one to set allow_private to true.

  2. If the code in OP shouldn't be accepted, then we should correctly handle a NULL return. In our case, lookup already sets an appropriate error message, so we should just print it to the user and return false from check_partial_function_call (which, in spite of the name, is called for any function, not just for partial functions)

@jemc
Copy link
Member

jemc commented Jun 14, 2022

This was discussed in the last ~20m or so of the sync call.

Today in Pony, we allow private types to be in the parameter types or return type of a public function signature. It may be considered by some to be "bad practice" to do so, but it is allowed. So for consistency with that, I think we'd have to also allow the default argument expression to name a private type (as was being attempted in this example).

However, one of the things I have been thinking about lately relates to the automatic detection of breaking changes in type signatures, for tooling to assist with semantic versioning. The challenge there is that if we allow private types to be the return type of a public function, then all public functions of the private type become part of the public API contract to be upheld with respect to semantic versioning.

So with that in mind, I think I'd be in favor of a breaking change to Pony language to disallow private types to be named in the public method signatures of a public type.

Gordon and red were both surprised that Pony allows this today and agreed with the idea of disallowing it.

@sgebbie
Copy link
Contributor Author

sgebbie commented Jun 15, 2022

This was discussed in the last ~20m or so of the sync call.

Today in Pony, we allow private types to be in the parameter types or return type of a public function signature. It may be considered by some to be "bad practice" to do so, but it is allowed. So for consistency with that, I think we'd have to also allow the default argument expression to name a private type (as was being attempted in this example).

However, one of the things I have been thinking about lately relates to the automatic detection of breaking changes in type signatures, for tooling to assist with semantic versioning. The challenge there is that if we allow private types to be the return type of a public function, then all public functions of the private type become part of the public API contract to be upheld with respect to semantic versioning.

So with that in mind, I think I'd be in favor of a breaking change to Pony language to disallow private types to be named in the public method signatures of a public type.

Gordon and red were both surprised that Pony allows this today and agreed with the idea of disallowing it.

While allowing private values as a default did provide a convenient way of providing a "hidden default," I have to admit that in the back of my mind it did feel like it wasn't a totally legitimate use of the language.

So, I am inclined to agree that it should be flagged as a compile time error.

@SeanTAllen SeanTAllen added the needs discussion Needs to be discussed further label Jun 21, 2022
@SeanTAllen
Copy link
Member

So the allowing of private types like this was core to the RFC to change collections iterators so its a well trod path at the moment for many in terms of it being discussed a lot. Both on at least 1 sync call, in Zulip, and as the basis for a very discussed RFC. I am not in favor of changing that behaviour without an RFC to consider the ramifications.

I would like for us to say that we will fix this bug as it stands without touching anything related to private visibility outside of a package if explicitly provided. That should only be done if an RFC is accepted.

I'd like to get others to agree first though. I know we have a user who is interested in fixing this issue so I'd like to come to some agreement as to what we do next.

@jemc
Copy link
Member

jemc commented Jun 21, 2022

Fixing the current bug as it exists makes sense to me, as long as the investment to do so is not too high.

@SeanTAllen SeanTAllen removed needs discussion Needs to be discussed further discuss during sync Should be discussed during an upcoming sync labels Jun 21, 2022
@jemc
Copy link
Member

jemc commented Jun 21, 2022

We discussed this more at today's sync.

Sean has spent time looking at how semver tooling would need to apply in the light of the current Pony behavior of allowing private types to be returned in a public method/field - such types would have to be marked as being "exported" by the package despite being private, and thus their public methods/fields would also be considered package-public. This would need to be applied transitively in order to determine which private types have public methods/fields that are package-public.

This rule would be correct for current Pony under the definition of correctness that says "a change to a package is not a breaking change if it is not possible to write a downstream program that could be broken by that change".

Also, I noticed that even though this conversation has drifted toward disallowing this use case for semantic versioning tooling simplicity reasons, the use case in this ticket (accepting a private parameter type and/or using a private type name in the default expression value) does not actually have the effect of "exporting" that private type, using the definition we discussed above - it would only be considered "exported" if it was in the return type.

@ponylang-main ponylang-main added the discuss during sync Should be discussed during an upcoming sync label Jun 21, 2022
@SeanTAllen SeanTAllen removed the discuss during sync Should be discussed during an upcoming sync label Jun 21, 2022
@stefandd
Copy link
Contributor

Fixing the current bug as it exists makes sense to me, as long as the investment to do so is not too high.

The problem is as @ergl pointed out there are 2 ways to fix: one allowing this use of a private parameter type and the other disallowing it and displaying an error message. Has the sync produced a concensus re: this?

@ponylang-main ponylang-main added the discuss during sync Should be discussed during an upcoming sync label Jun 21, 2022
@SeanTAllen
Copy link
Member

The inability to use the private parameter is a bug and will be fixed. It shouldn't be a compiler error as nothing is wrong with the example code. It's valid Pony.

@SeanTAllen SeanTAllen removed the discuss during sync Should be discussed during an upcoming sync label Jun 21, 2022
@stefandd
Copy link
Contributor

@SeanTAllen What about the poor code in lookup.c (potential compiler crash) I pointed out to you? Should this be fixed separately? Or should I monitor this issue and point it out when proposed changes have been PR-ed?

@ponylang-main ponylang-main added the discuss during sync Should be discussed during an upcoming sync label Jun 22, 2022
@SeanTAllen
Copy link
Member

Please open issues for anything that isn't specifically the fix to this issue or PRs if you want.

@jemc jemc removed the discuss during sync Should be discussed during an upcoming sync label Jun 28, 2022
stefandd added a commit to stefandd/ponyc that referenced this issue Jul 27, 2022
This attempts to fix issues ponylang#4130 and ponylang#4153.

The issue was when a private type in another package was used as a default value in a method call.

Fix to ponylang#4130
Since it has been decided to treat this as a bug instead of a missing error, this PR implements the fix suggested by @ergl, namely using `lookup_try()` instead of lookup() in call.c's `check_partial_function_call()` since it allows to permit private types.

Fix to ponylang#4153
This is also a simply fix to lookup.c that prevents a potential segfault by a dereferencing of `opt` (`typecheck_t* t = &opt->check;`) *before* `opt != NULL` was checked. As pointed out by @SeanTAllen, opt should not be NULL to begin with when lookup_nominal is called, and instead, an assert should be added and the NULL checks in that function removed.
stefandd added a commit to stefandd/ponyc that referenced this issue Jul 27, 2022
This attempts to fix ponylang#4130

This crash stems from the use of a private type as defined in another package when it was used as a default value in a method call. Since it has been decided to treat this as a bug instead of a missing error, this PR implements the fix suggested by @ergl, namely using `lookup_try()` instead of lookup() in call.c's `check_partial_function_call()` since the former can be configured to permit private types.

Fix to ponylang#4153
This is a simply change to `lookup_nominal()` in lookup.c that prevents a potential segfault by a dereferencing of `opt` (`typecheck_t* t = &opt->check;`) *before* `opt != NULL` was checked. As pointed out by @SeanTAllen, opt should not be NULL to begin with when `lookup_nominal()` is called, and instead, an assert should be added and the NULL checks in that function removed.

With this PR, the two examples below that crashed the compiler now both compile:

Original example:
```pony
// inside the "useful" package

primitive _PrivateDefault

actor Useful[A: Any val]
  fun tag config(value: (A | _PrivateDefault) = _PrivateDefault): Useful[A] => this

// inside "main"

use "useful"

primitive This
primitive That

type Stuff is (This | That)

actor Main
  new create(env: Env) =>
    let u = Useful[Stuff].config()
```

Minimal example:
```pony
// In the "lib" pacakge

primitive _Private

primitive Public
  fun apply[T](v: (T | _Private) = _Private): None => None

// In main
use lib = "lib"

actor Main
  new create(env: Env) =>
    let p = lib.Public.apply[U8]()
    env.out.print(p.string())
```
@jemc jemc closed this as completed in 6c6fc2e Aug 2, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working good first issue Good for newcomers help wanted Extra attention is needed
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants