-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Forward impl
declarations of incomplete facet types
#5219
base: trunk
Are you sure you want to change the base?
Conversation
toolchain/check/facet_type.cpp
Outdated
// this. | ||
CARBON_CHECK(is_definition); | ||
CARBON_DIAGNOSTIC(ImplAsIncompleteFacetTypeDefinition, Error, | ||
"impl as incomplete facet type {0} definition", |
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 have trouble reading this grammatically, so suggesting something else:
"impl as incomplete facet type {0} definition", | |
"`impl as` target has facet type {0} that is not yet defined", |
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 was trying to convey that impl
definitions specifically require the facet type to be complete (now unlike impl
declarations).
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.
Maybe something like definition of impl as incomplete facet type {0}
?
// TODO: Finish facet type resolution. This code currently only handles | ||
// rewrite constraints that set associated constants to a concrete value. | ||
// Need logic to topologically sort rewrites to respect dependencies, and | ||
// afterwards reject duplicates that are not identical. | ||
|
||
auto facet_type_id = | ||
context.types().GetTypeIdForTypeInstId(facet_type_inst_id); | ||
CARBON_CHECK(facet_type_id != SemIR::ErrorInst::SingletonTypeId); |
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.
Generally we want to pass errors along through the system, how come we want a CHECK instead of a return 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.
Because the case should be unreachable here.
context, witness_loc_id, | ||
{.type_id = | ||
GetSingletonType(context, SemIR::WitnessType::SingletonInstId), | ||
.elements_id = context.inst_blocks().AddPlaceholder(), |
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 wonder if we should have a special InstBlockId for this to differentiate from an empty block?
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.
These ids should not be distinguished. This is a block that will be filled in later without changing its id. I've added a comment.
// require Self impls Y; | ||
} | ||
|
||
// Classes must be defined before being used in a function definition. |
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.
There's a test for this somewhere?
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'm not sure, but it is not the topic of this PR. This comment I believe is copied from the proposal.
toolchain/check/testdata/impl/min_prelude/forward_declarations.carbon
Outdated
Show resolved
Hide resolved
toolchain/check/type_completion.h
Outdated
// set of interfaces the facet type requires is known. Since named constraints | ||
// are not yet supported, this currently never fails. Eventually this function | ||
// will be passed a diagnoser for facet types that use some incomplete named | ||
// constraint, and return `None` in that case. |
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.
Mention that this ensures the FacetType is present in identified_facet_types()
when it returns non-None?
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.
Done.
// the facet type to the right of an `impl`...`as`, or `None` if no such | ||
// single interface. | ||
InterfaceId interface_id; | ||
union { |
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.
Can we use a std::variant so that we get checks that verify we don't access the wrong one and make UB?
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 that would mean extra storage for the discriminator.
toolchain/sem_ir/facet_type_info.h
Outdated
// The single interface from `required_interfaces` to implement if this is | ||
// the facet type to the right of an `impl`...`as`, or `None` if no such | ||
// single interface. | ||
InterfaceId interface_id; |
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.
These fields are duplicating the first element of required_interfaces, but adding a bunch of complexity for using the IntentifiedFacetType.
I was wondering if a method would suit our purposes just as well, without causing us to use variants/unions and so on?
Something like this?
auto is_impl_as_target() -> bool {
return required_interfaces.size() == 1;
}
auto impl_as_target_interface( ) -> SpecificInterface {
if (is_impl_as_target()) { return required_interfaces.front(); }
else { return None; }
}
Or do you want to use these to hide the num_to_impl specifically when it's not used?
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.
One of my goals for this PR is to have the identified interfaces be a sorted list of all the required interfaces, mixing together the ones to implement. I'm hoping to follow this up with a PR to support where .Self impls J
, at which point the one to implement won't necessarily be first. Adding some accessors to hide this complexity and maintain the invariants is a good idea though.
Co-authored-by: Dana Jansens <[email protected]>
Co-authored-by: Dana Jansens <[email protected]>
This comment was marked as off-topic.
This comment was marked as off-topic.
toolchain/check/facet_type.cpp
Outdated
// this. | ||
CARBON_CHECK(is_definition); | ||
CARBON_DIAGNOSTIC(ImplAsIncompleteFacetTypeDefinition, Error, | ||
"impl as incomplete facet type {0} definition", |
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 was trying to convey that impl
definitions specifically require the facet type to be complete (now unlike impl
declarations).
// TODO: Finish facet type resolution. This code currently only handles | ||
// rewrite constraints that set associated constants to a concrete value. | ||
// Need logic to topologically sort rewrites to respect dependencies, and | ||
// afterwards reject duplicates that are not identical. | ||
|
||
auto facet_type_id = | ||
context.types().GetTypeIdForTypeInstId(facet_type_inst_id); | ||
CARBON_CHECK(facet_type_id != SemIR::ErrorInst::SingletonTypeId); |
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 the case should be unreachable here.
context, witness_loc_id, | ||
{.type_id = | ||
GetSingletonType(context, SemIR::WitnessType::SingletonInstId), | ||
.elements_id = context.inst_blocks().AddPlaceholder(), |
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.
These ids should not be distinguished. This is a block that will be filled in later without changing its id. I've added a comment.
} | ||
const auto& complete = context.complete_facet_types().Get(complete_id); | ||
if (complete.num_to_impl != 1) { | ||
auto identified_id = RequireIdentifiedFacetType(context, *facet_type); |
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'm happy to change the terminology, but that is really a topic for the proposal. I just haven't heard a clearer term. I feel like "identified" is less misleading than "fully declared" and I think users will have fewer preconceived ideas what "identified" means so it is more open for us to give it our own meaning.
// required to be complete. | ||
if (witness.elements_id != SemIR::InstBlockId::Empty && | ||
witness_block.empty()) { | ||
if (!RequireCompleteFacetTypeForImplDefinition( |
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.
In the other case, it was already checked in InitialFacetTypeImplWitness
.
toolchain/check/handle_impl.cpp
Outdated
impl_info.interface = *required_interface; | ||
} | ||
|
||
impl_info.interface = CheckConstraintIsInterface(context, impl_info); |
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.
Done. It required changing the signature of CheckConstraintIsInterface
, but that wasn't a problem.
// CHECK:STDERR: fail_extend_partially_defined_interface.carbon:[[@LINE-5]]:1: note: interface is currently being defined [InterfaceIncompleteWithinDefinition] | ||
// CHECK:STDERR: interface I { | ||
// CHECK:STDERR: ^~~~~~~~~~~~~ | ||
// CHECK:STDERR: | ||
// CHECK:STDERR: fail_extend_partially_defined_interface.carbon:[[@LINE+4]]:5: error: impl declared but not defined [ImplMissingDefinition] |
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.
Well, I'm definitely missing code to enforce I
is complete in this case. So that is now added. I don't know about your other question.
@@ -0,0 +1,2867 @@ | |||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM |
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 just realized we abbreviate "declarations", so I'll rename this file before merging.
interface X; | ||
|
||
// Allowed to use incomplete interfaces in function declarations. | ||
fn F(U:! X); |
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.
// require Self impls Y; | ||
} | ||
|
||
// Classes must be defined before being used in a function definition. |
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'm not sure, but it is not the topic of this PR. This comment I believe is copied from the proposal.
Implements some of the changes from proposal #5168.
impl
declarations of identified but incomplete facet types are allowed unless the facet type has rewrites. An incomplete facet type with rewrites is already either an error or has more than one interface and so can't be implemented, so this case can't be exercised very well yet.impl
declarations of interface without rewrites use a placeholder inst block for the witness.