Re: [PATCH] avoid modifying type in attribute access handler [PR94098]

2020-03-27 Thread Martin Sebor via Gcc-patches

On 3/26/20 6:52 PM, Jason Merrill wrote:

On 3/26/20 4:57 PM, Martin Sebor wrote:

On 3/25/20 3:56 PM, Jason Merrill wrote:

On 3/16/20 4:41 PM, Martin Sebor wrote:

The recent fix to avoid modifying in place the type argument in
handle_access_attribute (PR 92721) was incomplete and didn't fully
resolve the problem (an ICE in the C++ front-end).  The attached
patch removes the remaining modification that causes the ICE.  In
addition, the change adjusts checking calls to functions declared
with the attribute to scan all its instances.

The attached patch was tested on x86_64-linux.



   attrs = remove_attribute (IDENTIFIER_POINTER (name), attrs);


This unchanged line can still modify existing types; I think if attrs 
already has a matching attribute you need to work harder to replace it.


I have a number of questions.

1) There are two instances of the call above in the code (only one in
the context of the patch).  Are you referring specifically to the one
in the patch or to both?  The one in the patch manipulates the type
attributes of the last DECL (NODE[1]).  The other one, attributes of
the current type (NODE[0]).  Are both a problem?


Both, yes.  We should give the decl(s) a new type rather than modify 
their existing types.



2) What all do you include in "modifying existing types?"  A number
of attribute handlers unconditionally modify the type of a *NODE
without first testing ATTR_FLAG_TYPE_IN_PLACE (e.g., by setting
TYPE_USER_ALIGN, or TREE_USED).  Are those modifications safe?  If
they are, what makes the difference between those and the code above?


Those also seem unsafe.


3) Is the restriction on "modifying existing types" specific to
the C++ front end or a general one that applies to the whole C/C++
family?  (I've only seen a failure in the C++ FE.)


It's a general correctness issue, the C++ front end just catches the 
issues better.  If you have two declarations with the same type, and 
then you give one an attribute, it shouldn't give that same attribute to 
the other declaration that shared the type.



4) What exactly does "work harder" mean and how do I test it?  Are
you suggesting to call build_type_attribute_variant (or maybe
build_variant_type_copy like the existing handlers do) to replace
the type?  Or are you implying that unless I need to replace
the existing type  I should avoid modifying the TYPE's
TYPE_ATTRIBUTES and instead work with a copy of the attribute chain?


We can't modify the existing attribute chain; if we need to remove an 
attribute from the middle, it must be by copying all the list nodes 
before the attribute.



These restrictions and the lack of enforcement (only in the C++ FE)
make nontrivial handlers extremely fragile.  Unless the restrictions
are entirely C++-specific I would really like to add assertions to
decl_attributes to catch these problems earlier (and in C as well).


Sounds good.


Thanks for the answers.  I'll see about implementing some checking
of these restrictions in stage 1.

Martin




Either way, I also want to add test cases to exercise them.  But
I need to understand them first (i.e., get answers to the questions
above).  Nothing I've tried so far has triggered a problem due to
the code you point out above.

Martin







Re: [PATCH] avoid modifying type in attribute access handler [PR94098]

2020-03-26 Thread Jason Merrill via Gcc-patches

On 3/26/20 4:57 PM, Martin Sebor wrote:

On 3/25/20 3:56 PM, Jason Merrill wrote:

On 3/16/20 4:41 PM, Martin Sebor wrote:

The recent fix to avoid modifying in place the type argument in
handle_access_attribute (PR 92721) was incomplete and didn't fully
resolve the problem (an ICE in the C++ front-end).  The attached
patch removes the remaining modification that causes the ICE.  In
addition, the change adjusts checking calls to functions declared
with the attribute to scan all its instances.

The attached patch was tested on x86_64-linux.



   attrs = remove_attribute (IDENTIFIER_POINTER (name), attrs);


This unchanged line can still modify existing types; I think if attrs 
already has a matching attribute you need to work harder to replace it.


I have a number of questions.

1) There are two instances of the call above in the code (only one in
the context of the patch).  Are you referring specifically to the one
in the patch or to both?  The one in the patch manipulates the type
attributes of the last DECL (NODE[1]).  The other one, attributes of
the current type (NODE[0]).  Are both a problem?


Both, yes.  We should give the decl(s) a new type rather than modify 
their existing types.



2) What all do you include in "modifying existing types?"  A number
of attribute handlers unconditionally modify the type of a *NODE
without first testing ATTR_FLAG_TYPE_IN_PLACE (e.g., by setting
TYPE_USER_ALIGN, or TREE_USED).  Are those modifications safe?  If
they are, what makes the difference between those and the code above?


Those also seem unsafe.


3) Is the restriction on "modifying existing types" specific to
the C++ front end or a general one that applies to the whole C/C++
family?  (I've only seen a failure in the C++ FE.)


It's a general correctness issue, the C++ front end just catches the 
issues better.  If you have two declarations with the same type, and 
then you give one an attribute, it shouldn't give that same attribute to 
the other declaration that shared the type.



4) What exactly does "work harder" mean and how do I test it?  Are
you suggesting to call build_type_attribute_variant (or maybe
build_variant_type_copy like the existing handlers do) to replace
the type?  Or are you implying that unless I need to replace
the existing type  I should avoid modifying the TYPE's
TYPE_ATTRIBUTES and instead work with a copy of the attribute chain?


We can't modify the existing attribute chain; if we need to remove an 
attribute from the middle, it must be by copying all the list nodes 
before the attribute.



These restrictions and the lack of enforcement (only in the C++ FE)
make nontrivial handlers extremely fragile.  Unless the restrictions
are entirely C++-specific I would really like to add assertions to
decl_attributes to catch these problems earlier (and in C as well).


Sounds good.


Either way, I also want to add test cases to exercise them.  But
I need to understand them first (i.e., get answers to the questions
above).  Nothing I've tried so far has triggered a problem due to
the code you point out above.

Martin





Re: [PATCH] avoid modifying type in attribute access handler [PR94098]

2020-03-26 Thread Martin Sebor via Gcc-patches

I have removed both calls to remove_attribute because with the changes
to calls.c they are no longer needed.  The revised patch is attached.
I will plan to commit it tomorrow unless there's something else.

In hindsight, I think changing the attribute from the human-readable
form to the internal, condensed, form was a mistake (it was done to
address concerns about the costs of parsing the attribute raised
during code review).  I don't want to make further changes here now
but I will plan to revisit it in stage 1.

I would still appreciate answers to the questions below so I can work
on beefing up the verification in decl_attributes (also in stage 1).

Thanks
Martin

On 3/26/20 2:57 PM, Martin Sebor wrote:

On 3/25/20 3:56 PM, Jason Merrill wrote:

On 3/16/20 4:41 PM, Martin Sebor wrote:

The recent fix to avoid modifying in place the type argument in
handle_access_attribute (PR 92721) was incomplete and didn't fully
resolve the problem (an ICE in the C++ front-end).  The attached
patch removes the remaining modification that causes the ICE.  In
addition, the change adjusts checking calls to functions declared
with the attribute to scan all its instances.

The attached patch was tested on x86_64-linux.



   attrs = remove_attribute (IDENTIFIER_POINTER (name), attrs);


This unchanged line can still modify existing types; I think if attrs 
already has a matching attribute you need to work harder to replace it.


I have a number of questions.

1) There are two instances of the call above in the code (only one in
the context of the patch).  Are you referring specifically to the one
in the patch or to both?  The one in the patch manipulates the type
attributes of the last DECL (NODE[1]).  The other one, attributes of
the current type (NODE[0]).  Are both a problem?

2) What all do you include in "modifying existing types?"  A number
of attribute handlers unconditionally modify the type of a *NODE
without first testing ATTR_FLAG_TYPE_IN_PLACE (e.g., by setting
TYPE_USER_ALIGN, or TREE_USED).  Are those modifications safe?  If
they are, what makes the difference between those and the code above?

3) Is the restriction on "modifying existing types" specific to
the C++ front end or a general one that applies to the whole C/C++
family?  (I've only seen a failure in the C++ FE.)

4) What exactly does "work harder" mean and how do I test it?  Are
you suggesting to call build_type_attribute_variant (or maybe
build_variant_type_copy like the existing handlers do) to replace
the type?  Or are you implying that unless I need to replace
the existing type  I should avoid modifying the TYPE's
TYPE_ATTRIBUTES and instead work with a copy of the attribute chain?

These restrictions and the lack of enforcement (only in the C++ FE)
make nontrivial handlers extremely fragile.  Unless the restrictions
are entirely C++-specific I would really like to add assertions to
decl_attributes to catch these problems earlier (and in C as well).
Either way, I also want to add test cases to exercise them.  But
I need to understand them first (i.e., get answers to the questions
above).  Nothing I've tried so far has triggered a problem due to
the code you point out above.

Martin


PR c++/94098 - ICE on attribute access redeclaration

gcc/c-family/ChangeLog:

	PR c++/94098
	* c-attribs.c (handle_access_attribute): Avoid setting TYPE_ATTRIBUTES
	here.

gcc/ChangeLog:

	PR c++/94098
	* calls.c (init_attr_rdwr_indices): Iterate over all access attributes.

gcc/testsuite/ChangeLog:

	PR c++/94098
	* g++.dg/ext/attr-access-2.C: New test.

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 9abf81d0248..f30158a258b 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -4182,7 +4182,6 @@ handle_access_attribute (tree *node, tree name, tree args,
 
   /* Replace any existing access attribute specification with
  the concatenation above.  */
-  attrs = remove_attribute (IDENTIFIER_POINTER (name), attrs);
   new_attrs = tree_cons (NULL_TREE, new_attrs, NULL_TREE);
   new_attrs = tree_cons (name, new_attrs, attrs);
 
@@ -4190,15 +4189,12 @@ handle_access_attribute (tree *node, tree name, tree args,
 {
   /* Repeat for the previously declared type.  */
   attrs = TYPE_ATTRIBUTES (TREE_TYPE (node[1]));
-  tree new_attrs
-	= append_access_attrs (node[1], attrs, attrstr, code, idxs);
-  if (!new_attrs)
+  tree attrs1 = append_access_attrs (node[1], attrs, attrstr, code, idxs);
+  if (!attrs1)
 	return NULL_TREE;
 
-  attrs = remove_attribute (IDENTIFIER_POINTER (name), attrs);
-  new_attrs = tree_cons (NULL_TREE, new_attrs, NULL_TREE);
-  new_attrs = tree_cons (name, new_attrs, attrs);
-  TYPE_ATTRIBUTES (TREE_TYPE (node[1])) = new_attrs;
+  attrs1 = tree_cons (NULL_TREE, attrs1, NULL_TREE);
+  new_attrs = tree_cons (name, attrs1, attrs);
 }
 
   /* Recursively call self to "replace" the documented/external form
diff --git a/gcc/calls.c b/gcc/calls.c
index 

Re: [PATCH] avoid modifying type in attribute access handler [PR94098]

2020-03-26 Thread Martin Sebor via Gcc-patches

On 3/25/20 3:56 PM, Jason Merrill wrote:

On 3/16/20 4:41 PM, Martin Sebor wrote:

The recent fix to avoid modifying in place the type argument in
handle_access_attribute (PR 92721) was incomplete and didn't fully
resolve the problem (an ICE in the C++ front-end).  The attached
patch removes the remaining modification that causes the ICE.  In
addition, the change adjusts checking calls to functions declared
with the attribute to scan all its instances.

The attached patch was tested on x86_64-linux.



   attrs = remove_attribute (IDENTIFIER_POINTER (name), attrs);


This unchanged line can still modify existing types; I think if attrs 
already has a matching attribute you need to work harder to replace it.


I have a number of questions.

1) There are two instances of the call above in the code (only one in
the context of the patch).  Are you referring specifically to the one
in the patch or to both?  The one in the patch manipulates the type
attributes of the last DECL (NODE[1]).  The other one, attributes of
the current type (NODE[0]).  Are both a problem?

2) What all do you include in "modifying existing types?"  A number
of attribute handlers unconditionally modify the type of a *NODE
without first testing ATTR_FLAG_TYPE_IN_PLACE (e.g., by setting
TYPE_USER_ALIGN, or TREE_USED).  Are those modifications safe?  If
they are, what makes the difference between those and the code above?

3) Is the restriction on "modifying existing types" specific to
the C++ front end or a general one that applies to the whole C/C++
family?  (I've only seen a failure in the C++ FE.)

4) What exactly does "work harder" mean and how do I test it?  Are
you suggesting to call build_type_attribute_variant (or maybe
build_variant_type_copy like the existing handlers do) to replace
the type?  Or are you implying that unless I need to replace
the existing type  I should avoid modifying the TYPE's
TYPE_ATTRIBUTES and instead work with a copy of the attribute chain?

These restrictions and the lack of enforcement (only in the C++ FE)
make nontrivial handlers extremely fragile.  Unless the restrictions
are entirely C++-specific I would really like to add assertions to
decl_attributes to catch these problems earlier (and in C as well).
Either way, I also want to add test cases to exercise them.  But
I need to understand them first (i.e., get answers to the questions
above).  Nothing I've tried so far has triggered a problem due to
the code you point out above.

Martin


Re: [PATCH] avoid modifying type in attribute access handler [PR94098]

2020-03-25 Thread Jason Merrill via Gcc-patches

On 3/16/20 4:41 PM, Martin Sebor wrote:

The recent fix to avoid modifying in place the type argument in
handle_access_attribute (PR 92721) was incomplete and didn't fully
resolve the problem (an ICE in the C++ front-end).  The attached
patch removes the remaining modification that causes the ICE.  In
addition, the change adjusts checking calls to functions declared
with the attribute to scan all its instances.

The attached patch was tested on x86_64-linux.



   attrs = remove_attribute (IDENTIFIER_POINTER (name), attrs);


This unchanged line can still modify existing types; I think if attrs 
already has a matching attribute you need to work harder to replace it.


Jason



Re: [PATCH] avoid modifying type in attribute access handler [PR94098]

2020-03-25 Thread Martin Sebor via Gcc-patches

Thanks for the suggestion to use type_cache_hasher::equal.  I'll
revisit the idea of adding an assertion to decl_attribute in
stage 1 and will look into using it there.

Until then, unless you or anyone else has any concerns with
the original patch I will plan to commit tomorrow:
  https://gcc.gnu.org/pipermail/gcc-patches/2020-March/542098.html

Martin

On 3/18/20 10:10 PM, Jason Merrill wrote:

On 3/18/20 6:04 PM, Martin Sebor wrote:

On 3/16/20 3:10 PM, Jason Merrill wrote:

On 3/16/20 4:41 PM, Martin Sebor wrote:

The recent fix to avoid modifying in place the type argument in
handle_access_attribute (PR 92721) was incomplete and didn't fully
resolve the problem (an ICE in the C++ front-end).  The attached
patch removes the remaining modification that causes the ICE.  In
addition, the change adjusts checking calls to functions declared
with the attribute to scan all its instances.

The attached patch was tested on x86_64-linux.

I'm puzzled that the ICE only triggers in the C++ front-end and not
also in C.  The code that issues the internal error is in comptypes()
in cp/typeck.c and has this comment:

 /* The two types are structurally equivalent, but their
    canonical types were different. This is a failure of the
    canonical type propagation code.*/
 internal_error
   ("canonical types differ for identical types %qT and %qT",
    t1, t2);

What is "the canonical type propagation code" it refers to?


Generally, code that makes sure that TYPE_CANONICAL equality is 
equivalent to type identity, not any one specific place.



Is it valid to modify the type in an attribute handler


Only if (flags & ATTR_FLAG_TYPE_IN_PLACE).


If not, and if modifying a type in place is not valid I'd expect
decl_attributes to enforce it.  I looked for other attribute handlers
to see if any also modify the type argument in place (by adding or
removing attributes) but couldn't really find any.  So if it is
invalid I'd like to add such an assertion (probably for GCC 11) but
before I do I want to make sure I'm not missing something.


Generally messing with _ATTRIBUTES happens in decl_attributes: 
changing it directly if it's a DECL or ATTR_FLAG_TYPE_IN_PLACE, 
otherwise using build_type_attribute_variant.  If you need to do it 
in your handler, you should follow the same pattern.


That's the conclusion I came to as well, but thanks for confirming
it.  With the patch I don't need to make the change but since it's
not obvious that it's a no-no and since it's apparently only detected
under very special conditions I'm wondering is if it's possible to
detect it more reliably than only in C++ comptypes.  The trouble is
that I don't exactly what is allowed and what isn't and what to look
for to tell if the handler did something that's not allowed.

The C++ ICE triggered because the redeclared function's type is
considered the same as the original (structural_comptypes()
returns true) but the declarations' canonical types are different.
structural_comptypes() is C++-specific and I don't know what
alternative to call in the middle-end to compare the types and
get the equivalent result.


I think type_cache_hasher::equal is the closest, but it looks like it 
doesn't check everything.



PS As a data point, I found just two attribute handlers in
c-attribs.c that modify a type in place: one for attribute const
and the other noreturn.  They both do it for function pointers
to set the 'const' and 'noreturn' bits on the pointed to types,
and by calling build_type_variant.


Hmm, yes, that does sound like a bug.

Jason





Re: [PATCH] avoid modifying type in attribute access handler [PR94098]

2020-03-18 Thread Jason Merrill via Gcc-patches

On 3/18/20 6:04 PM, Martin Sebor wrote:

On 3/16/20 3:10 PM, Jason Merrill wrote:

On 3/16/20 4:41 PM, Martin Sebor wrote:

The recent fix to avoid modifying in place the type argument in
handle_access_attribute (PR 92721) was incomplete and didn't fully
resolve the problem (an ICE in the C++ front-end).  The attached
patch removes the remaining modification that causes the ICE.  In
addition, the change adjusts checking calls to functions declared
with the attribute to scan all its instances.

The attached patch was tested on x86_64-linux.

I'm puzzled that the ICE only triggers in the C++ front-end and not
also in C.  The code that issues the internal error is in comptypes()
in cp/typeck.c and has this comment:

 /* The two types are structurally equivalent, but their
    canonical types were different. This is a failure of the
    canonical type propagation code.*/
 internal_error
   ("canonical types differ for identical types %qT and %qT",
    t1, t2);

What is "the canonical type propagation code" it refers to?


Generally, code that makes sure that TYPE_CANONICAL equality is 
equivalent to type identity, not any one specific place.



Is it valid to modify the type in an attribute handler


Only if (flags & ATTR_FLAG_TYPE_IN_PLACE).


If not, and if modifying a type in place is not valid I'd expect
decl_attributes to enforce it.  I looked for other attribute handlers
to see if any also modify the type argument in place (by adding or
removing attributes) but couldn't really find any.  So if it is
invalid I'd like to add such an assertion (probably for GCC 11) but
before I do I want to make sure I'm not missing something.


Generally messing with _ATTRIBUTES happens in decl_attributes: 
changing it directly if it's a DECL or ATTR_FLAG_TYPE_IN_PLACE, 
otherwise using build_type_attribute_variant.  If you need to do it in 
your handler, you should follow the same pattern.


That's the conclusion I came to as well, but thanks for confirming
it.  With the patch I don't need to make the change but since it's
not obvious that it's a no-no and since it's apparently only detected
under very special conditions I'm wondering is if it's possible to
detect it more reliably than only in C++ comptypes.  The trouble is
that I don't exactly what is allowed and what isn't and what to look
for to tell if the handler did something that's not allowed.

The C++ ICE triggered because the redeclared function's type is
considered the same as the original (structural_comptypes()
returns true) but the declarations' canonical types are different.
structural_comptypes() is C++-specific and I don't know what
alternative to call in the middle-end to compare the types and
get the equivalent result.


I think type_cache_hasher::equal is the closest, but it looks like it 
doesn't check everything.



PS As a data point, I found just two attribute handlers in
c-attribs.c that modify a type in place: one for attribute const
and the other noreturn.  They both do it for function pointers
to set the 'const' and 'noreturn' bits on the pointed to types,
and by calling build_type_variant.


Hmm, yes, that does sound like a bug.

Jason



Re: [PATCH] avoid modifying type in attribute access handler [PR94098]

2020-03-18 Thread Martin Sebor via Gcc-patches

On 3/16/20 3:10 PM, Jason Merrill wrote:

On 3/16/20 4:41 PM, Martin Sebor wrote:

The recent fix to avoid modifying in place the type argument in
handle_access_attribute (PR 92721) was incomplete and didn't fully
resolve the problem (an ICE in the C++ front-end).  The attached
patch removes the remaining modification that causes the ICE.  In
addition, the change adjusts checking calls to functions declared
with the attribute to scan all its instances.

The attached patch was tested on x86_64-linux.

I'm puzzled that the ICE only triggers in the C++ front-end and not
also in C.  The code that issues the internal error is in comptypes()
in cp/typeck.c and has this comment:

 /* The two types are structurally equivalent, but their
    canonical types were different. This is a failure of the
    canonical type propagation code.*/
 internal_error
   ("canonical types differ for identical types %qT and %qT",
    t1, t2);

What is "the canonical type propagation code" it refers to?


Generally, code that makes sure that TYPE_CANONICAL equality is 
equivalent to type identity, not any one specific place.



Is it valid to modify the type in an attribute handler


Only if (flags & ATTR_FLAG_TYPE_IN_PLACE).


If not, and if modifying a type in place is not valid I'd expect
decl_attributes to enforce it.  I looked for other attribute handlers
to see if any also modify the type argument in place (by adding or
removing attributes) but couldn't really find any.  So if it is
invalid I'd like to add such an assertion (probably for GCC 11) but
before I do I want to make sure I'm not missing something.


Generally messing with _ATTRIBUTES happens in decl_attributes: changing 
it directly if it's a DECL or ATTR_FLAG_TYPE_IN_PLACE, otherwise using 
build_type_attribute_variant.  If you need to do it in your handler, you 
should follow the same pattern.


That's the conclusion I came to as well, but thanks for confirming
it.  With the patch I don't need to make the change but since it's
not obvious that it's a no-no and since it's apparently only detected
under very special conditions I'm wondering is if it's possible to
detect it more reliably than only in C++ comptypes.  The trouble is
that I don't exactly what is allowed and what isn't and what to look
for to tell if the handler did something that's not allowed.

The C++ ICE triggered because the redeclared function's type is
considered the same as the original (structural_comptypes()
returns true) but the declarations' canonical types are different.
structural_comptypes() is C++-specific and I don't know what
alternative to call in the middle-end to compare the types and
get the equivalent result.

Martin

PS As a data point, I found just two attribute handlers in
c-attribs.c that modify a type in place: one for attribute const
and the other noreturn.  They both do it for function pointers
to set the 'const' and 'noreturn' bits on the pointed to types,
and by calling build_type_variant.


Re: [PATCH] avoid modifying type in attribute access handler [PR94098]

2020-03-16 Thread Jason Merrill via Gcc-patches

On 3/16/20 4:41 PM, Martin Sebor wrote:

The recent fix to avoid modifying in place the type argument in
handle_access_attribute (PR 92721) was incomplete and didn't fully
resolve the problem (an ICE in the C++ front-end).  The attached
patch removes the remaining modification that causes the ICE.  In
addition, the change adjusts checking calls to functions declared
with the attribute to scan all its instances.

The attached patch was tested on x86_64-linux.

I'm puzzled that the ICE only triggers in the C++ front-end and not
also in C.  The code that issues the internal error is in comptypes()
in cp/typeck.c and has this comment:

     /* The two types are structurally equivalent, but their
    canonical types were different. This is a failure of the
    canonical type propagation code.*/
     internal_error
   ("canonical types differ for identical types %qT and %qT",
    t1, t2);

What is "the canonical type propagation code" it refers to?


Generally, code that makes sure that TYPE_CANONICAL equality is 
equivalent to type identity, not any one specific place.



Is it valid to modify the type in an attribute handler


Only if (flags & ATTR_FLAG_TYPE_IN_PLACE).


If not, and if modifying a type in place is not valid I'd expect
decl_attributes to enforce it.  I looked for other attribute handlers
to see if any also modify the type argument in place (by adding or
removing attributes) but couldn't really find any.  So if it is
invalid I'd like to add such an assertion (probably for GCC 11) but
before I do I want to make sure I'm not missing something.


Generally messing with _ATTRIBUTES happens in decl_attributes: changing 
it directly if it's a DECL or ATTR_FLAG_TYPE_IN_PLACE, otherwise using 
build_type_attribute_variant.  If you need to do it in your handler, you 
should follow the same pattern.


Jason