-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Add builtin functions for destroy, with special requirements in facet types #6035
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
Conversation
69eeff4
to
d25ea05
Compare
//@dump-sem-ir-end | ||
} | ||
|
||
// --- todo_fail_wrong_type.carbon |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is supposed to fail in here? Leave a TODO?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added TODO below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't it the exact same signature as the Op in the test above?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Above, Self
is ()
or {}
. In this test, Self
is class C
. Can't that be an error in the declaration, since it's not a blanket impl?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I see, because C
is not AggregateDestroy? Interesting, the actual interface is a builtin one (CanAggregateDestroy()
). It's a bit of an extra step to also restrict which impls are allows to use the builtin function. Is the plan to require it to be in an impl which is a blanket impl and implementing the built-in interface (though this test is not)? Or would it make sense to just not allow it in any impl outside Core, or something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because C
is not intended to be destructed through type.aggregate_destroy
, yes. Note that CanAggregateDestroy
isn't mentioned in the function:
impl C as DestroyLike {
// TODO: This should fail because it has the wrong type.
fn Op[addr self: Self*]() = "type.aggregate_destroy";
}
Is the plan to require it to be in an impl which is a blanket impl and implementing the built-in interface (though this test is not)?
Three ends where I think validation may be possible:
- Uses
addr self
, not an arbitrary pointer ("// TODO: The argument should beaddr self: Self*
.") - Is a blanket impl of the right builtin constraint, and/or whatever type information it sees matches that.
- If we can easily do validation when creating a specific, double-check there.
I think it should be possible to do more, but in particular addr self
requires modifying ValidateSignature
, or creating something different, because ValidateSignature
exposes the type but not the pattern. Yes, this change isn't doing that. :)
Or would it make sense to just not allow it in any impl outside Core, or something?
We haven't generally been restricting builtin functions to Core
, so I'm hesitant to start that.
@danakj Several of your comments seem to be asking for a different implementation. At present, this can be used with
That's intentional -- while I haven't figured out precisely what will work with destruction, I think we want to ability to combine special requirements with
In the above, creating an extra interface works, but it also creates an additional indirection. Also,disallowing Given all your comments to this effect, should we try using one of the open discussion slots to discuss the approach here? |
This is because the RHS of carbon-lang/toolchain/check/eval.cpp Lines 2227 to 2228 in 1c6e859
So that's not to say carbon-lang/toolchain/check/eval.cpp Lines 2234 to 2241 in 1c6e859
That is a point about combining facet types. Given that, I guess what I would like is a bit of a change but less dramatic:
On the topic though of "like an interface", do you expect |
To explain, I went in this specific direction because
Fixing this; it's not intentional, I'd just missed the extra work for
Will do.
Ideally, I'd hope that these special requirements don't need to have anything. So far, we're only talking about using them just to filter types in order to determine whether there should be an impl, because that filtering is complex. If we need an interface, I'd hope that it behaves more like |
This is a bit of an experiment to see if there's a reasonable way to write a shared enum type, rather than writing per-case wrappers for things like `HasTypeQualifiers` or the printing. I think it's a bit borderline complexity right now, but I'm not sure I can reduce it much further. This changes from things like `Internal::EnumClassName##RawEnum` to `Internal::EnumClassName##Data::RawEnum` so that the enum entries can have back references to bit shifts without needing to know the containing type name. Because I'm trying to reduce duplication between mask and non-mask enums, I did this to non-mask enums too. This was motivated by #6035 adding another enum mask (which will grow more entries, and is intended to switch if this is accepted), but I'm not using that PR as a base here because I didn't want the merge dependency.
(note on comment times, I just now realized I hadn't published draft comments) |
I was looking at this for |
//@dump-sem-ir-end | ||
} | ||
|
||
// --- todo_fail_wrong_type.carbon |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't it the exact same signature as the Op in the test above?
toolchain/sem_ir/facet_type_info.h
Outdated
rewrite_constraints.empty() && special_requirement_mask.empty() && | ||
!other_requirements) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This means you can't write impl C as SpecialRequirementThing()
which seems good. It's a little bit obfuscated though. Maybe worth a comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added to BuiltinConstraintMask
, along with notes about the expected API; does that seem good to you?
This did make me notice that impl C as (I & CanAggregateDestroy())
is valid, which does that seem right to you?
(either way I've added a test to show the behavior, fail_impl.carbon
and impl_with_interface.carbon
here)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added to
BuiltinConstraintMask
, along with notes about the expected API; does that seem good to you?This did make me notice that
impl C as (I & CanAggregateDestroy())
is valid, which does that seem right to you?
Uh.. well, Idk. CanAggregeateDestroy provides the equivalent of a FacetType type where .Self impls <stuff>
. We also allow this:
interface Z {}
interface Y {}
class C {}
impl C as Y where .Self impls Z {}
and
interface Z {}
interface Y {}
class C {}
impl C as Y & (type where .Self impls Z) {}
So I guess yes (though I am not sure that's what the design says...). It should just stay consistent with the .Self impls
case if that changes.
But I was wrong about this function being the thing that limits the right side of impl as
. It looks like that is the job of IdentifiedFacetType::is_valid_impl_as_target()
instead.
Which raises the point that if you go from FacetTypeInfo to an IdentifiedFacetType at the moment, you lose the builtins. Maybe that's fine? They don't need to be identified and they can be looked for on the FacetTypeInfo.. but maybe also worth a comment at the least.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the IdentifiedFacetType
behavior is fine for the moment, but adding a TODO that BuiltinConstraintMask
should probably be included there. My guess is we'll want that eventually.
Yes works for me |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Catching up with comments. :)
//@dump-sem-ir-end | ||
} | ||
|
||
// --- todo_fail_wrong_type.carbon |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Above, Self
is ()
or {}
. In this test, Self
is class C
. Can't that be an error in the declaration, since it's not a blanket impl?
toolchain/sem_ir/facet_type_info.h
Outdated
rewrite_constraints.empty() && special_requirement_mask.empty() && | ||
!other_requirements) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added to BuiltinConstraintMask
, along with notes about the expected API; does that seem good to you?
This did make me notice that impl C as (I & CanAggregateDestroy())
is valid, which does that seem right to you?
(either way I've added a test to show the behavior, fail_impl.carbon
and impl_with_interface.carbon
here)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
toolchain/sem_ir/facet_type_info.h
Outdated
rewrite_constraints.empty() && special_requirement_mask.empty() && | ||
!other_requirements) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added to
BuiltinConstraintMask
, along with notes about the expected API; does that seem good to you?This did make me notice that
impl C as (I & CanAggregateDestroy())
is valid, which does that seem right to you?
Uh.. well, Idk. CanAggregeateDestroy provides the equivalent of a FacetType type where .Self impls <stuff>
. We also allow this:
interface Z {}
interface Y {}
class C {}
impl C as Y where .Self impls Z {}
and
interface Z {}
interface Y {}
class C {}
impl C as Y & (type where .Self impls Z) {}
So I guess yes (though I am not sure that's what the design says...). It should just stay consistent with the .Self impls
case if that changes.
But I was wrong about this function being the thing that limits the right side of impl as
. It looks like that is the job of IdentifiedFacetType::is_valid_impl_as_target()
instead.
Which raises the point that if you go from FacetTypeInfo to an IdentifiedFacetType at the moment, you lose the builtins. Maybe that's fine? They don't need to be identified and they can be looked for on the FacetTypeInfo.. but maybe also worth a comment at the least.
//@dump-sem-ir-end | ||
} | ||
|
||
// --- todo_fail_wrong_type.carbon |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I see, because C
is not AggregateDestroy? Interesting, the actual interface is a builtin one (CanAggregateDestroy()
). It's a bit of an extra step to also restrict which impls are allows to use the builtin function. Is the plan to require it to be in an impl which is a blanket impl and implementing the built-in interface (though this test is not)? Or would it make sense to just not allow it in any impl outside Core, or something?
Co-authored-by: Dana Jansens <[email protected]>
Co-authored-by: Dana Jansens <[email protected]>
This is in support of a goal of changing the blanket
destroy
impl to use (roughly):That isn't done here because there's still other issues that migrating raises. What this does do is add the builtin functions, and in particular, support to
FacetTypeInfo
to makeCanAggregateDestroy
work.The "special requirement" approach in
FacetTypeInfo
allows us to support restricting a blanket impl under the current approach of impls. Maybe we'll find a cleaner approach that can work in the future, but this fits into the current model by propagating similar to other requirements. I'm using an enum mask because we have a number of similar things to add (e.g. copy, move) but I'm not sure we need a full vector.A few alternatives considered were:
where .Self impls TypeCanAggregateDestroy(.Self, SupportedInterface, UnsupportedInterface)
. I think it'd be a little cleaner, but requires better compile-time evaluation in order to assess the type of the call. Right now it's expected to be aFacetType
too early to make this work, and I was concerned about pouring too much more time down this route.Core.
for an interface. This would've added name lookup overhead, and the question of whether animpl
exists.impl
should also be generated. Work I've previously done generating interfaces for class destruction also feels complex to both write and understand (an unfortunate issue).ImplsConstraint
, for example by defining a specialInterfaceId::CanAggregateDestroy = -2
similar to what we do on other ids. I was hesitant because of how this expands the number of modes ofInterfaceId
, and things for consuming code to watch out for, for what feels like a relatively niche set of use-cases that are only interface-like.