Date: Mon, 2 Nov 2020 15:05:06 -0500
I believe what you're thinking of is from within a fragment
https://cppx.godbolt.org/z/bazc9K:
class foo {
consteval {
-> fragment struct {
foo bar() { // this isn't okay, foo is incomplete
return {};
}
};
}
};
You can of course express the same idea, however you need to do this
indirectly (so that injection can sew things together for you, at the
right time):
class foo {
consteval {
-> fragment struct {
typename |%{reflexpr(foo)}| bar() {
return {};
}
};
}
};
Also, consider injection into another context (e.g. inject foo
global_bar() { return { }; } into the global namespace):
class foo {
consteval {
-> namespace(::) fragment namespace {
typename |%{reflexpr(foo)}| global_bar() {
return {};
}
};
}
};
This injection is delayed until the class is complete, and then injected
into the global namespace -- allowing the compiler to work with this
code "in the usual way".
------------------------------------------------------------------------
That means you can't rely on declarations assumed to have been injected
template<typename T>
struct my_class {
consteval { -> inject_useful_decl<T>(); }
decltype(useful_decl) var; // oops. error: no useful_decl
declared.
};
Correct, this doesn't work, as the consteval block doesn't execute until
the template is instantiated (https://cppx.godbolt.org/z/97v6Wr).
------------------------------------------------------------------------
I don’t think we have to make consteval blocks part of the
complete-class context of the enclosing class. Lookup already
works. We just have to ensure that it is clear that some properties
of nonstatic data members aren’t queryable.
struct S {
int a;
consteval {
-> inject_useful_decl<typename(reflexpr(S::a))>(); // Okay.
}
consteval {
-> inject_other<meta::offset_of(reflexpr(S::a))>(); // Error.
}
};
A question we’d have to answer is whether a reflection of a
nonstatic data member obtained while the class is still incomplete
would remain valid after the class is completed, and, if so, whether
the completeness would be reflected in that reflection (so that,
e.g., querying the offset later on would work). IMO, the answer
should be yes to both of those.
Agreed, on all points.
On 11/2/20 2:03 PM, Andrew Sutton via SG7 wrote:
>
>>
>> Default member initializers are parsed in a
>> complete-class-context", essentially meaning the parsing is
>> delayed until the closing } of the class.
>
> Right, but Hana’s two examples aren’t in such contexts, and we
> permit them anyway. Lookup succeeds, but some operations (like
> offsetof) aren’t permitted.
>
>
> Wow. I am not reading well today.
>
>> Adding consteval blocks to the complete-class-context might make
>> lookups work in metaprograms, but that also breaks this very
>> simple model.
>
> I don’t think we have to make consteval blocks part of the
> complete-class context of the enclosing class. Lookup already
> works. We just have to ensure that it is clear that some
> properties of nonstatic data members aren’t queryable. E.g.:
>
> struct S {
> int a;
> consteval {
> -> inject_useful_decl<typename(reflexpr(S::a))>(); // Okay.
> }
> consteval {
> -> inject_other<meta::offset_of(reflexpr(S::a))>(); // Error.
> }
> };
>
> A question we’d have to answer is whether a reflection of a
> nonstatic data member obtained while the class is still incomplete
> would remain valid after the class is completed, and, if so,
> whether the completeness would be reflected in that reflection (so
> that, e.g., querying the offset later on would work). IMO, the
> answer should be yes to both of those.
>
>
> I must be forgetting where lookup fails, but we've definitely run into
> issues requiring complete class types in *some* contexts. Wyatt can
> probably answer that question better than I can at this point.
>
>
>
https://cppx.godbolt.org/z/bazc9K:
class foo {
consteval {
-> fragment struct {
foo bar() { // this isn't okay, foo is incomplete
return {};
}
};
}
};
You can of course express the same idea, however you need to do this
indirectly (so that injection can sew things together for you, at the
right time):
class foo {
consteval {
-> fragment struct {
typename |%{reflexpr(foo)}| bar() {
return {};
}
};
}
};
Also, consider injection into another context (e.g. inject foo
global_bar() { return { }; } into the global namespace):
class foo {
consteval {
-> namespace(::) fragment namespace {
typename |%{reflexpr(foo)}| global_bar() {
return {};
}
};
}
};
This injection is delayed until the class is complete, and then injected
into the global namespace -- allowing the compiler to work with this
code "in the usual way".
------------------------------------------------------------------------
That means you can't rely on declarations assumed to have been injected
template<typename T>
struct my_class {
consteval { -> inject_useful_decl<T>(); }
decltype(useful_decl) var; // oops. error: no useful_decl
declared.
};
Correct, this doesn't work, as the consteval block doesn't execute until
the template is instantiated (https://cppx.godbolt.org/z/97v6Wr).
------------------------------------------------------------------------
I don’t think we have to make consteval blocks part of the
complete-class context of the enclosing class. Lookup already
works. We just have to ensure that it is clear that some properties
of nonstatic data members aren’t queryable.
struct S {
int a;
consteval {
-> inject_useful_decl<typename(reflexpr(S::a))>(); // Okay.
}
consteval {
-> inject_other<meta::offset_of(reflexpr(S::a))>(); // Error.
}
};
A question we’d have to answer is whether a reflection of a
nonstatic data member obtained while the class is still incomplete
would remain valid after the class is completed, and, if so, whether
the completeness would be reflected in that reflection (so that,
e.g., querying the offset later on would work). IMO, the answer
should be yes to both of those.
Agreed, on all points.
On 11/2/20 2:03 PM, Andrew Sutton via SG7 wrote:
>
>>
>> Default member initializers are parsed in a
>> complete-class-context", essentially meaning the parsing is
>> delayed until the closing } of the class.
>
> Right, but Hana’s two examples aren’t in such contexts, and we
> permit them anyway. Lookup succeeds, but some operations (like
> offsetof) aren’t permitted.
>
>
> Wow. I am not reading well today.
>
>> Adding consteval blocks to the complete-class-context might make
>> lookups work in metaprograms, but that also breaks this very
>> simple model.
>
> I don’t think we have to make consteval blocks part of the
> complete-class context of the enclosing class. Lookup already
> works. We just have to ensure that it is clear that some
> properties of nonstatic data members aren’t queryable. E.g.:
>
> struct S {
> int a;
> consteval {
> -> inject_useful_decl<typename(reflexpr(S::a))>(); // Okay.
> }
> consteval {
> -> inject_other<meta::offset_of(reflexpr(S::a))>(); // Error.
> }
> };
>
> A question we’d have to answer is whether a reflection of a
> nonstatic data member obtained while the class is still incomplete
> would remain valid after the class is completed, and, if so,
> whether the completeness would be reflected in that reflection (so
> that, e.g., querying the offset later on would work). IMO, the
> answer should be yes to both of those.
>
>
> I must be forgetting where lookup fails, but we've definitely run into
> issues requiring complete class types in *some* contexts. Wyatt can
> probably answer that question better than I can at this point.
>
>
>
Received on 2020-11-02 14:05:13