diff mbox series

[v2] C-family : Add attribute 'unavailable'.

Message ID 1DC1B5BE-1FCE-4E55-9F91-96A475D0CA84@sandoe.co.uk
State New
Headers show
Series [v2] C-family : Add attribute 'unavailable'. | expand

Commit Message

Iain Sandoe Nov. 10, 2020, 7:38 p.m. UTC
Hi Jospeh,

Joseph Myers <joseph@codesourcery.com> wrote:

> This patch seems to be missing documentation for the new attribute in 
> extend.texi.

Apologies, for that omission, revised patch includes the documentation and
also addresses Richi’s comments.

documentation patch tested with “make pdf” and visual inspection of the output.

OK now?
thanks
Iain

—— commit message.

If an interface is marked 'deprecated' then, presumably, at some point it
will be withdrawn and no longer available.  The 'unavailable' attribute
makes it possible to mark up interfaces to indicate this status.  It is used
quite extensively in some codebases where a single set of headers can be used
to permit code generation for multiple system versions.

From a configuration perspective, it also allows a compile test to determine
that an interface is missing - rather than requiring a link test.

The implementation follows the pattern of attribute deprecated, but produces
an error (where deprecation produces a warning).

This attribute has been implemented in clang for some years.

gcc/c-family/ChangeLog:

	* c-attribs.c (handle_unavailable_attribute): New.

gcc/c/ChangeLog:

	* c-decl.c (enum deprecated_states): Add unavailable state.
	(merge_decls): Copy unavailability.
	(quals_from_declspecs): Handle unavailable case.
	(start_decl): Amend the logic handling suppression of nested
	deprecation states to include unavailability.
	(smallest_type_quals_location): Amend comment.
	(grokdeclarator): Handle the unavailable deprecation state.
	(declspecs_add_type): Set TREE_UNAVAILABLE from the decl specs.
	* c-tree.h (struct c_declspecs): Add unavailable_p.
	* c-typeck.c (build_component_ref): Handle unavailability.
	(build_external_ref): Likewise.

gcc/cp/ChangeLog:

	* call.c (build_over_call): Handle unavailable state in addition to
	deprecation.
	* class.c (type_build_ctor_call): Likewise.
	(type_build_dtor_call): Likewise.
	* cp-tree.h: Rename cp_warn_deprecated_use to
	cp_handle_deprecated_or_unavailable.
	* decl.c (duplicate_decls): Merge unavailability.
	(grokdeclarator): Handle unavailability in addition to deprecation.
	(type_is_unavailable): New.
	(grokparms): Handle unavailability in addition to deprecation.
	* decl.h (enum deprecated_states): Add
	UNAVAILABLE_DEPRECATED_SUPPRESS.
	* decl2.c (cplus_decl_attributes): Propagate unavailability to
	templates.
	(cp_warn_deprecated_use): Rename to ...
	(cp_handle_deprecated_or_unavailable): ... this and amend to handle
	the unavailable case. It remains a warning in the case of deprecation
	but becomes an error in the case of unavailability.
	(cp_warn_deprecated_use_scopes): Handle unavailability.
	(mark_used): Likewise.
	* parser.c (cp_parser_template_name): Likewise.
	(cp_parser_template_argument): Likewise.
	(cp_parser_parameter_declaration_list): Likewise.
	* typeck.c (build_class_member_access_expr): Likewise.
	(finish_class_member_access_expr): Likewise.
	* typeck2.c (build_functional_cast_1): Likewise.

gcc/ChangeLog:

	* doc/extend.texi: Document unavailable attribute.
	* print-tree.c (print_node): Handle unavailable attribute.
	* tree-core.h (struct tree_base): Add a bit to carry unavailability.
	* tree.c (error_unavailable_use): New.
	* tree.h (TREE_UNAVAILABLE): New.
	(error_unavailable_use): New.

gcc/objc/ChangeLog:

	* objc-act.c (objc_add_property_declaration): Register unavailable
	attribute.
	(maybe_make_artificial_property_decl): Set available.
	(objc_maybe_build_component_ref): Generalise to the method prototype
	to count availability.
	(objc_build_class_component_ref): Likewise.
	(build_private_template): Likewise.
	(objc_decl_method_attributes): Handle unavailable attribute.
	(lookup_method_in_hash_lists): Amend comments.
	(objc_finish_message_expr): Handle unavailability in addition to
	deprecation.
	(start_class): Likewise.
	(finish_class): Likewise.
	(lookup_protocol): Likewise.
	(objc_declare_protocol): Likewise.
	(start_protocol): Register unavailable attribute.
	(really_start_method): Likewise.
	(objc_gimplify_property_ref): Emit error on encountering an
	unavailable entity (and a warning for a deprecated one).

gcc/testsuite/ChangeLog:

	* g++.dg/ext/attr-unavailable-1.C: New test.
	* g++.dg/ext/attr-unavailable-2.C: New test.
	* g++.dg/ext/attr-unavailable-3.C: New test.
	* g++.dg/ext/attr-unavailable-4.C: New test.
	* g++.dg/ext/attr-unavailable-5.C: New test.
	* g++.dg/ext/attr-unavailable-6.C: New test.
	* g++.dg/ext/attr-unavailable-7.C: New test.
	* g++.dg/ext/attr-unavailable-8.C: New test.
	* g++.dg/ext/attr-unavailable-9.C: New test.
	* gcc.dg/attr-unavailable-1.c: New test.
	* gcc.dg/attr-unavailable-2.c: New test.
	* gcc.dg/attr-unavailable-3.c: New test.
	* gcc.dg/attr-unavailable-4.c: New test.
	* gcc.dg/attr-unavailable-5.c: New test.
	* gcc.dg/attr-unavailable-6.c: New test.
	* obj-c++.dg/attributes/method-unavailable-1.mm: New test.
	* obj-c++.dg/attributes/method-unavailable-2.mm: New test.
	* obj-c++.dg/attributes/method-unavailable-3.mm: New test.
	* obj-c++.dg/property/at-property-unavailable-1.mm: New test.
	* obj-c++.dg/property/at-property-unavailable-2.mm: New test.
	* obj-c++.dg/property/dotsyntax-unavailable-1.mm: New test.
	* objc.dg/attributes/method-unavailable-1.m: New test.
	* objc.dg/attributes/method-unavailable-2.m: New test.
	* objc.dg/attributes/method-unavailable-3.m: New test.
	* objc.dg/property/at-property-unavailable-1.m: New test.
	* objc.dg/property/at-property-unavailable-2.m: New test.
	* objc.dg/property/dotsyntax-unavailable-1.m: New test.
---
 gcc/c-family/c-attribs.c                      |  69 +++++++++++
 gcc/c/c-decl.c                                |  39 ++++--
 gcc/c/c-tree.h                                |   2 +
 gcc/c/c-typeck.c                              |   8 +-
 gcc/cp/call.c                                 |   4 +-
 gcc/cp/class.c                                |   2 +
 gcc/cp/cp-tree.h                              |   2 +-
 gcc/cp/decl.c                                 |  66 ++++++++--
 gcc/cp/decl.h                                 |   3 +-
 gcc/cp/decl2.c                                |  58 +++++++--
 gcc/cp/parser.c                               |  32 +++--
 gcc/cp/typeck.c                               |   9 +-
 gcc/cp/typeck2.c                              |   2 +-
 gcc/doc/extend.texi                           |  46 +++++++
 gcc/objc/objc-act.c                           |  81 +++++++++----
 gcc/print-tree.c                              |   2 +
 gcc/testsuite/g++.dg/ext/attr-unavailable-1.C | 113 ++++++++++++++++++
 gcc/testsuite/g++.dg/ext/attr-unavailable-2.C |  10 ++
 gcc/testsuite/g++.dg/ext/attr-unavailable-3.C |  14 +++
 gcc/testsuite/g++.dg/ext/attr-unavailable-4.C |  11 ++
 gcc/testsuite/g++.dg/ext/attr-unavailable-5.C |   6 +
 gcc/testsuite/g++.dg/ext/attr-unavailable-6.C | 110 +++++++++++++++++
 gcc/testsuite/g++.dg/ext/attr-unavailable-7.C |  19 +++
 gcc/testsuite/g++.dg/ext/attr-unavailable-8.C |  17 +++
 gcc/testsuite/g++.dg/ext/attr-unavailable-9.C |  17 +++
 gcc/testsuite/gcc.dg/attr-unavailable-1.c     |  88 ++++++++++++++
 gcc/testsuite/gcc.dg/attr-unavailable-2.c     |   6 +
 gcc/testsuite/gcc.dg/attr-unavailable-3.c     |  10 ++
 gcc/testsuite/gcc.dg/attr-unavailable-4.c     |  88 ++++++++++++++
 gcc/testsuite/gcc.dg/attr-unavailable-5.c     |   6 +
 gcc/testsuite/gcc.dg/attr-unavailable-6.c     |  11 ++
 .../attributes/method-unavailable-1.mm        |  34 ++++++
 .../attributes/method-unavailable-2.mm        |  24 ++++
 .../attributes/method-unavailable-3.mm        |  22 ++++
 .../property/at-property-unavailable-1.mm     |  38 ++++++
 .../property/at-property-unavailable-2.mm     |  26 ++++
 .../property/dotsyntax-unavailable-1.mm       |  42 +++++++
 .../objc.dg/attributes/method-unavailable-1.m |  34 ++++++
 .../objc.dg/attributes/method-unavailable-2.m |  24 ++++
 .../objc.dg/attributes/method-unavailable-3.m |  22 ++++
 .../property/at-property-unavailable-1.m      |  38 ++++++
 .../property/at-property-unavailable-2.m      |  26 ++++
 .../property/dotsyntax-unavailable-1.m        |  42 +++++++
 gcc/tree-core.h                               |  10 +-
 gcc/tree.c                                    |  72 +++++++++++
 gcc/tree.h                                    |   6 +
 46 files changed, 1347 insertions(+), 64 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/attr-unavailable-1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/attr-unavailable-2.C
 create mode 100644 gcc/testsuite/g++.dg/ext/attr-unavailable-3.C
 create mode 100644 gcc/testsuite/g++.dg/ext/attr-unavailable-4.C
 create mode 100644 gcc/testsuite/g++.dg/ext/attr-unavailable-5.C
 create mode 100644 gcc/testsuite/g++.dg/ext/attr-unavailable-6.C
 create mode 100644 gcc/testsuite/g++.dg/ext/attr-unavailable-7.C
 create mode 100644 gcc/testsuite/g++.dg/ext/attr-unavailable-8.C
 create mode 100644 gcc/testsuite/g++.dg/ext/attr-unavailable-9.C
 create mode 100644 gcc/testsuite/gcc.dg/attr-unavailable-1.c
 create mode 100644 gcc/testsuite/gcc.dg/attr-unavailable-2.c
 create mode 100644 gcc/testsuite/gcc.dg/attr-unavailable-3.c
 create mode 100644 gcc/testsuite/gcc.dg/attr-unavailable-4.c
 create mode 100644 gcc/testsuite/gcc.dg/attr-unavailable-5.c
 create mode 100644 gcc/testsuite/gcc.dg/attr-unavailable-6.c
 create mode 100644 gcc/testsuite/obj-c++.dg/attributes/method-unavailable-1.mm
 create mode 100644 gcc/testsuite/obj-c++.dg/attributes/method-unavailable-2.mm
 create mode 100644 gcc/testsuite/obj-c++.dg/attributes/method-unavailable-3.mm
 create mode 100644 gcc/testsuite/obj-c++.dg/property/at-property-unavailable-1.mm
 create mode 100644 gcc/testsuite/obj-c++.dg/property/at-property-unavailable-2.mm
 create mode 100644 gcc/testsuite/obj-c++.dg/property/dotsyntax-unavailable-1.mm
 create mode 100644 gcc/testsuite/objc.dg/attributes/method-unavailable-1.m
 create mode 100644 gcc/testsuite/objc.dg/attributes/method-unavailable-2.m
 create mode 100644 gcc/testsuite/objc.dg/attributes/method-unavailable-3.m
 create mode 100644 gcc/testsuite/objc.dg/property/at-property-unavailable-1.m
 create mode 100644 gcc/testsuite/objc.dg/property/at-property-unavailable-2.m
 create mode 100644 gcc/testsuite/objc.dg/property/dotsyntax-unavailable-1.m

Comments

Iain Sandoe Nov. 27, 2020, 8:49 p.m. UTC | #1
Hi

I believe this version addressed Joseph’s and Richard’s comment, but it
C++ review.

thanks
Iain



Iain Sandoe <iain@sandoe.co.uk> wrote:

> Joseph Myers <joseph@codesourcery.com> wrote:
>
>> This patch seems to be missing documentation for the new attribute in
>> extend.texi.
>
> Apologies, for that omission, revised patch includes the documentation and
> also addresses Richi’s comments.
>
> documentation patch tested with “make pdf” and visual inspection of the  
> output.
>
> OK now?
> thanks
> Iain
>
> —— commit message.
>
> If an interface is marked 'deprecated' then, presumably, at some point it
> will be withdrawn and no longer available.  The 'unavailable' attribute
> makes it possible to mark up interfaces to indicate this status.  It is  
> used
> quite extensively in some codebases where a single set of headers can be  
> used
> to permit code generation for multiple system versions.
>
> From a configuration perspective, it also allows a compile test to  
> determine
> that an interface is missing - rather than requiring a link test.
>
> The implementation follows the pattern of attribute deprecated, but  
> produces
> an error (where deprecation produces a warning).
>
> This attribute has been implemented in clang for some years.
>
> gcc/c-family/ChangeLog:
>
> 	* c-attribs.c (handle_unavailable_attribute): New.
>
> gcc/c/ChangeLog:
>
> 	* c-decl.c (enum deprecated_states): Add unavailable state.
> 	(merge_decls): Copy unavailability.
> 	(quals_from_declspecs): Handle unavailable case.
> 	(start_decl): Amend the logic handling suppression of nested
> 	deprecation states to include unavailability.
> 	(smallest_type_quals_location): Amend comment.
> 	(grokdeclarator): Handle the unavailable deprecation state.
> 	(declspecs_add_type): Set TREE_UNAVAILABLE from the decl specs.
> 	* c-tree.h (struct c_declspecs): Add unavailable_p.
> 	* c-typeck.c (build_component_ref): Handle unavailability.
> 	(build_external_ref): Likewise.
>
> gcc/cp/ChangeLog:
>
> 	* call.c (build_over_call): Handle unavailable state in addition to
> 	deprecation.
> 	* class.c (type_build_ctor_call): Likewise.
> 	(type_build_dtor_call): Likewise.
> 	* cp-tree.h: Rename cp_warn_deprecated_use to
> 	cp_handle_deprecated_or_unavailable.
> 	* decl.c (duplicate_decls): Merge unavailability.
> 	(grokdeclarator): Handle unavailability in addition to deprecation.
> 	(type_is_unavailable): New.
> 	(grokparms): Handle unavailability in addition to deprecation.
> 	* decl.h (enum deprecated_states): Add
> 	UNAVAILABLE_DEPRECATED_SUPPRESS.
> 	* decl2.c (cplus_decl_attributes): Propagate unavailability to
> 	templates.
> 	(cp_warn_deprecated_use): Rename to ...
> 	(cp_handle_deprecated_or_unavailable): ... this and amend to handle
> 	the unavailable case. It remains a warning in the case of deprecation
> 	but becomes an error in the case of unavailability.
> 	(cp_warn_deprecated_use_scopes): Handle unavailability.
> 	(mark_used): Likewise.
> 	* parser.c (cp_parser_template_name): Likewise.
> 	(cp_parser_template_argument): Likewise.
> 	(cp_parser_parameter_declaration_list): Likewise.
> 	* typeck.c (build_class_member_access_expr): Likewise.
> 	(finish_class_member_access_expr): Likewise.
> 	* typeck2.c (build_functional_cast_1): Likewise.
>
> gcc/ChangeLog:
>
> 	* doc/extend.texi: Document unavailable attribute.
> 	* print-tree.c (print_node): Handle unavailable attribute.
> 	* tree-core.h (struct tree_base): Add a bit to carry unavailability.
> 	* tree.c (error_unavailable_use): New.
> 	* tree.h (TREE_UNAVAILABLE): New.
> 	(error_unavailable_use): New.
>
> gcc/objc/ChangeLog:
>
> 	* objc-act.c (objc_add_property_declaration): Register unavailable
> 	attribute.
> 	(maybe_make_artificial_property_decl): Set available.
> 	(objc_maybe_build_component_ref): Generalise to the method prototype
> 	to count availability.
> 	(objc_build_class_component_ref): Likewise.
> 	(build_private_template): Likewise.
> 	(objc_decl_method_attributes): Handle unavailable attribute.
> 	(lookup_method_in_hash_lists): Amend comments.
> 	(objc_finish_message_expr): Handle unavailability in addition to
> 	deprecation.
> 	(start_class): Likewise.
> 	(finish_class): Likewise.
> 	(lookup_protocol): Likewise.
> 	(objc_declare_protocol): Likewise.
> 	(start_protocol): Register unavailable attribute.
> 	(really_start_method): Likewise.
> 	(objc_gimplify_property_ref): Emit error on encountering an
> 	unavailable entity (and a warning for a deprecated one).
>
> gcc/testsuite/ChangeLog:
>
> 	* g++.dg/ext/attr-unavailable-1.C: New test.
> 	* g++.dg/ext/attr-unavailable-2.C: New test.
> 	* g++.dg/ext/attr-unavailable-3.C: New test.
> 	* g++.dg/ext/attr-unavailable-4.C: New test.
> 	* g++.dg/ext/attr-unavailable-5.C: New test.
> 	* g++.dg/ext/attr-unavailable-6.C: New test.
> 	* g++.dg/ext/attr-unavailable-7.C: New test.
> 	* g++.dg/ext/attr-unavailable-8.C: New test.
> 	* g++.dg/ext/attr-unavailable-9.C: New test.
> 	* gcc.dg/attr-unavailable-1.c: New test.
> 	* gcc.dg/attr-unavailable-2.c: New test.
> 	* gcc.dg/attr-unavailable-3.c: New test.
> 	* gcc.dg/attr-unavailable-4.c: New test.
> 	* gcc.dg/attr-unavailable-5.c: New test.
> 	* gcc.dg/attr-unavailable-6.c: New test.
> 	* obj-c++.dg/attributes/method-unavailable-1.mm: New test.
> 	* obj-c++.dg/attributes/method-unavailable-2.mm: New test.
> 	* obj-c++.dg/attributes/method-unavailable-3.mm: New test.
> 	* obj-c++.dg/property/at-property-unavailable-1.mm: New test.
> 	* obj-c++.dg/property/at-property-unavailable-2.mm: New test.
> 	* obj-c++.dg/property/dotsyntax-unavailable-1.mm: New test.
> 	* objc.dg/attributes/method-unavailable-1.m: New test.
> 	* objc.dg/attributes/method-unavailable-2.m: New test.
> 	* objc.dg/attributes/method-unavailable-3.m: New test.
> 	* objc.dg/property/at-property-unavailable-1.m: New test.
> 	* objc.dg/property/at-property-unavailable-2.m: New test.
> 	* objc.dg/property/dotsyntax-unavailable-1.m: New test.
> ---
> gcc/c-family/c-attribs.c                      |  69 +++++++++++
> gcc/c/c-decl.c                                |  39 ++++--
> gcc/c/c-tree.h                                |   2 +
> gcc/c/c-typeck.c                              |   8 +-
> gcc/cp/call.c                                 |   4 +-
> gcc/cp/class.c                                |   2 +
> gcc/cp/cp-tree.h                              |   2 +-
> gcc/cp/decl.c                                 |  66 ++++++++--
> gcc/cp/decl.h                                 |   3 +-
> gcc/cp/decl2.c                                |  58 +++++++--
> gcc/cp/parser.c                               |  32 +++--
> gcc/cp/typeck.c                               |   9 +-
> gcc/cp/typeck2.c                              |   2 +-
> gcc/doc/extend.texi                           |  46 +++++++
> gcc/objc/objc-act.c                           |  81 +++++++++----
> gcc/print-tree.c                              |   2 +
> gcc/testsuite/g++.dg/ext/attr-unavailable-1.C | 113 ++++++++++++++++++
> gcc/testsuite/g++.dg/ext/attr-unavailable-2.C |  10 ++
> gcc/testsuite/g++.dg/ext/attr-unavailable-3.C |  14 +++
> gcc/testsuite/g++.dg/ext/attr-unavailable-4.C |  11 ++
> gcc/testsuite/g++.dg/ext/attr-unavailable-5.C |   6 +
> gcc/testsuite/g++.dg/ext/attr-unavailable-6.C | 110 +++++++++++++++++
> gcc/testsuite/g++.dg/ext/attr-unavailable-7.C |  19 +++
> gcc/testsuite/g++.dg/ext/attr-unavailable-8.C |  17 +++
> gcc/testsuite/g++.dg/ext/attr-unavailable-9.C |  17 +++
> gcc/testsuite/gcc.dg/attr-unavailable-1.c     |  88 ++++++++++++++
> gcc/testsuite/gcc.dg/attr-unavailable-2.c     |   6 +
> gcc/testsuite/gcc.dg/attr-unavailable-3.c     |  10 ++
> gcc/testsuite/gcc.dg/attr-unavailable-4.c     |  88 ++++++++++++++
> gcc/testsuite/gcc.dg/attr-unavailable-5.c     |   6 +
> gcc/testsuite/gcc.dg/attr-unavailable-6.c     |  11 ++
> .../attributes/method-unavailable-1.mm        |  34 ++++++
> .../attributes/method-unavailable-2.mm        |  24 ++++
> .../attributes/method-unavailable-3.mm        |  22 ++++
> .../property/at-property-unavailable-1.mm     |  38 ++++++
> .../property/at-property-unavailable-2.mm     |  26 ++++
> .../property/dotsyntax-unavailable-1.mm       |  42 +++++++
> .../objc.dg/attributes/method-unavailable-1.m |  34 ++++++
> .../objc.dg/attributes/method-unavailable-2.m |  24 ++++
> .../objc.dg/attributes/method-unavailable-3.m |  22 ++++
> .../property/at-property-unavailable-1.m      |  38 ++++++
> .../property/at-property-unavailable-2.m      |  26 ++++
> .../property/dotsyntax-unavailable-1.m        |  42 +++++++
> gcc/tree-core.h                               |  10 +-
> gcc/tree.c                                    |  72 +++++++++++
> gcc/tree.h                                    |   6 +
> 46 files changed, 1347 insertions(+), 64 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/ext/attr-unavailable-1.C
> create mode 100644 gcc/testsuite/g++.dg/ext/attr-unavailable-2.C
> create mode 100644 gcc/testsuite/g++.dg/ext/attr-unavailable-3.C
> create mode 100644 gcc/testsuite/g++.dg/ext/attr-unavailable-4.C
> create mode 100644 gcc/testsuite/g++.dg/ext/attr-unavailable-5.C
> create mode 100644 gcc/testsuite/g++.dg/ext/attr-unavailable-6.C
> create mode 100644 gcc/testsuite/g++.dg/ext/attr-unavailable-7.C
> create mode 100644 gcc/testsuite/g++.dg/ext/attr-unavailable-8.C
> create mode 100644 gcc/testsuite/g++.dg/ext/attr-unavailable-9.C
> create mode 100644 gcc/testsuite/gcc.dg/attr-unavailable-1.c
> create mode 100644 gcc/testsuite/gcc.dg/attr-unavailable-2.c
> create mode 100644 gcc/testsuite/gcc.dg/attr-unavailable-3.c
> create mode 100644 gcc/testsuite/gcc.dg/attr-unavailable-4.c
> create mode 100644 gcc/testsuite/gcc.dg/attr-unavailable-5.c
> create mode 100644 gcc/testsuite/gcc.dg/attr-unavailable-6.c
> create mode 100644  
> gcc/testsuite/obj-c++.dg/attributes/method-unavailable-1.mm
> create mode 100644  
> gcc/testsuite/obj-c++.dg/attributes/method-unavailable-2.mm
> create mode 100644  
> gcc/testsuite/obj-c++.dg/attributes/method-unavailable-3.mm
> create mode 100644  
> gcc/testsuite/obj-c++.dg/property/at-property-unavailable-1.mm
> create mode 100644  
> gcc/testsuite/obj-c++.dg/property/at-property-unavailable-2.mm
> create mode 100644  
> gcc/testsuite/obj-c++.dg/property/dotsyntax-unavailable-1.mm
> create mode 100644 gcc/testsuite/objc.dg/attributes/method-unavailable-1.m
> create mode 100644 gcc/testsuite/objc.dg/attributes/method-unavailable-2.m
> create mode 100644 gcc/testsuite/objc.dg/attributes/method-unavailable-3.m
> create mode 100644  
> gcc/testsuite/objc.dg/property/at-property-unavailable-1.m
> create mode 100644  
> gcc/testsuite/objc.dg/property/at-property-unavailable-2.m
> create mode 100644 gcc/testsuite/objc.dg/property/dotsyntax-unavailable-1.m
>
> diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
> index f1680820ecd..067dfabe0a4 100644
> --- a/gcc/c-family/c-attribs.c
> +++ b/gcc/c-family/c-attribs.c
> @@ -120,6 +120,8 @@ static tree handle_pure_attribute (tree *, tree,  
> tree, int, bool *);
> static tree handle_tm_attribute (tree *, tree, tree, int, bool *);
> static tree handle_tm_wrap_attribute (tree *, tree, tree, int, bool *);
> static tree handle_novops_attribute (tree *, tree, tree, int, bool *);
> +static tree handle_unavailable_attribute (tree *, tree, tree, int,
> +					  bool *);
> static tree handle_vector_size_attribute (tree *, tree, tree, int,
> 					  bool *) ATTRIBUTE_NONNULL(3);
> static tree handle_nonnull_attribute (tree *, tree, tree, int, bool *);
> @@ -391,6 +393,8 @@ const struct attribute_spec  
> c_common_attribute_table[] =
> 			      handle_novops_attribute, NULL },
>   { "deprecated",             0, 1, false, false, false, false,
> 			      handle_deprecated_attribute, NULL },
> +  { "unavailable",            0, 1, false, false, false, false,
> +			      handle_unavailable_attribute, NULL },
>   { "vector_size",	      1, 1, false, true, false, true,
> 			      handle_vector_size_attribute, NULL },
>   { "visibility",	      1, 1, false, false, false, false,
> @@ -3737,6 +3741,71 @@ handle_deprecated_attribute (tree *node, tree name,
>   return NULL_TREE;
> }
>
> +/* Handle a "unavailable" attribute; arguments as in
> +   struct attribute_spec.handler.  */
> +
> +static tree
> +handle_unavailable_attribute (tree *node, tree name,
> +			     tree args, int flags,
> +			     bool *no_add_attrs)
> +{
> +  tree type = NULL_TREE;
> +  int warn = 0;
> +  tree what = NULL_TREE;
> +
> +  if (!args)
> +    *no_add_attrs = true;
> +  else if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
> +    {
> +      error ("the message attached to %<unavailable%> is not a string");
> +      *no_add_attrs = true;
> +    }
> +
> +  if (DECL_P (*node))
> +    {
> +      tree decl = *node;
> +      type = TREE_TYPE (decl);
> +
> +      if (TREE_CODE (decl) == TYPE_DECL
> +	  || TREE_CODE (decl) == PARM_DECL
> +	  || VAR_OR_FUNCTION_DECL_P (decl)
> +	  || TREE_CODE (decl) == FIELD_DECL
> +	  || TREE_CODE (decl) == CONST_DECL
> +	  || objc_method_decl (TREE_CODE (decl)))
> +	TREE_UNAVAILABLE (decl) = 1;
> +      else
> +	warn = 1;
> +    }
> +  else if (TYPE_P (*node))
> +    {
> +      if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
> +	*node = build_variant_type_copy (*node);
> +      TREE_UNAVAILABLE (*node) = 1;
> +      type = *node;
> +    }
> +  else
> +    warn = 1;
> +
> +  if (warn)
> +    {
> +      *no_add_attrs = true;
> +      if (type && TYPE_NAME (type))
> +	{
> +	  if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
> +	    what = TYPE_NAME (*node);
> +	  else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
> +		   && DECL_NAME (TYPE_NAME (type)))
> +	    what = DECL_NAME (TYPE_NAME (type));
> +	}
> +      if (what)
> +	warning (OPT_Wattributes, "%qE attribute ignored for %qE", name, what);
> +      else
> +	warning (OPT_Wattributes, "%qE attribute ignored", name);
> +    }
> +
> +  return NULL_TREE;
> +}
> +
> /* Return the "base" type from TYPE that is suitable to apply attribute
>    vector_size to by stripping arrays, function types, etc.  */
> static tree
> diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
> index f19c82c81dd..27c6f13e8f2 100644
> --- a/gcc/c/c-decl.c
> +++ b/gcc/c/c-decl.c
> @@ -70,13 +70,16 @@ enum decl_context
>   TYPENAME};			/* Typename (inside cast or sizeof)  */
>
> /* States indicating how grokdeclarator() should handle declspecs marked
> -   with __attribute__((deprecated)).  An object declared as
> -   __attribute__((deprecated)) suppresses warnings of uses of other
> -   deprecated items.  */
> +   with __attribute__((deprecated)) or __attribute__((unavailable)).
> +   An object declared as __attribute__((unavailable)) should suppress
> +   any reports of being declared  with unavailable or deprecated items.
> +   An object declared as __attribute__((deprecated)) should suppress
> +   warnings of uses of other deprecated items.  */
>
> enum deprecated_states {
>   DEPRECATED_NORMAL,
> -  DEPRECATED_SUPPRESS
> +  DEPRECATED_SUPPRESS,
> +  UNAVAILABLE_DEPRECATED_SUPPRESS
> };
>
>
> @@ -2630,6 +2633,10 @@ merge_decls (tree newdecl, tree olddecl, tree  
> newtype, tree oldtype)
>   if (TREE_DEPRECATED (newdecl))
>     TREE_DEPRECATED (olddecl) = 1;
>
> +  /* Merge unavailability.  */
> +  if (TREE_UNAVAILABLE (newdecl))
> +    TREE_UNAVAILABLE (olddecl) = 1;
> +
>   /* If a decl is in a system header and the other isn't, keep the one on the
>      system header. Otherwise, keep source location of definition rather than
>      declaration and of prototype rather than non-prototype unless that
> @@ -4868,6 +4875,7 @@ quals_from_declspecs (const struct c_declspecs  
> *specs)
> 	      && !specs->typedef_p
> 	      && !specs->explicit_signed_p
> 	      && !specs->deprecated_p
> +	      && !specs->unavailable_p
> 	      && !specs->long_p
> 	      && !specs->long_long_p
> 	      && !specs->short_p
> @@ -5070,9 +5078,14 @@ start_decl (struct c_declarator *declarator,  
> struct c_declspecs *declspecs,
>   tree expr = NULL_TREE;
>   enum deprecated_states deprecated_state = DEPRECATED_NORMAL;
>
> -  /* An object declared as __attribute__((deprecated)) suppresses
> +  /* An object declared as __attribute__((unavailable)) suppresses
> +     warnings and errors from __attribute__((deprecated/unavailable))
> +     components.
> +     An object declared as __attribute__((deprecated)) suppresses
>      warnings of uses of other deprecated items.  */
> -  if (lookup_attribute ("deprecated", attributes))
> +  if (lookup_attribute ("unavailable", attributes))
> +    deprecated_state = UNAVAILABLE_DEPRECATED_SUPPRESS;
> +  else if (lookup_attribute ("deprecated", attributes))
>     deprecated_state = DEPRECATED_SUPPRESS;
>
>   decl = grokdeclarator (declarator, declspecs,
> @@ -6190,7 +6203,7 @@ smallest_type_quals_location (const location_t  
> *locations,
>      set to indicate whether operands in *EXPR can be used in constant
>      expressions.
>    DEPRECATED_STATE is a deprecated_states value indicating whether
> -   deprecation warnings should be suppressed.
> +   deprecation/unavailability warnings should be suppressed.
>
>    In the TYPENAME case, DECLARATOR is really an absolute declarator.
>    It may also be so in the PARM case, for a prototype where the
> @@ -6320,8 +6333,14 @@ grokdeclarator (const struct c_declarator  
> *declarator,
>   if (decl_context == NORMAL && !funcdef_flag && current_scope->parm_flag)
>     decl_context = PARM;
>
> -  if (declspecs->deprecated_p && deprecated_state != DEPRECATED_SUPPRESS)
> -    warn_deprecated_use (declspecs->type, declspecs->decl_attr);
> +  if (deprecated_state != UNAVAILABLE_DEPRECATED_SUPPRESS)
> +    {
> +      if (declspecs->unavailable_p)
> +	error_unavailable_use (declspecs->type, declspecs->decl_attr);
> +      else if (declspecs->deprecated_p
> +		&& deprecated_state != DEPRECATED_SUPPRESS)
> +	warn_deprecated_use (declspecs->type, declspecs->decl_attr);
> +    }
>
>   if ((decl_context == NORMAL || decl_context == FIELD)
>       && current_scope == file_scope
> @@ -10753,6 +10772,8 @@ declspecs_add_type (location_t loc, struct  
> c_declspecs *specs,
>   specs->typespec_kind = spec.kind;
>   if (TREE_DEPRECATED (type))
>     specs->deprecated_p = true;
> +  if (TREE_UNAVAILABLE (type))
> +    specs->unavailable_p = true;
>
>   /* Handle type specifier keywords.  */
>   if (TREE_CODE (type) == IDENTIFIER_NODE
> diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
> index 1f783db7dbc..292874cc317 100644
> --- a/gcc/c/c-tree.h
> +++ b/gcc/c/c-tree.h
> @@ -370,6 +370,8 @@ struct c_declspecs {
>   BOOL_BITFIELD explicit_signed_p : 1;
>   /* Whether the specifiers include a deprecated typedef.  */
>   BOOL_BITFIELD deprecated_p : 1;
> +  /* Whether the specifiers include an unavailable typedef.  */
> +  BOOL_BITFIELD unavailable_p : 1;
>   /* Whether the type defaulted to "int" because there were no type
>      specifiers.  */
>   BOOL_BITFIELD default_int_p : 1;
> diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
> index 96840377d90..16b6f776214 100644
> --- a/gcc/c/c-typeck.c
> +++ b/gcc/c/c-typeck.c
> @@ -2504,7 +2504,9 @@ build_component_ref (location_t loc, tree datum,  
> tree component,
> 	      || (use_datum_quals && TREE_THIS_VOLATILE (datum)))
> 	    TREE_THIS_VOLATILE (ref) = 1;
>
> -	  if (TREE_DEPRECATED (subdatum))
> +	  if (TREE_UNAVAILABLE (subdatum))
> +	    error_unavailable_use (subdatum, NULL_TREE);
> +	  else if (TREE_DEPRECATED (subdatum))
> 	    warn_deprecated_use (subdatum, NULL_TREE);
>
> 	  datum = ref;
> @@ -2789,7 +2791,9 @@ build_external_ref (location_t loc, tree id, bool  
> fun, tree *type)
>   if (TREE_TYPE (ref) == error_mark_node)
>     return error_mark_node;
>
> -  if (TREE_DEPRECATED (ref))
> +  if (TREE_UNAVAILABLE (ref))
> +    error_unavailable_use (ref, NULL_TREE);
> +  else if (TREE_DEPRECATED (ref))
>     warn_deprecated_use (ref, NULL_TREE);
>
>   /* Recursive call does not count as usage.  */
> diff --git a/gcc/cp/call.c b/gcc/cp/call.c
> index 9861be1f856..3df6c48e237 100644
> --- a/gcc/cp/call.c
> +++ b/gcc/cp/call.c
> @@ -9078,7 +9078,7 @@ build_over_call (struct z_candidate *cand, int  
> flags, tsubst_flags_t complain)
> 	  already_used = true;
> 	}
>       else
> -	cp_warn_deprecated_use (fn, complain);
> +	cp_handle_deprecated_or_unavailable (fn, complain);
>
>       /* If we're creating a temp and we already have one, don't create a
> 	 new one.  If we're not creating a temp but we get one, use
> @@ -9148,7 +9148,7 @@ build_over_call (struct z_candidate *cand, int  
> flags, tsubst_flags_t complain)
>           TREE_NO_WARNING (val) = 1;
> 	}
>
> -      cp_warn_deprecated_use (fn, complain);
> +      cp_handle_deprecated_or_unavailable (fn, complain);
>
>       return val;
>     }
> diff --git a/gcc/cp/class.c b/gcc/cp/class.c
> index c03737294eb..f594d473325 100644
> --- a/gcc/cp/class.c
> +++ b/gcc/cp/class.c
> @@ -5678,6 +5678,7 @@ type_build_ctor_call (tree t)
>       tree fn = *iter;
>       if (!DECL_ARTIFICIAL (fn)
> 	  || TREE_DEPRECATED (fn)
> +	  || TREE_UNAVAILABLE (fn)
> 	  || DECL_DELETED_FN (fn))
> 	return true;
>     }
> @@ -5706,6 +5707,7 @@ type_build_dtor_call (tree t)
>       tree fn = *iter;
>       if (!DECL_ARTIFICIAL (fn)
> 	  || TREE_DEPRECATED (fn)
> +	  || TREE_UNAVAILABLE (fn)
> 	  || DECL_DELETED_FN (fn))
> 	return true;
>     }
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 052291c40fe..7912cf511ed 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -6303,7 +6303,7 @@ extern bool is_list_ctor			(tree);
> extern void validate_conversion_obstack		(void);
> extern void mark_versions_used			(tree);
> extern bool unsafe_return_slot_p		(tree);
> -extern bool cp_warn_deprecated_use		(tree, tsubst_flags_t =  
> tf_warning_or_error);
> +extern bool cp_handle_deprecated_or_unavailable (tree, tsubst_flags_t =  
> tf_warning_or_error);
> extern void cp_warn_deprecated_use_scopes	(tree);
> extern tree get_function_version_dispatcher	(tree);
>
> diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
> index 62648841ac3..bdd290f6d02 100644
> --- a/gcc/cp/decl.c
> +++ b/gcc/cp/decl.c
> @@ -2350,6 +2350,10 @@ duplicate_decls (tree newdecl, tree olddecl, bool  
> hiding, bool was_hidden)
>       if (TREE_DEPRECATED (newdecl))
> 	TREE_DEPRECATED (olddecl) = 1;
>
> +      /* Merge unavailability.  */
> +      if (TREE_UNAVAILABLE (newdecl))
> +	TREE_UNAVAILABLE (olddecl) = 1;
> +
>       /* Preserve function specific target and optimization options */
>       if (TREE_CODE (newdecl) == FUNCTION_DECL)
> 	{
> @@ -11398,20 +11402,24 @@ grokdeclarator (const cp_declarator *declarator,
>   if (attrlist && *attrlist == error_mark_node)
>     *attrlist = NULL_TREE;
>
> -  /* An object declared as __attribute__((deprecated)) suppresses
> -     warnings of uses of other deprecated items.  */
> +  /* An object declared as __attribute__((unavailable)) suppresses
> +     any reports of being declared with unavailable or deprecated
> +     items.  An object declared as __attribute__((deprecated))
> +     suppresses warnings of uses of other deprecated items.  */
>   temp_override<deprecated_states> ds (deprecated_state);
> -  if (attrlist && lookup_attribute ("deprecated", *attrlist))
> +  if (attrlist && lookup_attribute ("unavailable", *attrlist))
> +    deprecated_state = UNAVAILABLE_DEPRECATED_SUPPRESS;
> +  else if (attrlist && lookup_attribute ("deprecated", *attrlist))
>     deprecated_state = DEPRECATED_SUPPRESS;
>
> -  cp_warn_deprecated_use (type);
> +  cp_handle_deprecated_or_unavailable (type);
>   if (type && TREE_CODE (type) == TYPE_DECL)
>     {
>       cp_warn_deprecated_use_scopes (CP_DECL_CONTEXT (type));
>       typedef_decl = type;
>       type = TREE_TYPE (typedef_decl);
>       if (DECL_ARTIFICIAL (typedef_decl))
> -	cp_warn_deprecated_use (type);
> +	cp_handle_deprecated_or_unavailable (type);
>     }
>   /* No type at all: default to `int', and set DEFAULTED_INT
>      because it was not a user-defined typedef.  */
> @@ -14027,6 +14035,43 @@ type_is_deprecated (tree type)
>   return NULL_TREE;
> }
>
> +/* Returns an unavailable type used within TYPE, or NULL_TREE if none.  */
> +
> +static tree
> +type_is_unavailable (tree type)
> +{
> +  enum tree_code code;
> +  if (TREE_UNAVAILABLE (type))
> +    return type;
> +  if (TYPE_NAME (type))
> +    {
> +      if (TREE_UNAVAILABLE (TYPE_NAME (type)))
> +	return type;
> +      else
> +	{
> +	  cp_warn_deprecated_use_scopes (CP_DECL_CONTEXT (TYPE_NAME (type)));
> +	  return NULL_TREE;
> +	}
> +    }
> +
> +  /* Do warn about using typedefs to a deprecated class.  */
> +  if (OVERLOAD_TYPE_P (type) && type != TYPE_MAIN_VARIANT (type))
> +    return type_is_deprecated (TYPE_MAIN_VARIANT (type));
> +
> +  code = TREE_CODE (type);
> +
> +  if (code == POINTER_TYPE || code == REFERENCE_TYPE
> +      || code == OFFSET_TYPE || code == FUNCTION_TYPE
> +      || code == METHOD_TYPE || code == ARRAY_TYPE)
> +    return type_is_unavailable (TREE_TYPE (type));
> +
> +  if (TYPE_PTRMEMFUNC_P (type))
> +    return type_is_unavailable
> +      (TREE_TYPE (TREE_TYPE (TYPE_PTRMEMFUNC_FN_TYPE (type))));
> +
> +  return NULL_TREE;
> +}
> +
> /* Decode the list of parameter types for a function type.
>    Given the list of things declared inside the parens,
>    return a list of types.
> @@ -14086,11 +14131,18 @@ grokparms (tree parmlist, tree *parms)
>
>       if (type != error_mark_node)
> 	{
> -	  if (deprecated_state != DEPRECATED_SUPPRESS)
> +	  if (deprecated_state != UNAVAILABLE_DEPRECATED_SUPPRESS)
> +	    {
> +	      tree unavailtype = type_is_unavailable (type);
> +	      if (unavailtype)
> +		cp_handle_deprecated_or_unavailable (unavailtype);
> +	    }
> +	  if (deprecated_state != DEPRECATED_SUPPRESS
> +	      && deprecated_state != UNAVAILABLE_DEPRECATED_SUPPRESS)
> 	    {
> 	      tree deptype = type_is_deprecated (type);
> 	      if (deptype)
> -		cp_warn_deprecated_use (deptype);
> +		cp_handle_deprecated_or_unavailable (deptype);
> 	    }
>
> 	  /* [dcl.fct] "A parameter with volatile-qualified type is
> diff --git a/gcc/cp/decl.h b/gcc/cp/decl.h
> index 3252dd8a011..475a00d8a2f 100644
> --- a/gcc/cp/decl.h
> +++ b/gcc/cp/decl.h
> @@ -44,7 +44,8 @@ extern void name_unnamed_type (tree, tree);
>
> enum deprecated_states {
>   DEPRECATED_NORMAL,
> -  DEPRECATED_SUPPRESS
> +  DEPRECATED_SUPPRESS,
> +  UNAVAILABLE_DEPRECATED_SUPPRESS
> };
>
> extern enum deprecated_states deprecated_state;
> diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
> index 1bc7b7e0197..590f8a3c157 100644
> --- a/gcc/cp/decl2.c
> +++ b/gcc/cp/decl2.c
> @@ -1608,6 +1608,17 @@ cplus_decl_attributes (tree *decl, tree  
> attributes, int flags)
> 	if (*decl == pattern)
> 	  TREE_DEPRECATED (tmpl) = true;
>       }
> +
> +  /* Likewise, propagate unavailability out to the template.  */
> +  if (TREE_UNAVAILABLE (*decl))
> +    if (tree ti = get_template_info (*decl))
> +      {
> +	tree tmpl = TI_TEMPLATE (ti);
> +	tree pattern = (TYPE_P (*decl) ? TREE_TYPE (tmpl)
> +			: DECL_TEMPLATE_RESULT (tmpl));
> +	if (*decl == pattern)
> +	  TREE_UNAVAILABLE (tmpl) = true;
> +      }
> }
>
> /* Walks through the namespace- or function-scope anonymous union
> @@ -5389,14 +5400,47 @@ maybe_instantiate_decl (tree decl)
>     }
> }
>
> -/* Maybe warn if DECL is deprecated, subject to COMPLAIN.  Returns  
> whether or
> -   not a warning was emitted.  */
> +/* Error if the DECL is unavailable (unless this is currently suppressed).
> +   Maybe warn if DECL is deprecated, subject to COMPLAIN.  Returns true if
> +   an error or warning was emitted.  */
>
> bool
> -cp_warn_deprecated_use (tree decl, tsubst_flags_t complain)
> +cp_handle_deprecated_or_unavailable (tree decl, tsubst_flags_t complain)
> {
> -  if (!(complain & tf_warning) || !decl
> -      || deprecated_state == DEPRECATED_SUPPRESS)
> +  if (!decl)
> +    return false;
> +
> +  if ((complain & tf_error)
> +      && deprecated_state != UNAVAILABLE_DEPRECATED_SUPPRESS)
> +    {
> +      if (TREE_UNAVAILABLE (decl))
> +	{
> +	  error_unavailable_use (decl, NULL_TREE);
> +	  return true;
> +	}
> +      else
> +	{
> +	  /* Perhaps this is an unavailable typedef.  */
> +	  if (TYPE_P (decl)
> +	      && TYPE_NAME (decl)
> +	      && TREE_UNAVAILABLE (TYPE_NAME (decl)))
> +	    {
> +	      decl = TYPE_NAME (decl);
> +	      /* Don't error within members of a unavailable type.  */
> +	      if (TYPE_P (decl)
> +		  && currently_open_class (decl))
> +		return false;
> +
> +	      error_unavailable_use (decl, NULL_TREE);
> +	      return true;
> +	    }
> +	}
> +      /* Carry on to consider deprecatedness.  */
> +    }
> +
> +  if (!(complain & tf_warning)
> +      || deprecated_state == DEPRECATED_SUPPRESS
> +      || deprecated_state == UNAVAILABLE_DEPRECATED_SUPPRESS)
>     return false;
>
>   if (!TREE_DEPRECATED (decl))
> @@ -5455,7 +5499,7 @@ cp_warn_deprecated_use_scopes (tree scope)
> 	 && scope != error_mark_node
> 	 && scope != global_namespace)
>     {
> -      if (cp_warn_deprecated_use (scope))
> +      if (cp_handle_deprecated_or_unavailable (scope))
> 	return;
>       if (TYPE_P (scope))
> 	scope = CP_TYPE_CONTEXT (scope);
> @@ -5567,7 +5611,7 @@ mark_used (tree decl, tsubst_flags_t complain)
>       TREE_USED (decl) = true;
>     }
>
> -  cp_warn_deprecated_use (decl, complain);
> +  cp_handle_deprecated_or_unavailable (decl, complain);
>
>   /* We can only check DECL_ODR_USED on variables or functions with
>      DECL_LANG_SPECIFIC set, and these are also the only decls that we
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index 323d7424a83..00595c0365b 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -17170,18 +17170,26 @@ cp_parser_template_name (cp_parser* parser,
>   /* If DECL is a template, then the name was a template-name.  */
>   if (TREE_CODE (decl) == TEMPLATE_DECL)
>     {
> -      if (TREE_DEPRECATED (decl)
> -	  && deprecated_state != DEPRECATED_SUPPRESS)
> +      if ((TREE_DEPRECATED (decl) || TREE_UNAVAILABLE (decl))
> +	  && deprecated_state != UNAVAILABLE_DEPRECATED_SUPPRESS)
> 	{
> 	  tree d = DECL_TEMPLATE_RESULT (decl);
> 	  tree attr;
> 	  if (TREE_CODE (d) == TYPE_DECL)
> -	    attr = lookup_attribute ("deprecated",
> -				     TYPE_ATTRIBUTES (TREE_TYPE (d)));
> +	    attr = TYPE_ATTRIBUTES (TREE_TYPE (d));
> 	  else
> -	    attr = lookup_attribute ("deprecated",
> -				     DECL_ATTRIBUTES (d));
> -	  warn_deprecated_use (decl, attr);
> +	    attr = DECL_ATTRIBUTES (d);
> +	  if (TREE_UNAVAILABLE (decl))
> +	    {
> +	      attr = lookup_attribute ("unavailable", attr);
> +	      error_unavailable_use (decl, attr);
> +	    }
> +	  else if (TREE_DEPRECATED (decl)
> +		   && deprecated_state != DEPRECATED_SUPPRESS)
> +	    {
> +	      attr = lookup_attribute ("deprecated", attr);
> +	      warn_deprecated_use (decl, attr);
> +	    }
> 	}
>     }
>   else
> @@ -17432,7 +17440,9 @@ cp_parser_template_argument (cp_parser* parser)
>     }
>   if (cp_parser_parse_definitely (parser))
>     {
> -      if (TREE_DEPRECATED (argument))
> +      if (TREE_UNAVAILABLE (argument))
> +	error_unavailable_use (argument, NULL_TREE);
> +      else if (TREE_DEPRECATED (argument))
> 	warn_deprecated_use (argument, NULL_TREE);
>       return argument;
>     }
> @@ -22930,9 +22940,9 @@ cp_parser_parameter_declaration_list (cp_parser*  
> parser, cp_parser_flags flags)
> 					   /*template_parm_p=*/false,
> 					   &parenthesized_p);
>
> -      /* We don't know yet if the enclosing context is deprecated, so wait
> -	 and warn in grokparms if appropriate.  */
> -      deprecated_state = DEPRECATED_SUPPRESS;
> +      /* We don't know yet if the enclosing context is unavailable or  
> deprecated,
> +	 so wait and deal with it in grokparms if appropriate.  */
> +      deprecated_state = UNAVAILABLE_DEPRECATED_SUPPRESS;
>
>       if (parameter)
> 	{
> diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
> index 08e0c80f9b0..7d1e2fd27a6 100644
> --- a/gcc/cp/typeck.c
> +++ b/gcc/cp/typeck.c
> @@ -2514,7 +2514,10 @@ build_class_member_access_expr (cp_expr object,  
> tree member,
>       member_scope = DECL_CLASS_CONTEXT (member);
>       if (!mark_used (member, complain) && !(complain & tf_error))
> 	return error_mark_node;
> -      if (TREE_DEPRECATED (member))
> +
> +      if (TREE_UNAVAILABLE (member))
> +	error_unavailable_use (member, NULL_TREE);
> +      else if (TREE_DEPRECATED (member))
> 	warn_deprecated_use (member, NULL_TREE);
>     }
>   else
> @@ -3217,7 +3220,9 @@ finish_class_member_access_expr (cp_expr object,  
> tree name, bool template_p,
> 	}
>     }
>
> -  if (TREE_DEPRECATED (member))
> +  if (TREE_UNAVAILABLE (member))
> +    error_unavailable_use (member, NULL_TREE);
> +  else if (TREE_DEPRECATED (member))
>     warn_deprecated_use (member, NULL_TREE);
>
>   if (template_p)
> diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
> index 445e2a211c8..4d014dac88f 100644
> --- a/gcc/cp/typeck2.c
> +++ b/gcc/cp/typeck2.c
> @@ -2293,7 +2293,7 @@ build_functional_cast_1 (location_t loc, tree exp,  
> tree parms,
>       type = TREE_TYPE (exp);
>
>       if (DECL_ARTIFICIAL (exp))
> -	cp_warn_deprecated_use (type);
> +	cp_handle_deprecated_or_unavailable (type);
>     }
>   else
>     type = exp;
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index 5f1e3bf8a2e..a6b5867808a 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -2865,6 +2865,19 @@ types (@pxref{Variable Attributes}, @pxref{Type  
> Attributes}.)
> The message attached to the attribute is affected by the setting of
> the @option{-fmessage-length} option.
>
> +@item unavailable
> +@itemx unavailable (@var{msg})
> +@cindex @code{unavailable} function attribute
> +The @code{deprecated} attribute results in an error if the function
> +is used anywhere in the source file.  This is useful when identifying
> +functions that have been removed from a particular variation of an
> +interface.  Other than emitting an error rather than a warning, the
> +@code{unavailable} attribute behaves in the same manner as
> +@code{deprecated}.
> +
> +The @code{unavailable} attribute can also be used for variables and
> +types (@pxref{Variable Attributes}, @pxref{Type Attributes}.)
> +
> @item error ("@var{message}")
> @itemx warning ("@var{message}")
> @cindex @code{error} function attribute
> @@ -7223,6 +7236,22 @@ types (@pxref{Common Function Attributes},
> The message attached to the attribute is affected by the setting of
> the @option{-fmessage-length} option.
>
> +@item unavailable
> +@itemx unavailable (@var{msg})
> +@cindex @code{unavailable} variable attribute
> +The @code{unavailable} attribute indicates that the variable so marked
> +is not available, if it is used anywhere in the source file.  It behaves
> +in the same manner as the @code{deprecated} attribute except that the
> +compiler will emit an error rather than a warning.
> +
> +It is expected that items marked as @code{deprecated} will eventually be
> +withdrawn from interfaces, and then become unavailable.  This attribute
> +allows for marking them appropriately.
> +
> +The @code{unavailable} attribute can also be used for functions and
> +types (@pxref{Common Function Attributes},
> +@pxref{Common Type Attributes}).
> +
> @item mode (@var{mode})
> @cindex @code{mode} variable attribute
> This attribute specifies the data type for the declaration---whichever
> @@ -8249,6 +8278,17 @@ variables (@pxref{Function Attributes},  
> @pxref{Variable Attributes}.)
> The message attached to the attribute is affected by the setting of
> the @option{-fmessage-length} option.
>
> +@item unavailable
> +@itemx unavailable (@var{msg})
> +@cindex @code{unavailable} type attribute
> +The @code{unavailable} attribute behaves in the same manner as the
> +@code{deprecated} one, but emits an error rather than a warning.  It is
> +used to indicate that a (perhaps previously @code{deprecated}) type is
> +no longer usable.
> +
> +The @code{unavailable} attribute can also be used for functions and
> +variables (@pxref{Function Attributes}, @pxref{Variable Attributes}.)
> +
> @item designated_init
> @cindex @code{designated_init} type attribute
> This attribute may only be applied to structure types.  It indicates
> @@ -8716,6 +8756,12 @@ of the deprecated enumerator, to enable users to  
> easily find further
> information about why the enumerator is deprecated, or what they should
> do instead.  Note that the warnings only occurs for uses.
>
> +@item unavailable
> +@cindex @code{unavailable} enumerator attribute
> +The @code{unavailable} attribute results in an error if the enumerator
> +is used anywhere in the source file.  In other respects it behaves in the
> +same manner as the @code{deprecated} attribute.
> +
> @end table
>
> @node Statement Attributes
> diff --git a/gcc/objc/objc-act.c b/gcc/objc/objc-act.c
> index b9ed32d51d0..fc247220a62 100644
> --- a/gcc/objc/objc-act.c
> +++ b/gcc/objc/objc-act.c
> @@ -1275,6 +1275,7 @@ objc_add_property_declaration (location_t location,  
> tree decl,
>   TREE_TYPE (property_decl) = TREE_TYPE (decl);
>   DECL_SOURCE_LOCATION (property_decl) = DECL_SOURCE_LOCATION (decl);
>   TREE_DEPRECATED (property_decl) = TREE_DEPRECATED (decl);
> +  TREE_UNAVAILABLE (property_decl) = TREE_UNAVAILABLE (decl);
>
>   /* Add property-specific information.  */
>   PROPERTY_NAME (property_decl) = DECL_NAME (decl);
> @@ -1390,6 +1391,7 @@ maybe_make_artificial_property_decl (tree  
> interface, tree implementation,
>       TREE_TYPE (property_decl) = type;
>       DECL_SOURCE_LOCATION (property_decl) = input_location;
>       TREE_DEPRECATED (property_decl) = 0;
> +      TREE_UNAVAILABLE (property_decl) = 0;
>       DECL_ARTIFICIAL (property_decl) = 1;
>
>       /* Add property-specific information.  Note that one of
> @@ -1668,7 +1670,7 @@ objc_maybe_build_component_ref (tree object, tree  
> property_ident)
>     {
>       tree expression;
>       tree getter_call;
> -      tree deprecated_method_prototype = NULL_TREE;
> +      tree method_prototype_avail = NULL_TREE;
>
>       /* We have an additional nasty problem here; if this
> 	 PROPERTY_REF needs to become a 'getter', then the conversion
> @@ -1702,10 +1704,10 @@ objc_maybe_build_component_ref (tree object, tree  
> property_ident)
> 	      is deprecated, but record the fact that the getter is
> 	      deprecated by setting PROPERTY_REF_DEPRECATED_GETTER to
> 	      the method prototype.  */
> -	   &deprecated_method_prototype);
> +	   &method_prototype_avail);
>
>       expression = build4 (PROPERTY_REF, TREE_TYPE(x), object, x, getter_call,
> -			   deprecated_method_prototype);
> +			   method_prototype_avail);
>       SET_EXPR_LOCATION (expression, input_location);
>       TREE_SIDE_EFFECTS (expression) = 1;
>
> @@ -1755,7 +1757,9 @@ objc_build_class_component_ref (tree class_name,  
> tree property_ident)
>     }
>   else
>     {
> -      if (TREE_DEPRECATED (rtype))
> +      if (TREE_UNAVAILABLE (rtype))
> +	error ("class %qE is unavailable", class_name);
> +      else if (TREE_DEPRECATED (rtype))
> 	warning (OPT_Wdeprecated_declarations, "class %qE is deprecated", class_name);
>     }
>
> @@ -1767,17 +1771,17 @@ objc_build_class_component_ref (tree class_name,  
> tree property_ident)
>     {
>       tree expression;
>       tree getter_call;
> -      tree deprecated_method_prototype = NULL_TREE;
> +      tree method_prototype_avail = NULL_TREE;
>
>       if (PROPERTY_HAS_NO_GETTER (x))
> 	getter_call = NULL_TREE;
>       else
> 	getter_call = objc_finish_message_expr
> 	  (object, PROPERTY_GETTER_NAME (x), NULL_TREE,
> -	   &deprecated_method_prototype);
> +	   &method_prototype_avail);
>
>       expression = build4 (PROPERTY_REF, TREE_TYPE(x), object, x, getter_call,
> -			   deprecated_method_prototype);
> +			   method_prototype_avail);
>       SET_EXPR_LOCATION (expression, input_location);
>       TREE_SIDE_EFFECTS (expression) = 1;
>
> @@ -4548,6 +4552,8 @@ build_private_template (tree klass)
>       /* Copy the attributes from the class to the type.  */
>       if (TREE_DEPRECATED (klass))
> 	TREE_DEPRECATED (record) = 1;
> +      if (TREE_UNAVAILABLE (klass))
> +	TREE_UNAVAILABLE (record) = 1;
>     }
> }
>
> @@ -4973,6 +4979,7 @@ objc_decl_method_attributes (tree *node, tree  
> attributes, int flags)
> 	  tree name = TREE_PURPOSE (attribute);
>
> 	  if (is_attribute_p  ("deprecated", name)
> +	      || is_attribute_p ("unavailable", name)
> 	      || is_attribute_p ("sentinel", name)
> 	      || is_attribute_p ("noreturn", name))
> 	    {
> @@ -5438,9 +5445,9 @@ lookup_method_in_hash_lists (tree sel_name, int  
> is_class)
>    C++ template functions, it is called from 'build_expr_from_tree'
>    (in decl2.c) after RECEIVER and METHOD_PARAMS have been expanded.
>
> -   If the DEPRECATED_METHOD_PROTOTYPE argument is NULL, then we warn
> +   If the method_prototype_avail argument is NULL, then we warn
>    if the method being used is deprecated.  If it is not NULL, instead
> -   of deprecating, we set *DEPRECATED_METHOD_PROTOTYPE to the method
> +   of deprecating, we set *method_prototype_avail to the method
>    prototype that was used and is deprecated.  This is useful for
>    getter calls that are always generated when compiling dot-syntax
>    expressions, even if they may not be used.  In that case, we don't
> @@ -5449,7 +5456,7 @@ lookup_method_in_hash_lists (tree sel_name, int  
> is_class)
>    used.  */
> tree
> objc_finish_message_expr (tree receiver, tree sel_name, tree method_params,
> -			  tree *deprecated_method_prototype)
> +			  tree *method_prototype_avail)
> {
>   tree method_prototype = NULL_TREE, rprotos = NULL_TREE, rtype;
>   tree retval, class_tree;
> @@ -5761,10 +5768,17 @@ objc_finish_message_expr (tree receiver, tree  
> sel_name, tree method_params,
> 	 In practice this makes sense since casting an object to 'id'
> 	 is often used precisely to turn off warnings associated with
> 	 the object being of a particular class.  */
> -      if (TREE_DEPRECATED (method_prototype) && rtype != NULL_TREE)
> +      if (TREE_UNAVAILABLE (method_prototype) && rtype != NULL_TREE)
> 	{
> -	  if (deprecated_method_prototype)
> -	    *deprecated_method_prototype = method_prototype;
> +	  if (method_prototype_avail)
> +	    *method_prototype_avail = method_prototype;
> +	  else
> +	    error_unavailable_use (method_prototype, NULL_TREE);
> +	}
> +      else if (TREE_DEPRECATED (method_prototype) && rtype != NULL_TREE)
> +	{
> +	  if (method_prototype_avail)
> +	    *method_prototype_avail = method_prototype;
> 	  else
> 	    warn_deprecated_use (method_prototype, NULL_TREE);
> 	}
> @@ -6936,7 +6950,9 @@ start_class (enum tree_code code, tree class_name,  
> tree super_name,
> 	}
>       else
> 	{
> -	  if (TREE_DEPRECATED (super_interface))
> +	  if (TREE_UNAVAILABLE (super_interface))
> +	    error ("class %qE is not available", super);
> +	  else if (TREE_DEPRECATED (super_interface))
> 	    warning (OPT_Wdeprecated_declarations, "class %qE is deprecated",
> 		     super);
> 	  super_name = super;
> @@ -7040,7 +7056,9 @@ start_class (enum tree_code code, tree class_name,  
> tree super_name,
> 	      /* TODO: Document what the objc_exception attribute is/does.  */
> 	      /* We handle the 'deprecated', 'visibility' and (undocumented)
> 		 'objc_exception' attributes.  */
> -	      if (is_attribute_p  ("deprecated", name))
> +	      if (is_attribute_p  ("unavailable", name))
> +		TREE_UNAVAILABLE (klass) = 1;
> +	      else if (is_attribute_p  ("deprecated", name))
> 		TREE_DEPRECATED (klass) = 1;
> 	      else if (is_attribute_p  ("objc_exception", name))
> 		CLASS_HAS_EXCEPTION_ATTR (klass) = 1;
> @@ -7069,7 +7087,9 @@ start_class (enum tree_code code, tree class_name,  
> tree super_name,
> 	  }
> 	else
> 	  {
> -	    if (TREE_DEPRECATED (class_category_is_assoc_with))
> +	    if (TREE_UNAVAILABLE (class_category_is_assoc_with))
> +	      error ("class %qE is unavailable", class_name);
> +	    else if (TREE_DEPRECATED (class_category_is_assoc_with))
> 	      warning (OPT_Wdeprecated_declarations, "class %qE is deprecated",
> 		       class_name);
>
> @@ -8096,6 +8116,7 @@ finish_class (tree klass)
> 		else
> 		  objc_add_method (objc_interface_context, getter_decl, false, false);
> 		TREE_DEPRECATED (getter_decl) = TREE_DEPRECATED (x);
> +		TREE_UNAVAILABLE (getter_decl) = TREE_UNAVAILABLE (x);
> 		METHOD_PROPERTY_CONTEXT (getter_decl) = x;
> 	      }
>
> @@ -8140,6 +8161,7 @@ finish_class (tree klass)
> 		    else
> 		      objc_add_method (objc_interface_context, setter_decl, false, false);
> 		    TREE_DEPRECATED (setter_decl) = TREE_DEPRECATED (x);
> +		    TREE_UNAVAILABLE (setter_decl) = TREE_UNAVAILABLE (x);
> 		    METHOD_PROPERTY_CONTEXT (setter_decl) = x;
> 		  }
> 	      }
> @@ -8193,7 +8215,9 @@ lookup_protocol (tree ident, bool  
> warn_if_deprecated, bool definition_required)
>   for (chain = protocol_chain; chain; chain = TREE_CHAIN (chain))
>     if (ident == PROTOCOL_NAME (chain))
>       {
> -	if (warn_if_deprecated && TREE_DEPRECATED (chain))
> +	if (TREE_UNAVAILABLE (chain))
> +	  error ("protocol %qE is unavailable", PROTOCOL_NAME (chain));
> +	else if (warn_if_deprecated && TREE_DEPRECATED (chain))
> 	  {
> 	    /* It would be nice to use warn_deprecated_use() here, but
> 	       we are using TREE_CHAIN (which is supposed to be the
> @@ -8218,6 +8242,7 @@ void
> objc_declare_protocol (tree name, tree attributes)
> {
>   bool deprecated = false;
> +  bool unavailable = false;
>
> #ifdef OBJCPLUS
>   if (current_namespace != global_namespace) {
> @@ -8236,6 +8261,8 @@ objc_declare_protocol (tree name, tree attributes)
>
> 	  if (is_attribute_p  ("deprecated", name))
> 	    deprecated = true;
> +	  else if (is_attribute_p  ("unavailable", name))
> +	    unavailable = true;
> 	  else
> 	    warning (OPT_Wattributes, "%qE attribute directive ignored", name);
> 	}
> @@ -8260,6 +8287,8 @@ objc_declare_protocol (tree name, tree attributes)
> 	  TYPE_ATTRIBUTES (protocol) = attributes;
> 	  if (deprecated)
> 	    TREE_DEPRECATED (protocol) = 1;
> +	  if (unavailable)
> +	    TREE_UNAVAILABLE (protocol) = 1;
> 	}
>     }
> }
> @@ -8269,6 +8298,7 @@ start_protocol (enum tree_code code, tree name,  
> tree list, tree attributes)
> {
>   tree protocol;
>   bool deprecated = false;
> +  bool unavailable = false;
>
> #ifdef OBJCPLUS
>   if (current_namespace != global_namespace) {
> @@ -8287,6 +8317,8 @@ start_protocol (enum tree_code code, tree name,  
> tree list, tree attributes)
>
> 	  if (is_attribute_p  ("deprecated", name))
> 	    deprecated = true;
> +	  else if (is_attribute_p  ("unavailable", name))
> +	    unavailable = true;
> 	  else
> 	    warning (OPT_Wattributes, "%qE attribute directive ignored", name);
> 	}
> @@ -8326,6 +8358,8 @@ start_protocol (enum tree_code code, tree name,  
> tree list, tree attributes)
>       TYPE_ATTRIBUTES (protocol) = attributes;
>       if (deprecated)
> 	TREE_DEPRECATED (protocol) = 1;
> +      if (unavailable)
> +	TREE_UNAVAILABLE (protocol) = 1;
>     }
>
>   return protocol;
> @@ -8855,6 +8889,8 @@ really_start_method (tree method,
> 		 warnings are produced), but just in case.  */
> 	      if (TREE_DEPRECATED (proto))
> 		TREE_DEPRECATED (method) = 1;
> +	      if (TREE_UNAVAILABLE (proto))
> +		TREE_UNAVAILABLE (method) = 1;
>
> 	      /* If the method in the @interface was marked as
> 		 'noreturn', mark the function implementing the method
> @@ -9586,12 +9622,17 @@ objc_gimplify_property_ref (tree *expr_p)
>       return;
>     }
>
> +  /* FIXME, this should be a label indicating availability in general.  */
>   if (PROPERTY_REF_DEPRECATED_GETTER (*expr_p))
>     {
> -      /* PROPERTY_REF_DEPRECATED_GETTER contains the method prototype
> +      if (TREE_UNAVAILABLE (PROPERTY_REF_DEPRECATED_GETTER (*expr_p)))
> +	error_unavailable_use (PROPERTY_REF_DEPRECATED_GETTER (*expr_p),
> +			       NULL_TREE);
> +      else
> +	/* PROPERTY_REF_DEPRECATED_GETTER contains the method prototype
> 	 that is deprecated.  */
> -      warn_deprecated_use (PROPERTY_REF_DEPRECATED_GETTER (*expr_p),
> -			   NULL_TREE);
> +	warn_deprecated_use (PROPERTY_REF_DEPRECATED_GETTER (*expr_p),
> +			     NULL_TREE);
>     }
>
>   call_exp = getter;
> diff --git a/gcc/print-tree.c b/gcc/print-tree.c
> index 17c88f81770..1443d3f5c39 100644
> --- a/gcc/print-tree.c
> +++ b/gcc/print-tree.c
> @@ -364,6 +364,8 @@ print_node (FILE *file, const char *prefix, tree  
> node, int indent,
>     fputs (code == CALL_EXPR ? " must-tail-call" : " static", file);
>   if (TREE_DEPRECATED (node))
>     fputs (" deprecated", file);
> +  if (TREE_UNAVAILABLE (node))
> +    fputs (" unavailable", file);
>   if (TREE_VISITED (node))
>     fputs (" visited", file);
>
> diff --git a/gcc/testsuite/g++.dg/ext/attr-unavailable-1.C  
> b/gcc/testsuite/g++.dg/ext/attr-unavailable-1.C
> new file mode 100644
> index 00000000000..862651f6cbf
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/attr-unavailable-1.C
> @@ -0,0 +1,113 @@
> +/* Test __attribute__ ((unavailable)) */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +typedef int INT1 __attribute__((unavailable));
> +typedef INT1 INT2 __attribute__ ((__unavailable__));
> +
> +typedef INT1 INT1a; 			/* { dg-error "'INT1' is unavailable" "" } */
> +typedef INT1 INT1b __attribute__ ((unavailable));
> +
> +INT1 should_be_unavailable; 		/* { dg-error "'INT1' is  
> unavailable" "" } */
> +INT1a should_not_be_unavailable;
> +
> +INT1 f1(void) __attribute__ ((unavailable));
> +INT1 f2(void) { return 0; }		/* { dg-error "'INT1' is unavailable" "" } */
> +
> +INT2 f3(void) __attribute__ ((__unavailable__));
> +INT2 f4(void) { return 0; }		/* { dg-error "'INT2' is unavailable" "" } */
> +int f5(INT2 x);				/* { dg-error "'INT2' is unavailable" "" } */
> +int f6(INT2 x) __attribute__ ((__unavailable__));
> +
> +typedef enum Color {red, green, blue} Color __attribute__((unavailable));
> +
> +int g1;
> +int g2 __attribute__ ((unavailable));
> +int g3 __attribute__ ((__unavailable__));
> +Color k;				/* { dg-error "'Color' is unavailable" "" } */
> +
> +typedef struct {
> +  int field1;
> +  int field2 __attribute__ ((unavailable));
> +  int field3;
> +  int field4 __attribute__ ((__unavailable__));
> +  union {
> +    int field5;
> +    int field6 __attribute__ ((unavailable));
> +  } u1;
> +  int field7:1;
> +  int field8:1 __attribute__ ((unavailable));
> +  union {
> +    int field9;
> +    int field10;
> +  } u2 __attribute__ ((unavailable));
> +} S1;
> +
> +int func1()
> +{
> +   INT1 w;				/* { dg-error "'INT1' is unavailable" "" } */
> +   int x __attribute__ ((unavailable));
> +   int y __attribute__ ((__unavailable__));
> +   int z;
> +   int (*pf)() = f1;			/* { dg-error "'INT1 f1\\(\\)' is  
> unavailable" "" } */
> +
> +   z = w + x + y + g1 + g2 + g3;	/* { dg-error "'x' is unavailable"  
> "" } */
> +					/* { dg-error "'y' is unavailable" "y" { target *-*-* } .-1 } */
> +					/* { dg-error "'g2' is unavailable" "g2" { target *-*-* } .-2 } */
> +					/* { dg-error "'g3' is unavailable" "g3" { target *-*-* } .-3 } */
> +   return f1(); 			/* { dg-error "'INT1 f1\\(\\)' is  
> unavailable" "f1" } */
> +}
> +
> +int func2(S1 *p)
> +{
> +  S1 lp;
> +
> +  if (p->field1)
> +     return p->field2;			/* { dg-error "'S1::field2'  
> is unavailable" "" } */
> +  else if (lp.field4)			/* { dg-error "'S1::field4'  
> is unavailable" "" } */
> +     return p->field3;
> +
> +  p->u1.field5 = g1 + p->field7;
> +  p->u2.field9;				/* { dg-error "'S1::u2' is unavailable" "" } */
> +  return p->u1.field6 + p->field8;	/* { dg-error "'S1::<unnamed  
> union>::field6' is unavailable" "" } */
> +					/* { dg-error "'S1::field8' is  
> unavailable" "field8" { target *-*-* } .-1 } */
> +}
> +
> +struct SS1 {
> +  int x;
> +  INT1 y; 				/* { dg-error "'INT1' is unavailable" "" } */
> +} __attribute__ ((unavailable));
> +
> +struct SS1 *p1;				/* { dg-error "'SS1' is unavailable" "" } */
> +
> +struct __attribute__ ((__unavailable__)) SS2 {
> +  int x;
> +  INT1 y; 				/* { dg-error "'INT1' is unavailable" "" } */
> +};
> +
> +struct SS2 *p2;				/* { dg-error "'SS2' is unavailable" "" } */
> +
> +#ifdef __cplusplus
> +class T {
> +  public:
> +    void member1(int) __attribute__ ((unavailable));
> +    void member2(INT1) __attribute__ ((__unavailable__));
> +    int member3(T *);
> +    int x;
> +} __attribute__ ((unavailable));
> +
> +T *p3;				// { dg-error "'T' is unavailable" }
> +
> +inline void T::member1(int) {}
> +
> +int T::member3(T *p)		// { dg-error "'T' is unavailable" }
> +{
> +  p->member1(1);			/* { dg-error "'void  
> T::member1\\(int\\)' is unavailable" "" } */
> +  (*p).member1(2);			/* { dg-error "'void  
> T::member1\\(int\\)' is unavailable" "" } */
> +  p->member2(1);			/* { dg-error "'void  
> T::member2\\(INT1\\)' is unavailable" "" } */
> +  (*p).member2(2);			/* { dg-error "'void  
> T::member2\\(INT1\\)' is unavailable" "" } */
> +  p->member3(p);
> +  (*p).member3(p);
> +  return f1(); 				/* { dg-error "'INT1 f1\\(\\)' is unavailable" "" } */
> +}
> +#endif
> diff --git a/gcc/testsuite/g++.dg/ext/attr-unavailable-2.C  
> b/gcc/testsuite/g++.dg/ext/attr-unavailable-2.C
> new file mode 100644
> index 00000000000..3de5532817e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/attr-unavailable-2.C
> @@ -0,0 +1,10 @@
> +/* Test __attribute__ ((unavailable)) */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +void func(void);
> +void func(void) __attribute__((unavailable));
> +
> +void f(void) {
> +  func(); /* { dg-error "'void func\\(\\)' is unavailable" } */
> +}
> diff --git a/gcc/testsuite/g++.dg/ext/attr-unavailable-3.C  
> b/gcc/testsuite/g++.dg/ext/attr-unavailable-3.C
> new file mode 100644
> index 00000000000..1f267ea78c4
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/attr-unavailable-3.C
> @@ -0,0 +1,14 @@
> +/* Check operator with __attribute__((unavailable)) */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +struct Foo
> +{
> +  operator int() __attribute__((unavailable));
> +};
> +
> +void g(void)
> +{
> +  Foo f;
> +  (int)f; // { dg-error "'Foo::operator int\\(\\)' is unavailable" }
> +}
> diff --git a/gcc/testsuite/g++.dg/ext/attr-unavailable-4.C  
> b/gcc/testsuite/g++.dg/ext/attr-unavailable-4.C
> new file mode 100644
> index 00000000000..b7f352ee3bd
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/attr-unavailable-4.C
> @@ -0,0 +1,11 @@
> +/* Test __attribute__ ((unavailable)) */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +struct B {
> +    virtual int foo() __attribute__((unavailable));
> +};
> +
> +int main(void) {
> +  ((B*)0)->foo(); 		// { dg-error "unavailable" }
> +}
> diff --git a/gcc/testsuite/g++.dg/ext/attr-unavailable-5.C  
> b/gcc/testsuite/g++.dg/ext/attr-unavailable-5.C
> new file mode 100644
> index 00000000000..3beea5d22c5
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/attr-unavailable-5.C
> @@ -0,0 +1,6 @@
> +/* Test __attribute__ ((unavailable)) */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +struct Foo { int i; } __attribute__ ((unavailable));
> +void foo() { Foo f; }		// { dg-error "unavailable" }
> diff --git a/gcc/testsuite/g++.dg/ext/attr-unavailable-6.C  
> b/gcc/testsuite/g++.dg/ext/attr-unavailable-6.C
> new file mode 100644
> index 00000000000..8a57ea0d88c
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/attr-unavailable-6.C
> @@ -0,0 +1,110 @@
> +/* Test __attribute__ ((unavailable)) */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +typedef int INT1 __attribute__((unavailable("You can't use INT1")));
> +typedef INT1 INT2 __attribute__ ((__unavailable__("You can't use INT2")));
> +
> +typedef INT1 INT1a; 			/* { dg-error "'INT1' is  
> unavailable: You can't use INT1" "" } */
> +
> +INT1 should_be_unavailable; 		/* { dg-error "'INT1' is  
> unavailable: You can't use INT1" "" } */
> +INT1a should_not_be_unavailable;
> +
> +INT1 f1(void) __attribute__ ((unavailable("You can't use f1")));
> +INT1 f2(void) { return 0; }		/* { dg-error "'INT1' is  
> unavailable: You can't use INT1" "" } */
> +
> +INT2 f3(void) __attribute__ ((__unavailable__("You can't use f3")));
> +INT2 f4(void) { return 0; }		/* { dg-error "'INT2' is  
> unavailable: You can't use INT2" "" } */
> +int f5(INT2 x);				/* { dg-error "'INT2' is unavailable" "" } */
> +int f6(INT2 x) __attribute__ ((__unavailable__("You can't use f6")));
> +
> +typedef enum Color {red, green, blue} Color  
> __attribute__((unavailable("You can't use Color")));
> +
> +int g1;
> +int g2 __attribute__ ((unavailable("You can't use g2")));
> +int g3 __attribute__ ((__unavailable__("You can't use g3")));
> +Color k;				/* { dg-error "'Color' is  
> unavailable: You can't use Color" "" } */
> +
> +typedef struct {
> +  int field1;
> +  int field2 __attribute__ ((unavailable("You can't use field2")));
> +  int field3;
> +  int field4 __attribute__ ((__unavailable__("You can't use field4")));
> +  union {
> +    int field5;
> +    int field6 __attribute__ ((unavailable("You can't use field6")));
> +  } u1;
> +  int field7:1;
> +  int field8:1 __attribute__ ((unavailable("You can't use field8")));
> +  union {
> +    int field9;
> +    int field10;
> +  } u2 __attribute__ ((unavailable("You can't use u2")));
> +} S1;
> +
> +int func1()
> +{
> +   INT1 w;				/* { dg-error "'INT1' is  
> unavailable: You can't use INT1" "" } */
> +   int x __attribute__ ((unavailable("You can't use x")));
> +   int y __attribute__ ((__unavailable__("You can't use y")));
> +   int z;
> +   int (*pf)() = f1;			/* { dg-error "'INT1 f1\\(\\)' is  
> unavailable: You can't use f1" "" } */
> +
> +   z = w + x + y + g1 + g2 + g3;	/* { dg-error "'x' is unavailable:  
> You can't use x" "" } */
> +					/* { dg-error "'y' is unavailable:  
> You can't use y" "y" { target *-*-* } .-1 } */
> +					/* { dg-error "'g2' is unavailable:  
> You can't use g2" "g2" { target *-*-* } .-2 } */
> +					/* { dg-error "'g3' is unavailable:  
> You can't use g3" "g3" { target *-*-* } .-3 } */
> +   return f1(); 			/* { dg-error "'INT1 f1\\(\\)' is  
> unavailable: You can't use f1" "f1" } */
> +}
> +
> +int func2(S1 *p)
> +{
> +  S1 lp;
> +
> +  if (p->field1)
> +     return p->field2;			/* { dg-error "'S1::field2'  
> is unavailable: You can't use field2" "" } */
> +  else if (lp.field4)			/* { dg-error "'S1::field4'  
> is unavailable: You can't use field4" "" } */
> +     return p->field3;
> +
> +  p->u1.field5 = g1 + p->field7;
> +  p->u2.field9;				/* { dg-error "'S1::u2' is  
> unavailable: You can't use u2" "" } */
> +  return p->u1.field6 + p->field8;	/* { dg-error "'S1::<unnamed  
> union>::field6' is unavailable: You can't use field6" "" } */
> +					/* { dg-error "'S1::field8' is  
> unavailable: You can't use field8" "field8" { target *-*-* } .-1 } */
> +}
> +
> +struct SS1 {
> +  int x;
> +  INT1 y; 				/* { dg-error "'INT1' is  
> unavailable: You can't use INT1" "" } */
> +} __attribute__ ((unavailable("You can't use SS1")));
> +
> +struct SS1 *p1;				/* { dg-error "'SS1' is  
> unavailable: You can't use SS1" "" } */
> +
> +struct __attribute__ ((__unavailable__("You can't use SS2"))) SS2 {
> +  int x;
> +  INT1 y; 				/* { dg-error "'INT1' is  
> unavailable: You can't use INT1" "" } */
> +};
> +
> +struct SS2 *p2;				/* { dg-error "'SS2' is  
> unavailable: You can't use SS2" "" } */
> +
> +class T {
> +  public:
> +    void member1(int) __attribute__ ((unavailable("You can't use  
> member1")));
> +    void member2(INT1) __attribute__ ((__unavailable__("You can't use  
> member2")));
> +    int member3(T *);
> +    int x;
> +} __attribute__ ((unavailable("You can't use T")));
> +
> +T *p3;				// { dg-error "'T' is unavailable: You can't use T" }
> +
> +inline void T::member1(int) {}
> +
> +int T::member3(T *p)		// { dg-error "'T' is unavailable: You  
> can't use T" }
> +{
> +  p->member1(1);			/* { dg-error "'void  
> T::member1\\(int\\)' is unavailable: You can't use member1" "" } */
> +  (*p).member1(2);			/* { dg-error "'void  
> T::member1\\(int\\)' is unavailable: You can't use member1" "" } */
> +  p->member2(1);			/* { dg-error "'void  
> T::member2\\(INT1\\)' is unavailable: You can't use member2" "" } */
> +  (*p).member2(2);			/* { dg-error "'void  
> T::member2\\(INT1\\)' is unavailable: You can't use member2" "" } */
> +  p->member3(p);
> +  (*p).member3(p);
> +  return f1(); 				/* { dg-error "'INT1  
> f1\\(\\)' is unavailable: You can't use f1" "" } */
> +}
> diff --git a/gcc/testsuite/g++.dg/ext/attr-unavailable-7.C  
> b/gcc/testsuite/g++.dg/ext/attr-unavailable-7.C
> new file mode 100644
> index 00000000000..c061aa3b6a2
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/attr-unavailable-7.C
> @@ -0,0 +1,19 @@
> +/* Test __attribute__ ((unavailable)) */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +int g_nn;
> +int& g_n __attribute__((unavailable)) = g_nn;
> +
> +void f()
> +{
> +  int f_nn;
> +  int& f_n __attribute__((unavailable)) = f_nn;
> +  f_n = 1;    // { dg-error "'f_n' is unavailable" }
> +}
> +
> +int main()
> +{
> +  g_n = 1;    // { dg-error "'g_n' is unavailable" }
> +  f();
> +}
> diff --git a/gcc/testsuite/g++.dg/ext/attr-unavailable-8.C  
> b/gcc/testsuite/g++.dg/ext/attr-unavailable-8.C
> new file mode 100644
> index 00000000000..334a2cf7286
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/attr-unavailable-8.C
> @@ -0,0 +1,17 @@
> +/* Test __attribute__ ((unavailable)) */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +class ToBeunavailable {
> +} __attribute__ ((unavailable ("unavailable!")));
> +
> +typedef ToBeunavailable NotToBeunavailable; // { dg-error  
> "'ToBeunavailable' is unavailable" }
> +
> +int main() {
> +
> +  ToBeunavailable();    // { dg-error "'ToBeunavailable' is unavailable" }
> +  ToBeunavailable x;    // { dg-error "'ToBeunavailable' is unavailable" }
> +
> +  NotToBeunavailable();
> +  NotToBeunavailable y;
> +}
> diff --git a/gcc/testsuite/g++.dg/ext/attr-unavailable-9.C  
> b/gcc/testsuite/g++.dg/ext/attr-unavailable-9.C
> new file mode 100644
> index 00000000000..44161336e78
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/attr-unavailable-9.C
> @@ -0,0 +1,17 @@
> +/* Test __attribute__ ((unavailable)) */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +template<typename T> struct __attribute__ ((unavailable)) S {};
> +S<int> s;
> +
> +template <template <class> class T> struct A { };
> +A<S> a;
> +
> +template <class T> void f() __attribute__ ((unavailable));
> +
> +int main()
> +{
> +  f<int>();			// { dg-error "unavailable" }
> +  void (*p)() = f<char>;	// { dg-error "unavailable" }
> +}
> diff --git a/gcc/testsuite/gcc.dg/attr-unavailable-1.c  
> b/gcc/testsuite/gcc.dg/attr-unavailable-1.c
> new file mode 100644
> index 00000000000..768214fcd3a
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/attr-unavailable-1.c
> @@ -0,0 +1,88 @@
> +/* Test __attribute__ ((unavailable)) */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +typedef int INT1 __attribute__((unavailable));
> +typedef INT1 INT2 __attribute__ ((__unavailable__));
> +
> +typedef INT1 INT1a; 			/* { dg-error "'INT1' is unavailable" "" } */
> +typedef INT1 INT1b __attribute__ ((unavailable));
> +
> +INT1 should_be_unavailable; 		/* { dg-error "'INT1' is  
> unavailable" "" } */
> +INT1a should_not_be_unavailable;
> +
> +INT1 f1(void) __attribute__ ((unavailable));
> +INT1 f2(void) { return 0; }		/* { dg-error "'INT1' is unavailable" "" } */
> +
> +INT2 f3(void) __attribute__ ((__unavailable__));
> +INT2 f4(void) { return 0; }		/* { dg-error "'INT2' is unavailable" "" } */
> +int f5(INT2 x);				/* { dg-error "'INT2' is unavailable" "" } */
> +int f6(INT2 x) __attribute__ ((__unavailable__)); /* { dg-error "'INT2'  
> is unavailable" "" } */
> +
> +typedef enum {red, green, blue} Color __attribute__((unavailable));
> +
> +int g1;
> +int g2 __attribute__ ((unavailable));
> +int g3 __attribute__ ((__unavailable__));
> +Color k;				/* { dg-error "'Color' is unavailable" "" } */
> +
> +typedef struct {
> +  int field1;
> +  int field2 __attribute__ ((unavailable));
> +  int field3;
> +  int field4 __attribute__ ((__unavailable__));
> +  union {
> +    int field5;
> +    int field6 __attribute__ ((unavailable));
> +  } u1;
> +  int field7:1;
> +  int field8:1 __attribute__ ((unavailable));
> +  union {
> +    int field9;
> +    int field10;
> +  } u2 __attribute__ ((unavailable));
> +} S1;
> +
> +int func1()
> +{
> +   INT1 w;				/* { dg-error "'INT1' is unavailable" "" } */
> +   int x __attribute__ ((unavailable));
> +   int y __attribute__ ((__unavailable__));
> +   int z;
> +   int (*pf)() = f1;			/* { dg-error "'f1' is unavailable" "" } */
> +
> +   z = w + x + y + g1 + g2 + g3;	/* { dg-error "'x' is unavailable"  
> "" } */
> +					/* { dg-error "'y' is unavailable" "y" { target *-*-* } .-1 } */
> +					/* { dg-error "'g2' is unavailable" "g2" { target *-*-* } .-2 } */
> +					/* { dg-error "'g3' is unavailable" "g3" { target *-*-* } .-3 } */
> +   return f1(); 			/* { dg-error "'f1' is unavailable" "f1" } */
> +}
> +
> +int func2(S1 *p)
> +{
> +  S1 lp;
> +
> +  if (p->field1)
> +     return p->field2;			/* { dg-error "'field2' is unavailable" "" } */
> +  else if (lp.field4)			/* { dg-error "'field4' is unavailable" "" } */
> +     return p->field3;
> +
> +  p->u1.field5 = g1 + p->field7;
> +  p->u2.field9;				/* { dg-error "'u2' is unavailable" "" } */
> +  return p->u1.field6 + p->field8;	/* { dg-error "'field6' is  
> unavailable" "" } */
> +					/* { dg-error "'field8' is  
> unavailable" "field8" { target *-*-* } .-1 } */
> +}
> +
> +struct SS1 {
> +  int x;
> +  INT1 y; 				/* { dg-error "'INT1' is unavailable" "" } */
> +} __attribute__ ((unavailable));
> +
> +struct SS1 *p1;				/* { dg-error "'SS1' is unavailable" "" } */
> +
> +struct __attribute__ ((__unavailable__)) SS2 {
> +  int x;
> +  INT1 y; 				/* { dg-error "'INT1' is unavailable" "" } */
> +};
> +
> +struct SS2 *p2;				/* { dg-error "'SS2' is unavailable" "" } */
> diff --git a/gcc/testsuite/gcc.dg/attr-unavailable-2.c  
> b/gcc/testsuite/gcc.dg/attr-unavailable-2.c
> new file mode 100644
> index 00000000000..303f973d5db
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/attr-unavailable-2.c
> @@ -0,0 +1,6 @@
> +/* Test __attribute__((unavailable)).  Test types without names.  */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +struct { int a; } __attribute__((unavailable)) x; /* { dg-error "type is  
> unavailable" } */
> +typeof(x) y; /* { dg-error "type is unavailable" } */
> diff --git a/gcc/testsuite/gcc.dg/attr-unavailable-3.c  
> b/gcc/testsuite/gcc.dg/attr-unavailable-3.c
> new file mode 100644
> index 00000000000..7274c193f3f
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/attr-unavailable-3.c
> @@ -0,0 +1,10 @@
> +/* Test __attribute__((unavailable)). */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +void func(void);
> +void func(void) __attribute__((unavailable));
> +
> +void f(void) {
> +  func(); /* { dg-error "'func' is unavailable" } */
> +}
> diff --git a/gcc/testsuite/gcc.dg/attr-unavailable-4.c  
> b/gcc/testsuite/gcc.dg/attr-unavailable-4.c
> new file mode 100644
> index 00000000000..9e39c50fec6
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/attr-unavailable-4.c
> @@ -0,0 +1,88 @@
> +/* Test __attribute__ ((unavailable("message"))) */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +typedef int INT1 __attribute__((unavailable("You can't use INT1")));
> +typedef INT1 INT2 __attribute__ ((__unavailable__("You can't use INT2")));
> +
> +typedef INT1 INT1a; 			/* { dg-error "'INT1' is  
> unavailable: You can't use INT1" "" } */
> +typedef INT1 INT1b __attribute__ ((unavailable("You can't use INT1b")));
> +
> +INT1 should_be_unavailable; 		/* { dg-error "'INT1' is  
> unavailable: You can't use INT1" "" } */
> +INT1a should_not_be_unavailable;
> +
> +INT1 f1(void) __attribute__ ((unavailable("You can't use f1")));
> +INT1 f2(void) { return 0; }		/* { dg-error "'INT1' is  
> unavailable: You can't use INT1" "" } */
> +
> +INT2 f3(void) __attribute__ ((__unavailable__("You can't use f3")));
> +INT2 f4(void) { return 0; }		/* { dg-error "'INT2' is  
> unavailable: You can't use INT2" "" } */
> +int f5(INT2 x);				/* { dg-error "'INT2' is  
> unavailable: You can't use INT2" "" } */
> +int f6(INT2 x) __attribute__ ((__unavailable__("You can't use f6"))); /*  
> { dg-error "'INT2' is unavailable: You can't use INT2" "" } */
> +
> +typedef enum {red, green, blue} Color __attribute__((unavailable("You  
> can't use Color")));
> +
> +int g1;
> +int g2 __attribute__ ((unavailable("You can't use g2")));
> +int g3 __attribute__ ((__unavailable__("You can't use g3")));
> +Color k;				/* { dg-error "'Color' is  
> unavailable: You can't use Color" "" } */
> +
> +typedef struct {
> +  int field1;
> +  int field2 __attribute__ ((unavailable("You can't use field2")));
> +  int field3;
> +  int field4 __attribute__ ((__unavailable__("You can't use field4")));
> +  union {
> +    int field5;
> +    int field6 __attribute__ ((unavailable("You can't use field6")));
> +  } u1;
> +  int field7:1;
> +  int field8:1 __attribute__ ((unavailable("You can't use field8")));
> +  union {
> +    int field9;
> +    int field10;
> +  } u2 __attribute__ ((unavailable("You can't use u2")));
> +} S1;
> +
> +int func1()
> +{
> +   INT1 w;				/* { dg-error "'INT1' is  
> unavailable: You can't use INT1" "" } */
> +   int x __attribute__ ((unavailable("Avoid x")));
> +   int y __attribute__ ((__unavailable__("Bad y")));
> +   int z;
> +   int (*pf)() = f1;			/* { dg-error "'f1' is unavailable:  
> You can't use f1" "" } */
> +
> +   z = w + x + y + g1 + g2 + g3;	/* { dg-error "'x' is unavailable:  
> Avoid x" "" } */
> +					/* { dg-error "'y' is unavailable:  
> Bad y" "y" { target *-*-* } .-1  } */
> +					/* { dg-error "'g2' is unavailable:  
> You can't use g2" "g2" { target *-*-* } .-2  }  */
> +					/* { dg-error "'g3' is unavailable:  
> You can't use g3" "g3" { target *-*-* } .-3  } */
> +   return f1(); 			/* { dg-error "'f1' is unavailable:  
> You can't use f1" "" } */
> +}
> +
> +int func2(S1 *p)
> +{
> +  S1 lp;
> +
> +  if (p->field1)
> +     return p->field2;			/* { dg-error "'field2' is  
> unavailable: You can't use field2" "" } */
> +  else if (lp.field4)			/* { dg-error "'field4' is  
> unavailable: You can't use field4" "" } */
> +     return p->field3;
> +
> +  p->u1.field5 = g1 + p->field7;
> +  p->u2.field9;				/* { dg-error "'u2' is  
> unavailable: You can't use u2" "" } */
> +  return p->u1.field6 + p->field8;	/* { dg-error "'field6' is  
> unavailable: You can't use field6" "" } */
> +					/* { dg-error "'field8' is  
> unavailable: You can't use field8" "field8" { target *-*-* } .-1 } */
> +}
> +
> +struct SS1 {
> +  int x;
> +  INT1 y; 				/* { dg-error "'INT1' is  
> unavailable: You can't use INT1" "" } */
> +} __attribute__ ((unavailable("You can't use SS1")));
> +
> +struct SS1 *p1;				/* { dg-error "'SS1' is  
> unavailable: You can't use SS1" "" } */
> +
> +struct __attribute__ ((__unavailable__("You can't use SS2"))) SS2 {
> +  int x;
> +  INT1 y; 				/* { dg-error "'INT1' is  
> unavailable: You can't use INT1" "" } */
> +};
> +
> +struct SS2 *p2;				/* { dg-error "'SS2' is  
> unavailable: You can't use SS2" "" } */
> diff --git a/gcc/testsuite/gcc.dg/attr-unavailable-5.c  
> b/gcc/testsuite/gcc.dg/attr-unavailable-5.c
> new file mode 100644
> index 00000000000..051f960c5cf
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/attr-unavailable-5.c
> @@ -0,0 +1,6 @@
> +/* Test __attribute__((unavailable)).  Test types without names.  */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +struct { int a; } __attribute__((unavailable ("Do not use"))) x; /* {  
> dg-error "type is unavailable" } */
> +typeof(x) y; /* { dg-error "type is unavailable: Do not use" } */
> diff --git a/gcc/testsuite/gcc.dg/attr-unavailable-6.c  
> b/gcc/testsuite/gcc.dg/attr-unavailable-6.c
> new file mode 100644
> index 00000000000..f5f4560c735
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/attr-unavailable-6.c
> @@ -0,0 +1,11 @@
> +/* Test __attribute__((unavailable)).  Test merging with multiple
> +   declarations. */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +void func(void);
> +void func(void) __attribute__((unavailable ("Do not use")));
> +
> +void f(void) {
> +  func(); /* { dg-error "'func' is unavailable: Do not use" } */
> +}
> diff --git a/gcc/testsuite/obj-c++.dg/attributes/method-unavailable-1.mm  
> b/gcc/testsuite/obj-c++.dg/attributes/method-unavailable-1.mm
> new file mode 100644
> index 00000000000..e5708c202c0
> --- /dev/null
> +++ b/gcc/testsuite/obj-c++.dg/attributes/method-unavailable-1.mm
> @@ -0,0 +1,34 @@
> +/* Test __attribute__ ((unavailable)) */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +#include <objc/objc.h>
> +
> +@interface MyClass
> +{
> +  Class isa;
> +}
> ++ (int) method;
> +- (int) method;
> ++ (int) unavailableClassMethod __attribute__((unavailable));
> +- (int) unavailableInstanceMethod __attribute__((unavailable));
> +@end
> +
> +/* Test that deprecation warnings are produced, but not if the
> +   receiver is of type 'id'.  */
> +void foo (void)
> +{
> +  Class c;
> +  id object;
> +  MyClass *another_object;
> +
> +  [c method];
> +  [object method];
> +  [c unavailableClassMethod];
> +  [object unavailableInstanceMethod];
> +
> +  [object method];
> +  [another_object method];
> +  [MyClass unavailableClassMethod];           /* { dg-error "is  
> unavailable" } */
> +  [another_object unavailableInstanceMethod]; /* { dg-error "is  
> unavailable" } */
> +}
> diff --git a/gcc/testsuite/obj-c++.dg/attributes/method-unavailable-2.mm  
> b/gcc/testsuite/obj-c++.dg/attributes/method-unavailable-2.mm
> new file mode 100644
> index 00000000000..68ea46d2543
> --- /dev/null
> +++ b/gcc/testsuite/obj-c++.dg/attributes/method-unavailable-2.mm
> @@ -0,0 +1,24 @@
> +/* Test __attribute__ ((unavailable)) */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +#include <objc/objc.h>
> +
> +@interface MyClass
> +{
> +  Class isa;
> +}
> ++ (int) unavailableClassMethod: (id)firstObject, ...     
> __attribute__((sentinel)) __attribute__((unavailable));
> +- (int) unavailableInstanceMethod: (id)firstobject, ...  
> __attribute__((sentinel)) __attribute__((unavailable));
> +@end
> +
> +/* Test that unavailability errors are produced even if the method is
> +   also marked with another attribute too (this is to test the
> +   processing of multiple attributes).  */
> +void foo (void)
> +{
> +  MyClass *object = nil;
> +
> +  [MyClass unavailableClassMethod: object, nil];           /* { dg-error  
> "is unavailable" } */
> +  [object unavailableInstanceMethod: object, nil];         /* { dg-error  
> "is unavailable" } */
> +}
> diff --git a/gcc/testsuite/obj-c++.dg/attributes/method-unavailable-3.mm  
> b/gcc/testsuite/obj-c++.dg/attributes/method-unavailable-3.mm
> new file mode 100644
> index 00000000000..9e55ae11e1f
> --- /dev/null
> +++ b/gcc/testsuite/obj-c++.dg/attributes/method-unavailable-3.mm
> @@ -0,0 +1,22 @@
> +/* Test __attribute__ ((unavailable)) */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +#include <objc/objc.h>
> +
> +/* Test that __attribute__ ((__unavailable__)) works as well as  
> __attribute__ ((unavailable)).  */
> +@interface MyClass
> +{
> +  Class isa;
> +}
> ++ (int) unavailableClassMethod: (id)firstObject, ...     
> __attribute__((__unavailable__));
> +- (int) unavailableInstanceMethod: (id)firstobject, ...  
> __attribute__((__unavailable__));
> +@end
> +
> +void foo (void)
> +{
> +  MyClass *object = nil;
> +
> +  [MyClass unavailableClassMethod: object, nil];           /* { dg-error  
> "is unavailable" } */
> +  [object unavailableInstanceMethod: object, nil];         /* { dg-error  
> "is unavailable" } */
> +}
> diff --git  
> a/gcc/testsuite/obj-c++.dg/property/at-property-unavailable-1.mm  
> b/gcc/testsuite/obj-c++.dg/property/at-property-unavailable-1.mm
> new file mode 100644
> index 00000000000..6bb4755220d
> --- /dev/null
> +++ b/gcc/testsuite/obj-c++.dg/property/at-property-unavailable-1.mm
> @@ -0,0 +1,38 @@
> +/* Test __attribute__ ((unavailable)) */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +/* Test that properties can be unavailable.  */
> +
> +#include <stdlib.h>
> +#include <objc/objc.h>
> +#include <objc/runtime.h>
> +
> +@interface MyRootClass
> +{
> +  Class isa;
> +  int a;
> +}
> +@property int a __attribute__((unavailable));
> ++ (id) initialize;
> ++ (id) alloc;
> +- (id) init;
> +@end
> +
> +@implementation MyRootClass
> ++ (id) initialize { return self; }
> ++ (id) alloc { return class_createInstance (self, 0); }
> +- (id) init { return self; }
> +@synthesize a;
> +@end
> +
> +int main (void)
> +{
> +  MyRootClass *object = [[MyRootClass alloc] init];
> +
> +  object.a = 40;      /* { dg-error "is unavailable" } */
> +  if (object.a != 40) /* { dg-error "is unavailable" } */
> +    abort ();
> +
> +  return (0);
> +}
> diff --git  
> a/gcc/testsuite/obj-c++.dg/property/at-property-unavailable-2.mm  
> b/gcc/testsuite/obj-c++.dg/property/at-property-unavailable-2.mm
> new file mode 100644
> index 00000000000..5edc1626c89
> --- /dev/null
> +++ b/gcc/testsuite/obj-c++.dg/property/at-property-unavailable-2.mm
> @@ -0,0 +1,26 @@
> +/* Test __attribute__ ((unavailable)) */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +/* Test that unavailability errors are produced when a setter/getter of
> +   a @property is used directly.  */
> +
> +#include <objc/objc.h>
> +
> +@interface MyClass
> +{
> +  Class isa;
> +  int variable;
> +}
> +@property (assign, nonatomic) int property __attribute__ ((unavailable));
> +@end
> +
> +void foo (void)
> +{
> +  MyClass *object = nil;
> +
> +  if ([object property] > 0)  /* { dg-error "is unavailable" } */
> +    {
> +      [object setProperty: 43]; /* { dg-error "is unavailable" } */
> +    }
> +}
> diff --git a/gcc/testsuite/obj-c++.dg/property/dotsyntax-unavailable-1.mm  
> b/gcc/testsuite/obj-c++.dg/property/dotsyntax-unavailable-1.mm
> new file mode 100644
> index 00000000000..e2ef2a5b23b
> --- /dev/null
> +++ b/gcc/testsuite/obj-c++.dg/property/dotsyntax-unavailable-1.mm
> @@ -0,0 +1,42 @@
> +/* Test __attribute__ ((unavailable)) */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +/* Test the 'dot syntax' with unavailable methods.  */
> +
> +#include <objc/objc.h>
> +
> +@interface MyClass
> +{
> +  Class isa;
> +}
> ++ (int) classCount __attribute__ ((unavailable));
> ++ (void) setClassCount: (int)value __attribute__ ((unavailable));
> +
> +- (int) count __attribute__ ((unavailable));
> +- (void) setCount: (int)value __attribute__ ((unavailable));
> +
> +- (int) classCount2;
> +- (void) setClassCount2: (int)value;
> +
> +- (int) count2;
> +- (void) setCount2: (int)value;
> +@end
> +
> +void foo (void)
> +{
> +  MyClass *object = nil;
> +
> +
> +  if (object.count > 0)  /* { dg-error "is unavailable" } */
> +    object.count = 20;  /* { dg-error "is unavailable" } */
> +
> +  if (MyClass.classCount < -7)   /* { dg-error "is unavailable" } */
> +    MyClass.classCount = 11;  /* { dg-error "is unavailable" } */
> +
> +  if (object.classCount2 > 0)
> +    object.classCount2 = 19;
> +
> +  if (object.count2 < -7)
> +    object.count2 = 74;
> +}
> diff --git a/gcc/testsuite/objc.dg/attributes/method-unavailable-1.m  
> b/gcc/testsuite/objc.dg/attributes/method-unavailable-1.m
> new file mode 100644
> index 00000000000..7a3de6b245d
> --- /dev/null
> +++ b/gcc/testsuite/objc.dg/attributes/method-unavailable-1.m
> @@ -0,0 +1,34 @@
> +/* Test __attribute__ ((unavailable)) */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +#include <objc/objc.h>
> +
> +@interface MyClass
> +{
> +  Class isa;
> +}
> ++ (int) method;
> +- (int) method;
> ++ (int) unavailableClassMethod __attribute__((unavailable));
> +- (int) unavailableInstanceMethod __attribute__((unavailable));
> +@end
> +
> +/* Test that unavailability errors are produced, but not if the
> +   receiver is of type 'id'.  */
> +void foo (void)
> +{
> +  Class c;
> +  id object;
> +  MyClass *another_object;
> +
> +  [c method];
> +  [object method];
> +  [c unavailableClassMethod];
> +  [object unavailableInstanceMethod];
> +
> +  [object method];
> +  [another_object method];
> +  [MyClass unavailableClassMethod];           /* { dg-error "is  
> unavailable" } */
> +  [another_object unavailableInstanceMethod]; /* { dg-error "is  
> unavailable" } */
> +}
> diff --git a/gcc/testsuite/objc.dg/attributes/method-unavailable-2.m  
> b/gcc/testsuite/objc.dg/attributes/method-unavailable-2.m
> new file mode 100644
> index 00000000000..68ea46d2543
> --- /dev/null
> +++ b/gcc/testsuite/objc.dg/attributes/method-unavailable-2.m
> @@ -0,0 +1,24 @@
> +/* Test __attribute__ ((unavailable)) */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +#include <objc/objc.h>
> +
> +@interface MyClass
> +{
> +  Class isa;
> +}
> ++ (int) unavailableClassMethod: (id)firstObject, ...     
> __attribute__((sentinel)) __attribute__((unavailable));
> +- (int) unavailableInstanceMethod: (id)firstobject, ...  
> __attribute__((sentinel)) __attribute__((unavailable));
> +@end
> +
> +/* Test that unavailability errors are produced even if the method is
> +   also marked with another attribute too (this is to test the
> +   processing of multiple attributes).  */
> +void foo (void)
> +{
> +  MyClass *object = nil;
> +
> +  [MyClass unavailableClassMethod: object, nil];           /* { dg-error  
> "is unavailable" } */
> +  [object unavailableInstanceMethod: object, nil];         /* { dg-error  
> "is unavailable" } */
> +}
> diff --git a/gcc/testsuite/objc.dg/attributes/method-unavailable-3.m  
> b/gcc/testsuite/objc.dg/attributes/method-unavailable-3.m
> new file mode 100644
> index 00000000000..9e55ae11e1f
> --- /dev/null
> +++ b/gcc/testsuite/objc.dg/attributes/method-unavailable-3.m
> @@ -0,0 +1,22 @@
> +/* Test __attribute__ ((unavailable)) */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +#include <objc/objc.h>
> +
> +/* Test that __attribute__ ((__unavailable__)) works as well as  
> __attribute__ ((unavailable)).  */
> +@interface MyClass
> +{
> +  Class isa;
> +}
> ++ (int) unavailableClassMethod: (id)firstObject, ...     
> __attribute__((__unavailable__));
> +- (int) unavailableInstanceMethod: (id)firstobject, ...  
> __attribute__((__unavailable__));
> +@end
> +
> +void foo (void)
> +{
> +  MyClass *object = nil;
> +
> +  [MyClass unavailableClassMethod: object, nil];           /* { dg-error  
> "is unavailable" } */
> +  [object unavailableInstanceMethod: object, nil];         /* { dg-error  
> "is unavailable" } */
> +}
> diff --git a/gcc/testsuite/objc.dg/property/at-property-unavailable-1.m  
> b/gcc/testsuite/objc.dg/property/at-property-unavailable-1.m
> new file mode 100644
> index 00000000000..855ccc6b028
> --- /dev/null
> +++ b/gcc/testsuite/objc.dg/property/at-property-unavailable-1.m
> @@ -0,0 +1,38 @@
> +/* Test __attribute__ ((unavailable)) */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +/* Test that properties can be unavailable.  */
> +
> +#include <stdlib.h>
> +#include <objc/objc.h>
> +#include <objc/runtime.h>
> +
> +@interface MyRootClass
> +{
> +  Class isa;
> +  int a;
> +}
> +@property int a __attribute__((unavailable));
> ++ (id) initialize;
> ++ (id) alloc;
> +- (id) init;
> +@end
> +
> +@implementation MyRootClass
> ++ (id) initialize { return self; }
> ++ (id) alloc { return class_createInstance (self, 0); }
> +- (id) init { return self; }
> +@synthesize a;
> +@end
> +
> +int main (void)
> +{
> +  MyRootClass *object = [[MyRootClass alloc] init];
> +
> +  object.a = 40;      /* { dg-error "is unavailable" } */
> +  if (object.a != 40) /* { dg-error "is unavailable" } */
> +    abort ();
> +
> +  return 0;
> +}
> diff --git a/gcc/testsuite/objc.dg/property/at-property-unavailable-2.m  
> b/gcc/testsuite/objc.dg/property/at-property-unavailable-2.m
> new file mode 100644
> index 00000000000..5edc1626c89
> --- /dev/null
> +++ b/gcc/testsuite/objc.dg/property/at-property-unavailable-2.m
> @@ -0,0 +1,26 @@
> +/* Test __attribute__ ((unavailable)) */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +/* Test that unavailability errors are produced when a setter/getter of
> +   a @property is used directly.  */
> +
> +#include <objc/objc.h>
> +
> +@interface MyClass
> +{
> +  Class isa;
> +  int variable;
> +}
> +@property (assign, nonatomic) int property __attribute__ ((unavailable));
> +@end
> +
> +void foo (void)
> +{
> +  MyClass *object = nil;
> +
> +  if ([object property] > 0)  /* { dg-error "is unavailable" } */
> +    {
> +      [object setProperty: 43]; /* { dg-error "is unavailable" } */
> +    }
> +}
> diff --git a/gcc/testsuite/objc.dg/property/dotsyntax-unavailable-1.m  
> b/gcc/testsuite/objc.dg/property/dotsyntax-unavailable-1.m
> new file mode 100644
> index 00000000000..e2ef2a5b23b
> --- /dev/null
> +++ b/gcc/testsuite/objc.dg/property/dotsyntax-unavailable-1.m
> @@ -0,0 +1,42 @@
> +/* Test __attribute__ ((unavailable)) */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +/* Test the 'dot syntax' with unavailable methods.  */
> +
> +#include <objc/objc.h>
> +
> +@interface MyClass
> +{
> +  Class isa;
> +}
> ++ (int) classCount __attribute__ ((unavailable));
> ++ (void) setClassCount: (int)value __attribute__ ((unavailable));
> +
> +- (int) count __attribute__ ((unavailable));
> +- (void) setCount: (int)value __attribute__ ((unavailable));
> +
> +- (int) classCount2;
> +- (void) setClassCount2: (int)value;
> +
> +- (int) count2;
> +- (void) setCount2: (int)value;
> +@end
> +
> +void foo (void)
> +{
> +  MyClass *object = nil;
> +
> +
> +  if (object.count > 0)  /* { dg-error "is unavailable" } */
> +    object.count = 20;  /* { dg-error "is unavailable" } */
> +
> +  if (MyClass.classCount < -7)   /* { dg-error "is unavailable" } */
> +    MyClass.classCount = 11;  /* { dg-error "is unavailable" } */
> +
> +  if (object.classCount2 > 0)
> +    object.classCount2 = 19;
> +
> +  if (object.count2 < -7)
> +    object.count2 = 74;
> +}
> diff --git a/gcc/tree-core.h b/gcc/tree-core.h
> index c9280a8d3b1..97b3cdd7bd9 100644
> --- a/gcc/tree-core.h
> +++ b/gcc/tree-core.h
> @@ -1001,7 +1001,8 @@ struct GTY(()) tree_base {
>       unsigned user_align : 1;
>       unsigned nameless_flag : 1;
>       unsigned atomic_flag : 1;
> -      unsigned spare0 : 3;
> +      unsigned unavailable_flag : 1;
> +      unsigned spare0 : 2;
>
>       unsigned spare1 : 8;
>
> @@ -1323,6 +1324,12 @@ struct GTY(()) tree_base {
>        SSA_NAME_POINTS_TO_READONLY_MEMORY in
> 	   SSA_NAME
>
> +   unavailable_flag:
> +
> +       TREE_UNAVAILABLE in
> +	   all decls
> +	   all types
> +
>    visited:
>
>        TREE_VISITED in
> @@ -1370,6 +1377,7 @@ struct GTY(()) tree_base {
>
>        CALL_EXPR_BY_DESCRIPTOR in
>            CALL_EXPR
> +
> */
>
> struct GTY(()) tree_typed {
> diff --git a/gcc/tree.c b/gcc/tree.c
> index 9260772b846..ea3f87650dd 100644
> --- a/gcc/tree.c
> +++ b/gcc/tree.c
> @@ -13162,6 +13162,78 @@ warn_deprecated_use (tree node, tree attr)
>   return w;
> }
>
> +/* Error out with an identifier which was marked 'unavailable'. */
> +void
> +error_unavailable_use (tree node, tree attr)
> +{
> +  escaped_string msg;
> +
> +  if (node == 0)
> +    return;
> +
> +  if (!attr)
> +    {
> +      if (DECL_P (node))
> +	attr = DECL_ATTRIBUTES (node);
> +      else if (TYPE_P (node))
> +	{
> +	  tree decl = TYPE_STUB_DECL (node);
> +	  if (decl)
> +	    attr = lookup_attribute ("unavailable",
> +				     TYPE_ATTRIBUTES (TREE_TYPE (decl)));
> +	}
> +    }
> +
> +  if (attr)
> +    attr = lookup_attribute ("unavailable", attr);
> +
> +  if (attr)
> +    msg.escape (TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
> +
> +  if (DECL_P (node))
> +    {
> +      auto_diagnostic_group d;
> +      if (msg)
> +	error ("%qD is unavailable: %s", node, (const char *) msg);
> +      else
> +	error ("%qD is unavailable", node);
> +      inform (DECL_SOURCE_LOCATION (node), "declared here");
> +    }
> +  else if (TYPE_P (node))
> +    {
> +      tree what = NULL_TREE;
> +      tree decl = TYPE_STUB_DECL (node);
> +
> +      if (TYPE_NAME (node))
> +	{
> +	  if (TREE_CODE (TYPE_NAME (node)) == IDENTIFIER_NODE)
> +	    what = TYPE_NAME (node);
> +	  else if (TREE_CODE (TYPE_NAME (node)) == TYPE_DECL
> +		   && DECL_NAME (TYPE_NAME (node)))
> +	    what = DECL_NAME (TYPE_NAME (node));
> +	}
> +
> +      auto_diagnostic_group d;
> +      if (what)
> +	{
> +	  if (msg)
> +	    error ("%qE is unavailable: %s", what, (const char *) msg);
> +	  else
> +	    error ("%qE is unavailable", what);
> +	}
> +      else
> +	{
> +	  if (msg)
> +	    error ("type is unavailable: %s", (const char *) msg);
> +	  else
> +	    error ("type is unavailable");
> +	}
> +
> +      if (decl)
> +	inform (DECL_SOURCE_LOCATION (decl), "declared here");
> +    }
> +}
> +
> /* Return true if REF has a COMPONENT_REF with a bit-field field  
> declaration
>    somewhere in it.  */
>
> diff --git a/gcc/tree.h b/gcc/tree.h
> index f8f0a606439..e8a9c90f234 100644
> --- a/gcc/tree.h
> +++ b/gcc/tree.h
> @@ -952,6 +952,11 @@ extern void omp_clause_range_check_failed  
> (const_tree, const char *, int,
> #define TREE_DEPRECATED(NODE) \
>   ((NODE)->base.deprecated_flag)
>
> +/* Nonzero in a _DECL if the use of the name is defined as an
> +   unavailable feature by __attribute__((unavailable)).  */
> +#define TREE_UNAVAILABLE(NODE) \
> +  ((NODE)->base.u.bits.unavailable_flag)
> +
> /* Nonzero indicates an IDENTIFIER_NODE that names an anonymous
>    aggregate, (as created by anon_aggr_name_format).  */
> #define IDENTIFIER_ANON_P(NODE) \
> @@ -5120,6 +5125,7 @@ extern const_tree strip_invariant_refs (const_tree);
> extern tree lhd_gcc_personality (void);
> extern void assign_assembler_name_if_needed (tree);
> extern bool warn_deprecated_use (tree, tree);
> +extern void error_unavailable_use (tree, tree);
> extern void cache_integer_cst (tree);
> extern const char *combined_fn_name (combined_fn);
>
> -- 
> 2.24.1
Martin Sebor Nov. 29, 2020, 11 p.m. UTC | #2
On 11/10/20 12:38 PM, Iain Sandoe wrote:
> Hi Jospeh,
> 
> Joseph Myers <joseph@codesourcery.com> wrote:
> 
>> This patch seems to be missing documentation for the new attribute in
>> extend.texi.
> 
> Apologies, for that omission, revised patch includes the documentation and
> also addresses Richi’s comments.
> 
> documentation patch tested with “make pdf” and visual inspection of the output.
> 
> OK now?
> thanks
> Iain
> 
> —— commit message.
> 
> If an interface is marked 'deprecated' then, presumably, at some point it
> will be withdrawn and no longer available.  The 'unavailable' attribute
> makes it possible to mark up interfaces to indicate this status.

(Sorry I'm a little with my feedback.  Hopefully it's still helpful.)

Making an interface unavailable isn't the intent of deprecation in
standards like C, C++, or POSIX.  Rather, the intended next stage
after deprecation is to make the interface available for other uses,
either by the standards themselves, or by implementations, or by
programs (if its name is not in the reserved namespace).  So unless
you have other kinds of deprecation in mind this doesn't seem like
a fitting use case.

> It is used
> quite extensively in some codebases where a single set of headers can be used
> to permit code generation for multiple system versions.

This sounds like a different use case than the next stage after
deprecation.  I haven't come across it but I imagine its purpose
is to foster portability between variants or flavors (rather
than versions) of APSs?  Say one set of APIs for a feature-rich
desktop variant of an OS versus a subset of the same APIs for
an embedded, more limited variant of the same OS.

> 
>  From a configuration perspective, it also allows a compile test to determine
> that an interface is missing - rather than requiring a link test.
> 
> The implementation follows the pattern of attribute deprecated, but produces
> an error (where deprecation produces a warning).
> 
> This attribute has been implemented in clang for some years.

The Clang manual says the attribute is useful in conjunction with
the enable_if and overloadble attributes in C, to remove overloads
of C functions from the overload set.  I'm trying to think how
the GCC implementation of the attribute might be useful in
the subset of cases that don't depend on the other two attributes
but I'm coming up empty (exceot for the different variants of
an API use case that seems rather esoteric).  It seems to me
the use case is close to #pragma GCC poison except more nunanced
(i.e., it doesn't poison a name but its uses in the given namespace,
as in functions, types, members, etc.)

...
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index 5f1e3bf8a2e..a6b5867808a 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -2865,6 +2865,19 @@ types (@pxref{Variable Attributes}, @pxref{Type Attributes}.)
>   The message attached to the attribute is affected by the setting of
>   the @option{-fmessage-length} option.
>   
> +@item unavailable
> +@itemx unavailable (@var{msg})
> +@cindex @code{unavailable} function attribute
> +The @code{deprecated} attribute results in an error if the function
              ^^^^^^^^^^

This should presumably read unavailable.

> +is used anywhere in the source file.  This is useful when identifying
> +functions that have been removed from a particular variation of an
> +interface.  Other than emitting an error rather than a warning, the
> +@code{unavailable} attribute behaves in the same manner as
> +@code{deprecated}.
> +
> +The @code{unavailable} attribute can also be used for variables and
> +types (@pxref{Variable Attributes}, @pxref{Type Attributes}.)
> +
>   @item error ("@var{message}")
>   @itemx warning ("@var{message}")
>   @cindex @code{error} function attribute
> @@ -7223,6 +7236,22 @@ types (@pxref{Common Function Attributes},
>   The message attached to the attribute is affected by the setting of
>   the @option{-fmessage-length} option.
>   
> +@item unavailable
> +@itemx unavailable (@var{msg})
> +@cindex @code{unavailable} variable attribute
> +The @code{unavailable} attribute indicates that the variable so marked
> +is not available, if it is used anywhere in the source file.  It behaves
> +in the same manner as the @code{deprecated} attribute except that the
> +compiler will emit an error rather than a warning.
> +
> +It is expected that items marked as @code{deprecated} will eventually be
> +withdrawn from interfaces, and then become unavailable.  This attribute
> +allows for marking them appropriately.

In Clang, declaring a member unavailable doesn't have the same effect
as withdrawing it (which I would interpret as removing).  The member
still takes up space, so if this patch does the same I think this
effect should be made clear here.

Like attribute deprecated, I suspect attribute unavailable in GCC
will also be subject to the same catch 22 of marking up both a type
and its uses in APIs.  E.g., in:

   struct __attribute__ ((deprecated)) A { ... };

   struct B {
     // Clang accepts this w/o warning, GCC warns.
     struct A a __attribute__ ((deprecated));
     int i;
   };

With unavailable, the problem will be made worse due to the error.
To be generally usable, I think GCC needs to change to behave more
like Clang.  As a motivating example, consider the deprecated POSIX
getitimer API:

   struct itimerval { int it_interval, it_value; };
   int getitimer (int, struct itimerval*);
   int setitimer (int, const struct itimerval*, struct itimerval*);

All three names are deprecated and so using each one alone outside
the header that declares them should trigger a deprecation message;
the declarations themselves must not.  If/when the APIs are removed,
marking them unavailable in the headers must likewise not trigger
errors.

Martin
Iain Sandoe Nov. 30, 2020, 1:56 a.m. UTC | #3
Hi Martin,

Martin Sebor via Gcc-patches <gcc-patches@gcc.gnu.org> wrote:

> On 11/10/20 12:38 PM, Iain Sandoe wrote:

>> —— commit message.
>> If an interface is marked 'deprecated' then, presumably, at some point it
>> will be withdrawn and no longer available.  The 'unavailable' attribute
>> makes it possible to mark up interfaces to indicate this status.

> Making an interface unavailable isn't the intent of deprecation in
> standards like C, C++, or POSIX.  Rather, the intended next stage
> after deprecation is to make the interface available for other uses,
> either by the standards themselves, or by implementations, or by
> programs (if its name is not in the reserved namespace).  So unless
> you have other kinds of deprecation in mind this doesn't seem like
> a fitting use case.

coming at things from the standards perspective .. perhaps.

.. however, in the first set of cases above, one never needs to indicate
   unavailability  since the entity never becomes unavailable, it changes
   meaning.

In practice, we don’t have markup of keywords etc. to indicate this (such
deprecation has been handled specifically in the FEs).

The practical (and actual) use of these attributes is in describing the  
lifecycle
of system APIs.

In that context, one could see it being used to describe APIs withdrawn in,  
say,
a Posix standard (as implemented by a given system or according to specific
compile-time flags).

>> It is used
>> quite extensively in some codebases where a single set of headers can be  
>> used
>> to permit code generation for multiple system versions.
>
> This sounds like a different use case than the next stage after
> deprecation.  I haven't come across it but I imagine its purpose
> is to foster portability between variants or flavors (rather
> than versions) of APSs?  Say one set of APIs for a feature-rich
> desktop variant of an OS versus a subset of the same APIs for
> an embedded, more limited variant of the same OS.

In the case of Darwin, the compilers are capable of targeting multiple  
versions
of the system (one doesn’t need a separate GCC or clang to target each  
version,
there is a -mmacosx-version-min= flag that allows the target version to be  
specified
on the command line).

Rather than install many versions of headers (and libraries) for all the  
system
versions, the designers elected to have one copy with markup that describes  
the
availability of APIs.

the lifecycle is typically:

introduced from version P.Q (ergo, unavailable before that)
perhaps deprecated at version R.S (ergo the user should be warned)
withdrawn at version X.Y (and unavailable thereafter).

The headers contain macros that are expanded according to the version
for which the code is being compiled - to produce deprecation warnings or
unavailability errors if such APIs are *used* in the code.

>> From a configuration perspective, it also allows a compile test to  
>> determine
>> that an interface is missing - rather than requiring a link test.
>> The implementation follows the pattern of attribute deprecated, but  
>> produces
>> an error (where deprecation produces a warning).
>> This attribute has been implemented in clang for some years.
>
> The Clang manual says the attribute is useful in conjunction with
> the enable_if and overloadble attributes in C, to remove overloads
> of C functions from the overload set.  I'm trying to think how
> the GCC implementation of the attribute might be useful in
> the subset of cases that don't depend on the other two attributes
> but I'm coming up empty (exceot for the different variants of
> an API use case that seems rather esoteric).  It seems to me
> the use case is close to #pragma GCC poison except more nunanced
> (i.e., it doesn't poison a name but its uses in the given namespace,
> as in functions, types, members, etc.)

Hopefully, the description above clarifies the intent.

It is possible, even likely, that the implementations in clang have  
additional
capabilities above those implemented in this first cut - and perhaps we might
adopt some of those in follow-on work.

however the immediate use-case is the hundreds of instances where APIs
are marked up in the manner I’ve outlined above.

>> +@cindex @code{unavailable} function attribute
>> +The @code{deprecated} attribute results in an error if the function
>             ^^^^^^^^^^
>
> This should presumably read unavailable.

yes, good catch

>> +It is expected that items marked as @code{deprecated} will eventually be
>> +withdrawn from interfaces, and then become unavailable.  This attribute
>> +allows for marking them appropriately.
>
> In Clang, declaring a member unavailable doesn't have the same effect
> as withdrawing it (which I would interpret as removing).  The member
> still takes up space, so if this patch does the same I think this
> effect should be made clear here.

The intent is to match the behaviour of clang sufficiently closely that GCC  
can
make more effective use of the system headers on Darwin platforms (and, of
course, allow for the same kind of life cycle markup on other platforms).

> Like attribute deprecated, I suspect attribute unavailable in GCC
> will also be subject to the same catch 22 of marking up both a type
> and its uses in APIs.  E.g., in:
>
>  struct __attribute__ ((deprecated)) A { ... };
>
>  struct B {
>    // Clang accepts this w/o warning, GCC warns.
>    struct A a __attribute__ ((deprecated));
>    int i;
>  };
>
> With unavailable, the problem will be made worse due to the error.

so this is a bug in GCC’s availability attributes (not specifically caused by
this patch) - addressing it for deprecation would naturally carry over to the
unavailable case.

> To be generally usable, I think GCC needs to change to behave more
> like Clang.  As a motivating example, consider the deprecated POSIX
> getitimer API:
>
>  struct itimerval { int it_interval, it_value; };
>  int getitimer (int, struct itimerval*);
>  int setitimer (int, const struct itimerval*, struct itimerval*);
>
> All three names are deprecated and so using each one alone outside
> the header that declares them should trigger a deprecation message;
> the declarations themselves must not.  If/when the APIs are removed,
> marking them unavailable in the headers must likewise not trigger
> errors.

Is there a PR for this ?
(IMO it’s still a separate issue, curing the problem for deprecation will  
also
  cure it for unavailability).

thanks for the review,
Iain
Nathan Sidwell Nov. 30, 2020, 1:27 p.m. UTC | #4
On 11/27/20 3:49 PM, Iain Sandoe wrote:
> Hi
> 
> I believe this version addressed Joseph’s and Richard’s comment, but it
> C++ review.

ok.  Found a doc nit though ...



+@item unavailable
+@itemx unavailable (@var{msg})
+@cindex @code{unavailable} function attribute
+The @code{deprecated} attribute results in an error if the function
...........^^^^^^^^^^  unavailable
Martin Sebor Nov. 30, 2020, 5:04 p.m. UTC | #5
On 11/29/20 6:56 PM, Iain Sandoe wrote:
> Hi Martin,
> 
> Martin Sebor via Gcc-patches <gcc-patches@gcc.gnu.org> wrote:
> 
>> On 11/10/20 12:38 PM, Iain Sandoe wrote:
> 
>>> —— commit message.
>>> If an interface is marked 'deprecated' then, presumably, at some 
>>> point it
>>> will be withdrawn and no longer available.  The 'unavailable' attribute
>>> makes it possible to mark up interfaces to indicate this status.
> 
>> Making an interface unavailable isn't the intent of deprecation in
>> standards like C, C++, or POSIX.  Rather, the intended next stage
>> after deprecation is to make the interface available for other uses,
>> either by the standards themselves, or by implementations, or by
>> programs (if its name is not in the reserved namespace).  So unless
>> you have other kinds of deprecation in mind this doesn't seem like
>> a fitting use case.
> 
> coming at things from the standards perspective .. perhaps.
> 
> .. however, in the first set of cases above, one never needs to indicate
>    unavailability  since the entity never becomes unavailable, it changes
>    meaning.
> 
> In practice, we don’t have markup of keywords etc. to indicate this (such
> deprecation has been handled specifically in the FEs).
> 
> The practical (and actual) use of these attributes is in describing the 
> lifecycle
> of system APIs.
> 
> In that context, one could see it being used to describe APIs withdrawn 
> in, say,
> a Posix standard (as implemented by a given system or according to specific
> compile-time flags).

Just for the sake of clarity, to make sure we are both talking about
the same thing, the term POSIX uses for "withdrawn" when referring
to APIs is removed.  Withdrawn is a term that ISO tends to apply to
whole standards.  For example, ISO 9945:2003 AKA SUSv3 is a withdrawn
revision of POSIX (likewise, C99 and C11 are withdrawn revisions of
C).

With that, the APIs that have been removed in recent POSIX versions
would not be appropriately described by the attribute, either on
paper, in the POSIX spec (if the hypothetical case when the attribute
was specified the C standard), or in implementations.  The APIs simply
don't exist and so are open to use by programs (unless they are in
the reserved namespace), or by implementations as extensions.
The latter typically means that the APIs are defined (i.e., can
continue to be linked to by legacy applications) but not declared
in the system's header so they can be defined by programs.  Declaring
removed APIs with the unavailable attribute would prevent that and so
would not be conforming.

An example from C (not yet POSIX) is the gets() function that was
removed in C11 (it's still SUSv4).  It's only declared in <stdio.h>
when targeting older versions of C and C++.  Because in more recent
versions of the standards gets is not a reserved name it's valid
for programs to declare symbols named gets (of any kind).  So
declaring gets in <stdio.h> with attribute unavailable would be
nonconforming.

My point here is not necessarily to object to the idea behind
the attribute but to draw attention to the fact that it's not
suitable for standard APIs.

>>> It is used
>>> quite extensively in some codebases where a single set of headers can 
>>> be used
>>> to permit code generation for multiple system versions.
>>
>> This sounds like a different use case than the next stage after
>> deprecation.  I haven't come across it but I imagine its purpose
>> is to foster portability between variants or flavors (rather
>> than versions) of APSs?  Say one set of APIs for a feature-rich
>> desktop variant of an OS versus a subset of the same APIs for
>> an embedded, more limited variant of the same OS.
> 
> In the case of Darwin, the compilers are capable of targeting multiple 
> versions
> of the system (one doesn’t need a separate GCC or clang to target each 
> version,
> there is a -mmacosx-version-min= flag that allows the target version to 
> be specified
> on the command line).
> 
> Rather than install many versions of headers (and libraries) for all the 
> system
> versions, the designers elected to have one copy with markup that 
> describes the
> availability of APIs.
> 
> the lifecycle is typically:
> 
> introduced from version P.Q (ergo, unavailable before that)
> perhaps deprecated at version R.S (ergo the user should be warned)
> withdrawn at version X.Y (and unavailable thereafter).
> 
> The headers contain macros that are expanded according to the version
> for which the code is being compiled - to produce deprecation warnings or
> unavailability errors if such APIs are *used* in the code.

I see.  That seems like a Darwin-specific use case, or one that may
be suitable for proprietary APIs not covered by the three language
standards (C, C++, or POSIX) where the attribute could only be used
this way as a non-conforming extension.  I suppose it might also be
applicable in the case of Linux distributions whose vendors support
multiple versions simultaneously, although I haven't come across
this use case.

> 
>>> From a configuration perspective, it also allows a compile test to 
>>> determine
>>> that an interface is missing - rather than requiring a link test.
>>> The implementation follows the pattern of attribute deprecated, but 
>>> produces
>>> an error (where deprecation produces a warning).
>>> This attribute has been implemented in clang for some years.
>>
>> The Clang manual says the attribute is useful in conjunction with
>> the enable_if and overloadble attributes in C, to remove overloads
>> of C functions from the overload set.  I'm trying to think how
>> the GCC implementation of the attribute might be useful in
>> the subset of cases that don't depend on the other two attributes
>> but I'm coming up empty (exceot for the different variants of
>> an API use case that seems rather esoteric).  It seems to me
>> the use case is close to #pragma GCC poison except more nunanced
>> (i.e., it doesn't poison a name but its uses in the given namespace,
>> as in functions, types, members, etc.)
> 
> Hopefully, the description above clarifies the intent.
> 
> It is possible, even likely, that the implementations in clang have 
> additional
> capabilities above those implemented in this first cut - and perhaps we 
> might
> adopt some of those in follow-on work.
> 
> however the immediate use-case is the hundreds of instances where APIs
> are marked up in the manner I’ve outlined above.
> 
>>> +@cindex @code{unavailable} function attribute
>>> +The @code{deprecated} attribute results in an error if the function
>>             ^^^^^^^^^^
>>
>> This should presumably read unavailable.
> 
> yes, good catch
> 
>>> +It is expected that items marked as @code{deprecated} will 
>>> eventually be
>>> +withdrawn from interfaces, and then become unavailable.  This attribute
>>> +allows for marking them appropriately.
>>
>> In Clang, declaring a member unavailable doesn't have the same effect
>> as withdrawing it (which I would interpret as removing).  The member
>> still takes up space, so if this patch does the same I think this
>> effect should be made clear here.
> 
> The intent is to match the behaviour of clang sufficiently closely that 
> GCC can
> make more effective use of the system headers on Darwin platforms (and, of
> course, allow for the same kind of life cycle markup on other platforms).
> 
>> Like attribute deprecated, I suspect attribute unavailable in GCC
>> will also be subject to the same catch 22 of marking up both a type
>> and its uses in APIs.  E.g., in:
>>
>>  struct __attribute__ ((deprecated)) A { ... };
>>
>>  struct B {
>>    // Clang accepts this w/o warning, GCC warns.
>>    struct A a __attribute__ ((deprecated));
>>    int i;
>>  };
>>
>> With unavailable, the problem will be made worse due to the error.
> 
> so this is a bug in GCC’s availability attributes (not specifically 
> caused by
> this patch) - addressing it for deprecation would naturally carry over 
> to the
> unavailable case.
> 
>> To be generally usable, I think GCC needs to change to behave more
>> like Clang.  As a motivating example, consider the deprecated POSIX
>> getitimer API:
>>
>>  struct itimerval { int it_interval, it_value; };
>>  int getitimer (int, struct itimerval*);
>>  int setitimer (int, const struct itimerval*, struct itimerval*);
>>
>> All three names are deprecated and so using each one alone outside
>> the header that declares them should trigger a deprecation message;
>> the declarations themselves must not.  If/when the APIs are removed,
>> marking them unavailable in the headers must likewise not trigger
>> errors.
> 
> Is there a PR for this ?
> (IMO it’s still a separate issue, curing the problem for deprecation 
> will also
>   cure it for unavailability).

There are a number of bugs for attribute deprecated.  The one that's
probably the closest to this problem is:
   https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61754

It's been open since 2014.  Bugs about attributes don't tend to get
a lot of attention, either in GCC or within standards committees
(the standard form came to C from C++ where all attributes are
unspecified for templates).  Which is in part why I view with
skepticism enhancements built on top of the same poorly specified
foundation.

Martin

> 
> thanks for the review,
> Iain
> 
>
Iain Sandoe Dec. 1, 2020, 9:07 p.m. UTC | #6
Hi Martin,

Martin Sebor <msebor@gmail.com> wrote:

> On 11/29/20 6:56 PM, Iain Sandoe wrote:

>> Martin Sebor via Gcc-patches <gcc-patches@gcc.gnu.org> wrote:
>>> On 11/10/20 12:38 PM, Iain Sandoe wrote:
>>>> —— commit message.
>>>> If an interface is marked 'deprecated' then, presumably, at some point  
>>>> it
>>>> will be withdrawn and no longer available.  The 'unavailable' attribute
>>>> makes it possible to mark up interfaces to indicate this status.
>>> Making an interface unavailable isn't the intent of deprecation in
>>> standards like C, C++, or POSIX.  Rather, the intended next stage
>>> after deprecation is to make the interface available for other uses,
>>> either by the standards themselves, or by implementations, or by
>>> programs (if its name is not in the reserved namespace).  So unless
>>> you have other kinds of deprecation in mind this doesn't seem like
>>> a fitting use case.
>> coming at things from the standards perspective .. perhaps.
>> .. however, in the first set of cases above, one never needs to indicate
>>   unavailability  since the entity never becomes unavailable, it changes
>>   meaning.
>> In practice, we don’t have markup of keywords etc. to indicate this (such
>> deprecation has been handled specifically in the FEs).
>> The practical (and actual) use of these attributes is in describing the  
>> lifecycle
>> of system APIs.
>> In that context, one could see it being used to describe APIs withdrawn  
>> in, say,
>> a Posix standard (as implemented by a given system or according to  
>> specific
>> compile-time flags).
>
> Just for the sake of clarity, to make sure we are both talking about
> the same thing, the term POSIX uses for "withdrawn" when referring
> to APIs is removed.  Withdrawn is a term that ISO tends to apply to
> whole standards.  For example, ISO 9945:2003 AKA SUSv3 is a withdrawn
> revision of POSIX (likewise, C99 and C11 are withdrawn revisions of
> C).
>
> With that, the APIs that have been removed in recent POSIX versions
> would not be appropriately described by the attribute, either on
> paper, in the POSIX spec (if the hypothetical case when the attribute
> was specified the C standard), or in implementations.  The APIs simply
> don't exist and so are open to use by programs (unless they are in
> the reserved namespace), or by implementations as extensions.
> The latter typically means that the APIs are defined (i.e., can
> continue to be linked to by legacy applications) but not declared
> in the system's header so they can be defined by programs.  Declaring
> removed APIs with the unavailable attribute would prevent that and so
> would not be conforming.
>
> An example from C (not yet POSIX) is the gets() function that was
> removed in C11 (it's still SUSv4).  It's only declared in <stdio.h>
> when targeting older versions of C and C++.  Because in more recent
> versions of the standards gets is not a reserved name it's valid
> for programs to declare symbols named gets (of any kind).  So
> declaring gets in <stdio.h> with attribute unavailable would be
> nonconforming.
>
> My point here is not necessarily to object to the idea behind
> the attribute but to draw attention to the fact that it's not
> suitable for standard APIs.

I understand (and have no disagreement with) that this is not a facility
that could be used as an enforcement of standard’s compliance.

====

However, imagine that you are a system integrator, and you have a
system which supports the API of SUSv3 and some of the additions of
later systems; let’s say you want to help your users avoid use of APIs
which would be unavailable on a SUSv3 system

You could mark up your implementation’s headers such that when presented
with some compile flags, it made the toolchain emit an error if the user
attempted to use APIs that are not present in SUSv3.

This is not actually a completely inane goal - despite that SUSv3 is  
retired -
in reality (AFAICS) there is only one system that’s qualified to UNIX7.

>>>> It is used
>>>> quite extensively in some codebases where a single set of headers can  
>>>> be used
>>>> to permit code generation for multiple system versions.
>>>
>>> This sounds like a different use case than the next stage after
>>> deprecation.  I haven't come across it but I imagine its purpose
>>> is to foster portability between variants or flavors (rather
>>> than versions) of APSs?  Say one set of APIs for a feature-rich
>>> desktop variant of an OS versus a subset of the same APIs for
>>> an embedded, more limited variant of the same OS.
>> In the case of Darwin, the compilers are capable of targeting multiple  
>> versions
>> of the system (one doesn’t need a separate GCC or clang to target each  
>> version,
>> there is a -mmacosx-version-min= flag that allows the target version to  
>> be specified
>> on the command line).
>> Rather than install many versions of headers (and libraries) for all the  
>> system
>> versions, the designers elected to have one copy with markup that  
>> describes the
>> availability of APIs.
>> the lifecycle is typically:
>> introduced from version P.Q (ergo, unavailable before that)
>> perhaps deprecated at version R.S (ergo the user should be warned)
>> withdrawn at version X.Y (and unavailable thereafter).
>> The headers contain macros that are expanded according to the version
>> for which the code is being compiled - to produce deprecation warnings or
>> unavailability errors if such APIs are *used* in the code.
>
> I see.  That seems like a Darwin-specific use case, or one that may
> be suitable for proprietary APIs not covered by the three language
> standards (C, C++, or POSIX) where the attribute could only be used
> this way as a non-conforming extension.  I suppose it might also be
> applicable in the case of Linux distributions whose vendors support
> multiple versions simultaneously, although I haven't come across
> this use case.

Well, I resisted the ‘easy way out’ [which would be to make this a  
target-only
attribute]  since it does appear to me to be useful in a wider context.

Absent facilites are never used, of course.

>>>> From a configuration perspective, it also allows a compile test to  
>>>> determine
>>>> that an interface is missing - rather than requiring a link test.
>>>> The implementation follows the pattern of attribute deprecated, but  
>>>> produces
>>>> an error (where deprecation produces a warning).
>>>> This attribute has been implemented in clang for some years.
>>>
>>> The Clang manual says the attribute is useful in conjunction with
>>> the enable_if and overloadble attributes in C, to remove overloads
>>> of C functions from the overload set.  I'm trying to think how
>>> the GCC implementation of the attribute might be useful in
>>> the subset of cases that don't depend on the other two attributes
>>> but I'm coming up empty (exceot for the different variants of
>>> an API use case that seems rather esoteric).  It seems to me
>>> the use case is close to #pragma GCC poison except more nunanced
>>> (i.e., it doesn't poison a name but its uses in the given namespace,
>>> as in functions, types, members, etc.)
>> Hopefully, the description above clarifies the intent.
>> It is possible, even likely, that the implementations in clang have  
>> additional
>> capabilities above those implemented in this first cut - and perhaps we  
>> might
>> adopt some of those in follow-on work.
>> however the immediate use-case is the hundreds of instances where APIs
>> are marked up in the manner I’ve outlined above.
>>>> +@cindex @code{unavailable} function attribute
>>>> +The @code{deprecated} attribute results in an error if the function
>>>             ^^^^^^^^^^
>>>
>>> This should presumably read unavailable.
>> yes, good catch
>>>> +It is expected that items marked as @code{deprecated} will eventually  
>>>> be
>>>> +withdrawn from interfaces, and then become unavailable.  This attribute
>>>> +allows for marking them appropriately.
>>>
>>> In Clang, declaring a member unavailable doesn't have the same effect
>>> as withdrawing it (which I would interpret as removing).  The member
>>> still takes up space, so if this patch does the same I think this
>>> effect should be made clear here.
>> The intent is to match the behaviour of clang sufficiently closely that  
>> GCC can
>> make more effective use of the system headers on Darwin platforms (and, of
>> course, allow for the same kind of life cycle markup on other platforms).
>>> Like attribute deprecated, I suspect attribute unavailable in GCC
>>> will also be subject to the same catch 22 of marking up both a type
>>> and its uses in APIs.  E.g., in:
>>>
>>>  struct __attribute__ ((deprecated)) A { ... };
>>>
>>>  struct B {
>>>    // Clang accepts this w/o warning, GCC warns.
>>>    struct A a __attribute__ ((deprecated));
>>>    int i;
>>>  };
>>>
>>> With unavailable, the problem will be made worse due to the error.
>> so this is a bug in GCC’s availability attributes (not specifically  
>> caused by
>> this patch) - addressing it for deprecation would naturally carry over  
>> to the
>> unavailable case.
>>> To be generally usable, I think GCC needs to change to behave more
>>> like Clang.  As a motivating example, consider the deprecated POSIX
>>> getitimer API:
>>>
>>>  struct itimerval { int it_interval, it_value; };
>>>  int getitimer (int, struct itimerval*);
>>>  int setitimer (int, const struct itimerval*, struct itimerval*);
>>>
>>> All three names are deprecated and so using each one alone outside
>>> the header that declares them should trigger a deprecation message;
>>> the declarations themselves must not.  If/when the APIs are removed,
>>> marking them unavailable in the headers must likewise not trigger
>>> errors.
>> Is there a PR for this ?
>> (IMO it’s still a separate issue, curing the problem for deprecation  
>> will also
>>  cure it for unavailability).
>
> There are a number of bugs for attribute deprecated.  The one that's
> probably the closest to this problem is:
>  https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61754
>
> It's been open since 2014.  Bugs about attributes don't tend to get
> a lot of attention, either in GCC or within standards committees
> (the standard form came to C from C++ where all attributes are
> unspecified for templates).

(wearing my BSI and WG21 committee member hat) I agree it’s unfortunate
when stuff gets out of sync and lack of discussion means that a poor solution
is implemented.

>  Which is in part why I view with
> skepticism enhancements built on top of the same poorly specified
> foundation.

(now wearing my system implementor’s hat) Implementations need escape
routes to avoid being stifled by the timescales inherent in standardisation.

This particular facility (whether considered ill-specified or not) has been  
in
active and successful use from the time of gcc-4.2.1 (and is available to all
clang’s platforms - not restricted to Darwin).

So, in general, skepticism is reasonable - in this specific case there is  
usage
'in the wild' for a significant number of years.

cheers
Iain
Iain Sandoe Dec. 21, 2020, 7:58 p.m. UTC | #7
Hi Joseph,

Nathan has approved this from the C++ perspective (and Martin said that his
comments were  "not necessarily to object to the idea behind the attribute  
but
to draw attention to the fact that it's not suitable for standard APIs.”)

So, I wonder, do you have more comments on the revised patch, or is this
now OK for master?

(reiterating, that we are not inventing something new here - but rather  
trying
  to achieve compatibility with implementations that already have the attrinbute).

thanks
Iain

Iain Sandoe <iain@sandoe.co.uk> wrote:


> Martin Sebor <msebor@gmail.com> wrote:
>
>> On 11/29/20 6:56 PM, Iain Sandoe wrote:
>
>>> Martin Sebor via Gcc-patches <gcc-patches@gcc.gnu.org> wrote:
>>>> On 11/10/20 12:38 PM, Iain Sandoe wrote:
>>>>> —— commit message.
>>>>> If an interface is marked 'deprecated' then, presumably, at some  
>>>>> point it
>>>>> will be withdrawn and no longer available.  The 'unavailable' attribute
>>>>> makes it possible to mark up interfaces to indicate this status.
>>>> Making an interface unavailable isn't the intent of deprecation in
>>>> standards like C, C++, or POSIX.  Rather, the intended next stage
>>>> after deprecation is to make the interface available for other uses,
>>>> either by the standards themselves, or by implementations, or by
>>>> programs (if its name is not in the reserved namespace).  So unless
>>>> you have other kinds of deprecation in mind this doesn't seem like
>>>> a fitting use case.
>>> coming at things from the standards perspective .. perhaps.
>>> .. however, in the first set of cases above, one never needs to indicate
>>>  unavailability  since the entity never becomes unavailable, it changes
>>>  meaning.
>>> In practice, we don’t have markup of keywords etc. to indicate this (such
>>> deprecation has been handled specifically in the FEs).
>>> The practical (and actual) use of these attributes is in describing the  
>>> lifecycle
>>> of system APIs.
>>> In that context, one could see it being used to describe APIs withdrawn  
>>> in, say,
>>> a Posix standard (as implemented by a given system or according to  
>>> specific
>>> compile-time flags).
>>
>> Just for the sake of clarity, to make sure we are both talking about
>> the same thing, the term POSIX uses for "withdrawn" when referring
>> to APIs is removed.  Withdrawn is a term that ISO tends to apply to
>> whole standards.  For example, ISO 9945:2003 AKA SUSv3 is a withdrawn
>> revision of POSIX (likewise, C99 and C11 are withdrawn revisions of
>> C).
>>
>> With that, the APIs that have been removed in recent POSIX versions
>> would not be appropriately described by the attribute, either on
>> paper, in the POSIX spec (if the hypothetical case when the attribute
>> was specified the C standard), or in implementations.  The APIs simply
>> don't exist and so are open to use by programs (unless they are in
>> the reserved namespace), or by implementations as extensions.
>> The latter typically means that the APIs are defined (i.e., can
>> continue to be linked to by legacy applications) but not declared
>> in the system's header so they can be defined by programs.  Declaring
>> removed APIs with the unavailable attribute would prevent that and so
>> would not be conforming.
>>
>> An example from C (not yet POSIX) is the gets() function that was
>> removed in C11 (it's still SUSv4).  It's only declared in <stdio.h>
>> when targeting older versions of C and C++.  Because in more recent
>> versions of the standards gets is not a reserved name it's valid
>> for programs to declare symbols named gets (of any kind).  So
>> declaring gets in <stdio.h> with attribute unavailable would be
>> nonconforming.
>>
>> My point here is not necessarily to object to the idea behind
>> the attribute but to draw attention to the fact that it's not
>> suitable for standard APIs.
>
> I understand (and have no disagreement with) that this is not a facility
> that could be used as an enforcement of standard’s compliance.
>
> ====
>
> However, imagine that you are a system integrator, and you have a
> system which supports the API of SUSv3 and some of the additions of
> later systems; let’s say you want to help your users avoid use of APIs
> which would be unavailable on a SUSv3 system
>
> You could mark up your implementation’s headers such that when presented
> with some compile flags, it made the toolchain emit an error if the user
> attempted to use APIs that are not present in SUSv3.
>
> This is not actually a completely inane goal - despite that SUSv3 is  
> retired -
> in reality (AFAICS) there is only one system that’s qualified to UNIX7.
>
>>>>> It is used
>>>>> quite extensively in some codebases where a single set of headers can  
>>>>> be used
>>>>> to permit code generation for multiple system versions.
>>>>
>>>> This sounds like a different use case than the next stage after
>>>> deprecation.  I haven't come across it but I imagine its purpose
>>>> is to foster portability between variants or flavors (rather
>>>> than versions) of APSs?  Say one set of APIs for a feature-rich
>>>> desktop variant of an OS versus a subset of the same APIs for
>>>> an embedded, more limited variant of the same OS.
>>> In the case of Darwin, the compilers are capable of targeting multiple  
>>> versions
>>> of the system (one doesn’t need a separate GCC or clang to target each  
>>> version,
>>> there is a -mmacosx-version-min= flag that allows the target version to  
>>> be specified
>>> on the command line).
>>> Rather than install many versions of headers (and libraries) for all  
>>> the system
>>> versions, the designers elected to have one copy with markup that  
>>> describes the
>>> availability of APIs.
>>> the lifecycle is typically:
>>> introduced from version P.Q (ergo, unavailable before that)
>>> perhaps deprecated at version R.S (ergo the user should be warned)
>>> withdrawn at version X.Y (and unavailable thereafter).
>>> The headers contain macros that are expanded according to the version
>>> for which the code is being compiled - to produce deprecation warnings or
>>> unavailability errors if such APIs are *used* in the code.
>>
>> I see.  That seems like a Darwin-specific use case, or one that may
>> be suitable for proprietary APIs not covered by the three language
>> standards (C, C++, or POSIX) where the attribute could only be used
>> this way as a non-conforming extension.  I suppose it might also be
>> applicable in the case of Linux distributions whose vendors support
>> multiple versions simultaneously, although I haven't come across
>> this use case.
>
> Well, I resisted the ‘easy way out’ [which would be to make this a  
> target-only
> attribute]  since it does appear to me to be useful in a wider context.
>
> Absent facilites are never used, of course.
>
>>>>> From a configuration perspective, it also allows a compile test to  
>>>>> determine
>>>>> that an interface is missing - rather than requiring a link test.
>>>>> The implementation follows the pattern of attribute deprecated, but  
>>>>> produces
>>>>> an error (where deprecation produces a warning).
>>>>> This attribute has been implemented in clang for some years.
>>>>
>>>> The Clang manual says the attribute is useful in conjunction with
>>>> the enable_if and overloadble attributes in C, to remove overloads
>>>> of C functions from the overload set.  I'm trying to think how
>>>> the GCC implementation of the attribute might be useful in
>>>> the subset of cases that don't depend on the other two attributes
>>>> but I'm coming up empty (exceot for the different variants of
>>>> an API use case that seems rather esoteric).  It seems to me
>>>> the use case is close to #pragma GCC poison except more nunanced
>>>> (i.e., it doesn't poison a name but its uses in the given namespace,
>>>> as in functions, types, members, etc.)
>>> Hopefully, the description above clarifies the intent.
>>> It is possible, even likely, that the implementations in clang have  
>>> additional
>>> capabilities above those implemented in this first cut - and perhaps we  
>>> might
>>> adopt some of those in follow-on work.
>>> however the immediate use-case is the hundreds of instances where APIs
>>> are marked up in the manner I’ve outlined above.
>>>>> +@cindex @code{unavailable} function attribute
>>>>> +The @code{deprecated} attribute results in an error if the function
>>>>            ^^^^^^^^^^
>>>>
>>>> This should presumably read unavailable.
>>> yes, good catch
>>>>> +It is expected that items marked as @code{deprecated} will  
>>>>> eventually be
>>>>> +withdrawn from interfaces, and then become unavailable.  This  
>>>>> attribute
>>>>> +allows for marking them appropriately.
>>>>
>>>> In Clang, declaring a member unavailable doesn't have the same effect
>>>> as withdrawing it (which I would interpret as removing).  The member
>>>> still takes up space, so if this patch does the same I think this
>>>> effect should be made clear here.
>>> The intent is to match the behaviour of clang sufficiently closely that  
>>> GCC can
>>> make more effective use of the system headers on Darwin platforms (and,  
>>> of
>>> course, allow for the same kind of life cycle markup on other platforms).
>>>> Like attribute deprecated, I suspect attribute unavailable in GCC
>>>> will also be subject to the same catch 22 of marking up both a type
>>>> and its uses in APIs.  E.g., in:
>>>>
>>>> struct __attribute__ ((deprecated)) A { ... };
>>>>
>>>> struct B {
>>>>   // Clang accepts this w/o warning, GCC warns.
>>>>   struct A a __attribute__ ((deprecated));
>>>>   int i;
>>>> };
>>>>
>>>> With unavailable, the problem will be made worse due to the error.
>>> so this is a bug in GCC’s availability attributes (not specifically  
>>> caused by
>>> this patch) - addressing it for deprecation would naturally carry over  
>>> to the
>>> unavailable case.
>>>> To be generally usable, I think GCC needs to change to behave more
>>>> like Clang.  As a motivating example, consider the deprecated POSIX
>>>> getitimer API:
>>>>
>>>> struct itimerval { int it_interval, it_value; };
>>>> int getitimer (int, struct itimerval*);
>>>> int setitimer (int, const struct itimerval*, struct itimerval*);
>>>>
>>>> All three names are deprecated and so using each one alone outside
>>>> the header that declares them should trigger a deprecation message;
>>>> the declarations themselves must not.  If/when the APIs are removed,
>>>> marking them unavailable in the headers must likewise not trigger
>>>> errors.
>>> Is there a PR for this ?
>>> (IMO it’s still a separate issue, curing the problem for deprecation  
>>> will also
>>> cure it for unavailability).
>>
>> There are a number of bugs for attribute deprecated.  The one that's
>> probably the closest to this problem is:
>> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61754
>>
>> It's been open since 2014.  Bugs about attributes don't tend to get
>> a lot of attention, either in GCC or within standards committees
>> (the standard form came to C from C++ where all attributes are
>> unspecified for templates).
>
> (wearing my BSI and WG21 committee member hat) I agree it’s unfortunate
> when stuff gets out of sync and lack of discussion means that a poor  
> solution
> is implemented.
>
>> Which is in part why I view with
>> skepticism enhancements built on top of the same poorly specified
>> foundation.
>
> (now wearing my system implementor’s hat) Implementations need escape
> routes to avoid being stifled by the timescales inherent in  
> standardisation.
>
> This particular facility (whether considered ill-specified or not) has  
> been in
> active and successful use from the time of gcc-4.2.1 (and is available to  
> all
> clang’s platforms - not restricted to Darwin).
>
> So, in general, skepticism is reasonable - in this specific case there is  
> usage
> 'in the wild' for a significant number of years.
>
> cheers
> Iain
Joseph Myers Jan. 4, 2021, 8:19 p.m. UTC | #8
On Mon, 21 Dec 2020, Iain Sandoe wrote:

> Hi Joseph,
> 
> Nathan has approved this from the C++ perspective (and Martin said that his
> comments were  "not necessarily to object to the idea behind the attribute but
> to draw attention to the fact that it's not suitable for standard APIs.”)
> 
> So, I wonder, do you have more comments on the revised patch, or is this
> now OK for master?

If <https://gcc.gnu.org/pipermail/gcc-patches/2020-November/558630.html> 
is the current version, one of the three copies of the attribute 
documentation (the one for it as a function attribute) starts with "The 
@code{deprecated} attribute results in an error", when it should refer to 
@code{unavailable} not @code{deprecated}.  I don't have further comments 
beyond that.
diff mbox series

Patch

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index f1680820ecd..067dfabe0a4 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -120,6 +120,8 @@  static tree handle_pure_attribute (tree *, tree, tree, int, bool *);
 static tree handle_tm_attribute (tree *, tree, tree, int, bool *);
 static tree handle_tm_wrap_attribute (tree *, tree, tree, int, bool *);
 static tree handle_novops_attribute (tree *, tree, tree, int, bool *);
+static tree handle_unavailable_attribute (tree *, tree, tree, int,
+					  bool *);
 static tree handle_vector_size_attribute (tree *, tree, tree, int,
 					  bool *) ATTRIBUTE_NONNULL(3);
 static tree handle_nonnull_attribute (tree *, tree, tree, int, bool *);
@@ -391,6 +393,8 @@  const struct attribute_spec c_common_attribute_table[] =
 			      handle_novops_attribute, NULL },
   { "deprecated",             0, 1, false, false, false, false,
 			      handle_deprecated_attribute, NULL },
+  { "unavailable",            0, 1, false, false, false, false,
+			      handle_unavailable_attribute, NULL },
   { "vector_size",	      1, 1, false, true, false, true,
 			      handle_vector_size_attribute, NULL },
   { "visibility",	      1, 1, false, false, false, false,
@@ -3737,6 +3741,71 @@  handle_deprecated_attribute (tree *node, tree name,
   return NULL_TREE;
 }
 
+/* Handle a "unavailable" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_unavailable_attribute (tree *node, tree name,
+			     tree args, int flags,
+			     bool *no_add_attrs)
+{
+  tree type = NULL_TREE;
+  int warn = 0;
+  tree what = NULL_TREE;
+
+  if (!args)
+    *no_add_attrs = true;
+  else if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+    {
+      error ("the message attached to %<unavailable%> is not a string");
+      *no_add_attrs = true;
+    }
+
+  if (DECL_P (*node))
+    {
+      tree decl = *node;
+      type = TREE_TYPE (decl);
+
+      if (TREE_CODE (decl) == TYPE_DECL
+	  || TREE_CODE (decl) == PARM_DECL
+	  || VAR_OR_FUNCTION_DECL_P (decl)
+	  || TREE_CODE (decl) == FIELD_DECL
+	  || TREE_CODE (decl) == CONST_DECL
+	  || objc_method_decl (TREE_CODE (decl)))
+	TREE_UNAVAILABLE (decl) = 1;
+      else
+	warn = 1;
+    }
+  else if (TYPE_P (*node))
+    {
+      if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
+	*node = build_variant_type_copy (*node);
+      TREE_UNAVAILABLE (*node) = 1;
+      type = *node;
+    }
+  else
+    warn = 1;
+
+  if (warn)
+    {
+      *no_add_attrs = true;
+      if (type && TYPE_NAME (type))
+	{
+	  if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
+	    what = TYPE_NAME (*node);
+	  else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
+		   && DECL_NAME (TYPE_NAME (type)))
+	    what = DECL_NAME (TYPE_NAME (type));
+	}
+      if (what)
+	warning (OPT_Wattributes, "%qE attribute ignored for %qE", name, what);
+      else
+	warning (OPT_Wattributes, "%qE attribute ignored", name);
+    }
+
+  return NULL_TREE;
+}
+
 /* Return the "base" type from TYPE that is suitable to apply attribute
    vector_size to by stripping arrays, function types, etc.  */
 static tree
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index f19c82c81dd..27c6f13e8f2 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -70,13 +70,16 @@  enum decl_context
   TYPENAME};			/* Typename (inside cast or sizeof)  */
 
 /* States indicating how grokdeclarator() should handle declspecs marked
-   with __attribute__((deprecated)).  An object declared as
-   __attribute__((deprecated)) suppresses warnings of uses of other
-   deprecated items.  */
+   with __attribute__((deprecated)) or __attribute__((unavailable)).
+   An object declared as __attribute__((unavailable)) should suppress
+   any reports of being declared  with unavailable or deprecated items.
+   An object declared as __attribute__((deprecated)) should suppress
+   warnings of uses of other deprecated items.  */
 
 enum deprecated_states {
   DEPRECATED_NORMAL,
-  DEPRECATED_SUPPRESS
+  DEPRECATED_SUPPRESS,
+  UNAVAILABLE_DEPRECATED_SUPPRESS
 };
 
 
@@ -2630,6 +2633,10 @@  merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype)
   if (TREE_DEPRECATED (newdecl))
     TREE_DEPRECATED (olddecl) = 1;
 
+  /* Merge unavailability.  */
+  if (TREE_UNAVAILABLE (newdecl))
+    TREE_UNAVAILABLE (olddecl) = 1;
+
   /* If a decl is in a system header and the other isn't, keep the one on the
      system header. Otherwise, keep source location of definition rather than
      declaration and of prototype rather than non-prototype unless that
@@ -4868,6 +4875,7 @@  quals_from_declspecs (const struct c_declspecs *specs)
 	      && !specs->typedef_p
 	      && !specs->explicit_signed_p
 	      && !specs->deprecated_p
+	      && !specs->unavailable_p
 	      && !specs->long_p
 	      && !specs->long_long_p
 	      && !specs->short_p
@@ -5070,9 +5078,14 @@  start_decl (struct c_declarator *declarator, struct c_declspecs *declspecs,
   tree expr = NULL_TREE;
   enum deprecated_states deprecated_state = DEPRECATED_NORMAL;
 
-  /* An object declared as __attribute__((deprecated)) suppresses
+  /* An object declared as __attribute__((unavailable)) suppresses
+     warnings and errors from __attribute__((deprecated/unavailable))
+     components.
+     An object declared as __attribute__((deprecated)) suppresses
      warnings of uses of other deprecated items.  */
-  if (lookup_attribute ("deprecated", attributes))
+  if (lookup_attribute ("unavailable", attributes))
+    deprecated_state = UNAVAILABLE_DEPRECATED_SUPPRESS;
+  else if (lookup_attribute ("deprecated", attributes))
     deprecated_state = DEPRECATED_SUPPRESS;
 
   decl = grokdeclarator (declarator, declspecs,
@@ -6190,7 +6203,7 @@  smallest_type_quals_location (const location_t *locations,
      set to indicate whether operands in *EXPR can be used in constant
      expressions.
    DEPRECATED_STATE is a deprecated_states value indicating whether
-   deprecation warnings should be suppressed.
+   deprecation/unavailability warnings should be suppressed.
 
    In the TYPENAME case, DECLARATOR is really an absolute declarator.
    It may also be so in the PARM case, for a prototype where the
@@ -6320,8 +6333,14 @@  grokdeclarator (const struct c_declarator *declarator,
   if (decl_context == NORMAL && !funcdef_flag && current_scope->parm_flag)
     decl_context = PARM;
 
-  if (declspecs->deprecated_p && deprecated_state != DEPRECATED_SUPPRESS)
-    warn_deprecated_use (declspecs->type, declspecs->decl_attr);
+  if (deprecated_state != UNAVAILABLE_DEPRECATED_SUPPRESS)
+    {
+      if (declspecs->unavailable_p)
+	error_unavailable_use (declspecs->type, declspecs->decl_attr);
+      else if (declspecs->deprecated_p
+		&& deprecated_state != DEPRECATED_SUPPRESS)
+	warn_deprecated_use (declspecs->type, declspecs->decl_attr);
+    }
 
   if ((decl_context == NORMAL || decl_context == FIELD)
       && current_scope == file_scope
@@ -10753,6 +10772,8 @@  declspecs_add_type (location_t loc, struct c_declspecs *specs,
   specs->typespec_kind = spec.kind;
   if (TREE_DEPRECATED (type))
     specs->deprecated_p = true;
+  if (TREE_UNAVAILABLE (type))
+    specs->unavailable_p = true;
 
   /* Handle type specifier keywords.  */
   if (TREE_CODE (type) == IDENTIFIER_NODE
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 1f783db7dbc..292874cc317 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -370,6 +370,8 @@  struct c_declspecs {
   BOOL_BITFIELD explicit_signed_p : 1;
   /* Whether the specifiers include a deprecated typedef.  */
   BOOL_BITFIELD deprecated_p : 1;
+  /* Whether the specifiers include an unavailable typedef.  */
+  BOOL_BITFIELD unavailable_p : 1;
   /* Whether the type defaulted to "int" because there were no type
      specifiers.  */
   BOOL_BITFIELD default_int_p : 1;
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 96840377d90..16b6f776214 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -2504,7 +2504,9 @@  build_component_ref (location_t loc, tree datum, tree component,
 	      || (use_datum_quals && TREE_THIS_VOLATILE (datum)))
 	    TREE_THIS_VOLATILE (ref) = 1;
 
-	  if (TREE_DEPRECATED (subdatum))
+	  if (TREE_UNAVAILABLE (subdatum))
+	    error_unavailable_use (subdatum, NULL_TREE);
+	  else if (TREE_DEPRECATED (subdatum))
 	    warn_deprecated_use (subdatum, NULL_TREE);
 
 	  datum = ref;
@@ -2789,7 +2791,9 @@  build_external_ref (location_t loc, tree id, bool fun, tree *type)
   if (TREE_TYPE (ref) == error_mark_node)
     return error_mark_node;
 
-  if (TREE_DEPRECATED (ref))
+  if (TREE_UNAVAILABLE (ref))
+    error_unavailable_use (ref, NULL_TREE);
+  else if (TREE_DEPRECATED (ref))
     warn_deprecated_use (ref, NULL_TREE);
 
   /* Recursive call does not count as usage.  */
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 9861be1f856..3df6c48e237 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -9078,7 +9078,7 @@  build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
 	  already_used = true;
 	}
       else
-	cp_warn_deprecated_use (fn, complain);
+	cp_handle_deprecated_or_unavailable (fn, complain);
 
       /* If we're creating a temp and we already have one, don't create a
 	 new one.  If we're not creating a temp but we get one, use
@@ -9148,7 +9148,7 @@  build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
           TREE_NO_WARNING (val) = 1;
 	}
 
-      cp_warn_deprecated_use (fn, complain);
+      cp_handle_deprecated_or_unavailable (fn, complain);
 
       return val;
     }
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index c03737294eb..f594d473325 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -5678,6 +5678,7 @@  type_build_ctor_call (tree t)
       tree fn = *iter;
       if (!DECL_ARTIFICIAL (fn)
 	  || TREE_DEPRECATED (fn)
+	  || TREE_UNAVAILABLE (fn)
 	  || DECL_DELETED_FN (fn))
 	return true;
     }
@@ -5706,6 +5707,7 @@  type_build_dtor_call (tree t)
       tree fn = *iter;
       if (!DECL_ARTIFICIAL (fn)
 	  || TREE_DEPRECATED (fn)
+	  || TREE_UNAVAILABLE (fn)
 	  || DECL_DELETED_FN (fn))
 	return true;
     }
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 052291c40fe..7912cf511ed 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6303,7 +6303,7 @@  extern bool is_list_ctor			(tree);
 extern void validate_conversion_obstack		(void);
 extern void mark_versions_used			(tree);
 extern bool unsafe_return_slot_p		(tree);
-extern bool cp_warn_deprecated_use		(tree, tsubst_flags_t = tf_warning_or_error);
+extern bool cp_handle_deprecated_or_unavailable (tree, tsubst_flags_t = tf_warning_or_error);
 extern void cp_warn_deprecated_use_scopes	(tree);
 extern tree get_function_version_dispatcher	(tree);
 
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 62648841ac3..bdd290f6d02 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -2350,6 +2350,10 @@  duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
       if (TREE_DEPRECATED (newdecl))
 	TREE_DEPRECATED (olddecl) = 1;
 
+      /* Merge unavailability.  */
+      if (TREE_UNAVAILABLE (newdecl))
+	TREE_UNAVAILABLE (olddecl) = 1;
+
       /* Preserve function specific target and optimization options */
       if (TREE_CODE (newdecl) == FUNCTION_DECL)
 	{
@@ -11398,20 +11402,24 @@  grokdeclarator (const cp_declarator *declarator,
   if (attrlist && *attrlist == error_mark_node)
     *attrlist = NULL_TREE;
 
-  /* An object declared as __attribute__((deprecated)) suppresses
-     warnings of uses of other deprecated items.  */
+  /* An object declared as __attribute__((unavailable)) suppresses
+     any reports of being declared with unavailable or deprecated
+     items.  An object declared as __attribute__((deprecated))
+     suppresses warnings of uses of other deprecated items.  */
   temp_override<deprecated_states> ds (deprecated_state);
-  if (attrlist && lookup_attribute ("deprecated", *attrlist))
+  if (attrlist && lookup_attribute ("unavailable", *attrlist))
+    deprecated_state = UNAVAILABLE_DEPRECATED_SUPPRESS;
+  else if (attrlist && lookup_attribute ("deprecated", *attrlist))
     deprecated_state = DEPRECATED_SUPPRESS;
 
-  cp_warn_deprecated_use (type);
+  cp_handle_deprecated_or_unavailable (type);
   if (type && TREE_CODE (type) == TYPE_DECL)
     {
       cp_warn_deprecated_use_scopes (CP_DECL_CONTEXT (type));
       typedef_decl = type;
       type = TREE_TYPE (typedef_decl);
       if (DECL_ARTIFICIAL (typedef_decl))
-	cp_warn_deprecated_use (type);
+	cp_handle_deprecated_or_unavailable (type);
     }
   /* No type at all: default to `int', and set DEFAULTED_INT
      because it was not a user-defined typedef.  */
@@ -14027,6 +14035,43 @@  type_is_deprecated (tree type)
   return NULL_TREE;
 }
 
+/* Returns an unavailable type used within TYPE, or NULL_TREE if none.  */
+
+static tree
+type_is_unavailable (tree type)
+{
+  enum tree_code code;
+  if (TREE_UNAVAILABLE (type))
+    return type;
+  if (TYPE_NAME (type))
+    {
+      if (TREE_UNAVAILABLE (TYPE_NAME (type)))
+	return type;
+      else
+	{
+	  cp_warn_deprecated_use_scopes (CP_DECL_CONTEXT (TYPE_NAME (type)));
+	  return NULL_TREE;
+	}
+    }
+
+  /* Do warn about using typedefs to a deprecated class.  */
+  if (OVERLOAD_TYPE_P (type) && type != TYPE_MAIN_VARIANT (type))
+    return type_is_deprecated (TYPE_MAIN_VARIANT (type));
+
+  code = TREE_CODE (type);
+
+  if (code == POINTER_TYPE || code == REFERENCE_TYPE
+      || code == OFFSET_TYPE || code == FUNCTION_TYPE
+      || code == METHOD_TYPE || code == ARRAY_TYPE)
+    return type_is_unavailable (TREE_TYPE (type));
+
+  if (TYPE_PTRMEMFUNC_P (type))
+    return type_is_unavailable
+      (TREE_TYPE (TREE_TYPE (TYPE_PTRMEMFUNC_FN_TYPE (type))));
+
+  return NULL_TREE;
+}
+
 /* Decode the list of parameter types for a function type.
    Given the list of things declared inside the parens,
    return a list of types.
@@ -14086,11 +14131,18 @@  grokparms (tree parmlist, tree *parms)
 
       if (type != error_mark_node)
 	{
-	  if (deprecated_state != DEPRECATED_SUPPRESS)
+	  if (deprecated_state != UNAVAILABLE_DEPRECATED_SUPPRESS)
+	    {
+	      tree unavailtype = type_is_unavailable (type);
+	      if (unavailtype)
+		cp_handle_deprecated_or_unavailable (unavailtype);
+	    }
+	  if (deprecated_state != DEPRECATED_SUPPRESS
+	      && deprecated_state != UNAVAILABLE_DEPRECATED_SUPPRESS)
 	    {
 	      tree deptype = type_is_deprecated (type);
 	      if (deptype)
-		cp_warn_deprecated_use (deptype);
+		cp_handle_deprecated_or_unavailable (deptype);
 	    }
 
 	  /* [dcl.fct] "A parameter with volatile-qualified type is
diff --git a/gcc/cp/decl.h b/gcc/cp/decl.h
index 3252dd8a011..475a00d8a2f 100644
--- a/gcc/cp/decl.h
+++ b/gcc/cp/decl.h
@@ -44,7 +44,8 @@  extern void name_unnamed_type (tree, tree);
 
 enum deprecated_states {
   DEPRECATED_NORMAL,
-  DEPRECATED_SUPPRESS
+  DEPRECATED_SUPPRESS,
+  UNAVAILABLE_DEPRECATED_SUPPRESS
 };
 
 extern enum deprecated_states deprecated_state;
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 1bc7b7e0197..590f8a3c157 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -1608,6 +1608,17 @@  cplus_decl_attributes (tree *decl, tree attributes, int flags)
 	if (*decl == pattern)
 	  TREE_DEPRECATED (tmpl) = true;
       }
+
+  /* Likewise, propagate unavailability out to the template.  */
+  if (TREE_UNAVAILABLE (*decl))
+    if (tree ti = get_template_info (*decl))
+      {
+	tree tmpl = TI_TEMPLATE (ti);
+	tree pattern = (TYPE_P (*decl) ? TREE_TYPE (tmpl)
+			: DECL_TEMPLATE_RESULT (tmpl));
+	if (*decl == pattern)
+	  TREE_UNAVAILABLE (tmpl) = true;
+      }
 }
 
 /* Walks through the namespace- or function-scope anonymous union
@@ -5389,14 +5400,47 @@  maybe_instantiate_decl (tree decl)
     }
 }
 
-/* Maybe warn if DECL is deprecated, subject to COMPLAIN.  Returns whether or
-   not a warning was emitted.  */
+/* Error if the DECL is unavailable (unless this is currently suppressed).
+   Maybe warn if DECL is deprecated, subject to COMPLAIN.  Returns true if
+   an error or warning was emitted.  */
 
 bool
-cp_warn_deprecated_use (tree decl, tsubst_flags_t complain)
+cp_handle_deprecated_or_unavailable (tree decl, tsubst_flags_t complain)
 {
-  if (!(complain & tf_warning) || !decl
-      || deprecated_state == DEPRECATED_SUPPRESS)
+  if (!decl)
+    return false;
+
+  if ((complain & tf_error)
+      && deprecated_state != UNAVAILABLE_DEPRECATED_SUPPRESS)
+    {
+      if (TREE_UNAVAILABLE (decl))
+	{
+	  error_unavailable_use (decl, NULL_TREE);
+	  return true;
+	}
+      else
+	{
+	  /* Perhaps this is an unavailable typedef.  */
+	  if (TYPE_P (decl)
+	      && TYPE_NAME (decl)
+	      && TREE_UNAVAILABLE (TYPE_NAME (decl)))
+	    {
+	      decl = TYPE_NAME (decl);
+	      /* Don't error within members of a unavailable type.  */
+	      if (TYPE_P (decl)
+		  && currently_open_class (decl))
+		return false;
+
+	      error_unavailable_use (decl, NULL_TREE);
+	      return true;
+	    }
+	}
+      /* Carry on to consider deprecatedness.  */
+    }
+
+  if (!(complain & tf_warning)
+      || deprecated_state == DEPRECATED_SUPPRESS
+      || deprecated_state == UNAVAILABLE_DEPRECATED_SUPPRESS)
     return false;
 
   if (!TREE_DEPRECATED (decl))
@@ -5455,7 +5499,7 @@  cp_warn_deprecated_use_scopes (tree scope)
 	 && scope != error_mark_node
 	 && scope != global_namespace)
     {
-      if (cp_warn_deprecated_use (scope))
+      if (cp_handle_deprecated_or_unavailable (scope))
 	return;
       if (TYPE_P (scope))
 	scope = CP_TYPE_CONTEXT (scope);
@@ -5567,7 +5611,7 @@  mark_used (tree decl, tsubst_flags_t complain)
       TREE_USED (decl) = true;
     }
 
-  cp_warn_deprecated_use (decl, complain);
+  cp_handle_deprecated_or_unavailable (decl, complain);
 
   /* We can only check DECL_ODR_USED on variables or functions with
      DECL_LANG_SPECIFIC set, and these are also the only decls that we
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 323d7424a83..00595c0365b 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -17170,18 +17170,26 @@  cp_parser_template_name (cp_parser* parser,
   /* If DECL is a template, then the name was a template-name.  */
   if (TREE_CODE (decl) == TEMPLATE_DECL)
     {
-      if (TREE_DEPRECATED (decl)
-	  && deprecated_state != DEPRECATED_SUPPRESS)
+      if ((TREE_DEPRECATED (decl) || TREE_UNAVAILABLE (decl))
+	  && deprecated_state != UNAVAILABLE_DEPRECATED_SUPPRESS)
 	{
 	  tree d = DECL_TEMPLATE_RESULT (decl);
 	  tree attr;
 	  if (TREE_CODE (d) == TYPE_DECL)
-	    attr = lookup_attribute ("deprecated",
-				     TYPE_ATTRIBUTES (TREE_TYPE (d)));
+	    attr = TYPE_ATTRIBUTES (TREE_TYPE (d));
 	  else
-	    attr = lookup_attribute ("deprecated",
-				     DECL_ATTRIBUTES (d));
-	  warn_deprecated_use (decl, attr);
+	    attr = DECL_ATTRIBUTES (d);
+	  if (TREE_UNAVAILABLE (decl))
+	    {
+	      attr = lookup_attribute ("unavailable", attr);
+	      error_unavailable_use (decl, attr);
+	    }
+	  else if (TREE_DEPRECATED (decl)
+		   && deprecated_state != DEPRECATED_SUPPRESS)
+	    {
+	      attr = lookup_attribute ("deprecated", attr);
+	      warn_deprecated_use (decl, attr);
+	    }
 	}
     }
   else
@@ -17432,7 +17440,9 @@  cp_parser_template_argument (cp_parser* parser)
     }
   if (cp_parser_parse_definitely (parser))
     {
-      if (TREE_DEPRECATED (argument))
+      if (TREE_UNAVAILABLE (argument))
+	error_unavailable_use (argument, NULL_TREE);
+      else if (TREE_DEPRECATED (argument))
 	warn_deprecated_use (argument, NULL_TREE);
       return argument;
     }
@@ -22930,9 +22940,9 @@  cp_parser_parameter_declaration_list (cp_parser* parser, cp_parser_flags flags)
 					   /*template_parm_p=*/false,
 					   &parenthesized_p);
 
-      /* We don't know yet if the enclosing context is deprecated, so wait
-	 and warn in grokparms if appropriate.  */
-      deprecated_state = DEPRECATED_SUPPRESS;
+      /* We don't know yet if the enclosing context is unavailable or deprecated,
+	 so wait and deal with it in grokparms if appropriate.  */
+      deprecated_state = UNAVAILABLE_DEPRECATED_SUPPRESS;
 
       if (parameter)
 	{
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 08e0c80f9b0..7d1e2fd27a6 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -2514,7 +2514,10 @@  build_class_member_access_expr (cp_expr object, tree member,
       member_scope = DECL_CLASS_CONTEXT (member);
       if (!mark_used (member, complain) && !(complain & tf_error))
 	return error_mark_node;
-      if (TREE_DEPRECATED (member))
+
+      if (TREE_UNAVAILABLE (member))
+	error_unavailable_use (member, NULL_TREE);
+      else if (TREE_DEPRECATED (member))
 	warn_deprecated_use (member, NULL_TREE);
     }
   else
@@ -3217,7 +3220,9 @@  finish_class_member_access_expr (cp_expr object, tree name, bool template_p,
 	}
     }
 
-  if (TREE_DEPRECATED (member))
+  if (TREE_UNAVAILABLE (member))
+    error_unavailable_use (member, NULL_TREE);
+  else if (TREE_DEPRECATED (member))
     warn_deprecated_use (member, NULL_TREE);
 
   if (template_p)
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 445e2a211c8..4d014dac88f 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -2293,7 +2293,7 @@  build_functional_cast_1 (location_t loc, tree exp, tree parms,
       type = TREE_TYPE (exp);
 
       if (DECL_ARTIFICIAL (exp))
-	cp_warn_deprecated_use (type);
+	cp_handle_deprecated_or_unavailable (type);
     }
   else
     type = exp;
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 5f1e3bf8a2e..a6b5867808a 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2865,6 +2865,19 @@  types (@pxref{Variable Attributes}, @pxref{Type Attributes}.)
 The message attached to the attribute is affected by the setting of
 the @option{-fmessage-length} option.
 
+@item unavailable
+@itemx unavailable (@var{msg})
+@cindex @code{unavailable} function attribute
+The @code{deprecated} attribute results in an error if the function
+is used anywhere in the source file.  This is useful when identifying
+functions that have been removed from a particular variation of an
+interface.  Other than emitting an error rather than a warning, the
+@code{unavailable} attribute behaves in the same manner as
+@code{deprecated}.
+
+The @code{unavailable} attribute can also be used for variables and
+types (@pxref{Variable Attributes}, @pxref{Type Attributes}.)
+
 @item error ("@var{message}")
 @itemx warning ("@var{message}")
 @cindex @code{error} function attribute
@@ -7223,6 +7236,22 @@  types (@pxref{Common Function Attributes},
 The message attached to the attribute is affected by the setting of
 the @option{-fmessage-length} option.
 
+@item unavailable
+@itemx unavailable (@var{msg})
+@cindex @code{unavailable} variable attribute
+The @code{unavailable} attribute indicates that the variable so marked
+is not available, if it is used anywhere in the source file.  It behaves
+in the same manner as the @code{deprecated} attribute except that the
+compiler will emit an error rather than a warning.
+
+It is expected that items marked as @code{deprecated} will eventually be
+withdrawn from interfaces, and then become unavailable.  This attribute
+allows for marking them appropriately.
+
+The @code{unavailable} attribute can also be used for functions and
+types (@pxref{Common Function Attributes},
+@pxref{Common Type Attributes}).
+
 @item mode (@var{mode})
 @cindex @code{mode} variable attribute
 This attribute specifies the data type for the declaration---whichever
@@ -8249,6 +8278,17 @@  variables (@pxref{Function Attributes}, @pxref{Variable Attributes}.)
 The message attached to the attribute is affected by the setting of
 the @option{-fmessage-length} option.
 
+@item unavailable
+@itemx unavailable (@var{msg})
+@cindex @code{unavailable} type attribute
+The @code{unavailable} attribute behaves in the same manner as the
+@code{deprecated} one, but emits an error rather than a warning.  It is
+used to indicate that a (perhaps previously @code{deprecated}) type is
+no longer usable.
+
+The @code{unavailable} attribute can also be used for functions and
+variables (@pxref{Function Attributes}, @pxref{Variable Attributes}.)
+
 @item designated_init
 @cindex @code{designated_init} type attribute
 This attribute may only be applied to structure types.  It indicates
@@ -8716,6 +8756,12 @@  of the deprecated enumerator, to enable users to easily find further
 information about why the enumerator is deprecated, or what they should
 do instead.  Note that the warnings only occurs for uses.
 
+@item unavailable
+@cindex @code{unavailable} enumerator attribute
+The @code{unavailable} attribute results in an error if the enumerator
+is used anywhere in the source file.  In other respects it behaves in the
+same manner as the @code{deprecated} attribute.
+
 @end table
 
 @node Statement Attributes
diff --git a/gcc/objc/objc-act.c b/gcc/objc/objc-act.c
index b9ed32d51d0..fc247220a62 100644
--- a/gcc/objc/objc-act.c
+++ b/gcc/objc/objc-act.c
@@ -1275,6 +1275,7 @@  objc_add_property_declaration (location_t location, tree decl,
   TREE_TYPE (property_decl) = TREE_TYPE (decl);
   DECL_SOURCE_LOCATION (property_decl) = DECL_SOURCE_LOCATION (decl);
   TREE_DEPRECATED (property_decl) = TREE_DEPRECATED (decl);
+  TREE_UNAVAILABLE (property_decl) = TREE_UNAVAILABLE (decl);
 
   /* Add property-specific information.  */
   PROPERTY_NAME (property_decl) = DECL_NAME (decl);
@@ -1390,6 +1391,7 @@  maybe_make_artificial_property_decl (tree interface, tree implementation,
       TREE_TYPE (property_decl) = type;
       DECL_SOURCE_LOCATION (property_decl) = input_location;
       TREE_DEPRECATED (property_decl) = 0;
+      TREE_UNAVAILABLE (property_decl) = 0;
       DECL_ARTIFICIAL (property_decl) = 1;
 
       /* Add property-specific information.  Note that one of
@@ -1668,7 +1670,7 @@  objc_maybe_build_component_ref (tree object, tree property_ident)
     {
       tree expression;
       tree getter_call;
-      tree deprecated_method_prototype = NULL_TREE;
+      tree method_prototype_avail = NULL_TREE;
 
       /* We have an additional nasty problem here; if this
 	 PROPERTY_REF needs to become a 'getter', then the conversion
@@ -1702,10 +1704,10 @@  objc_maybe_build_component_ref (tree object, tree property_ident)
 	      is deprecated, but record the fact that the getter is
 	      deprecated by setting PROPERTY_REF_DEPRECATED_GETTER to
 	      the method prototype.  */
-	   &deprecated_method_prototype);
+	   &method_prototype_avail);
 
       expression = build4 (PROPERTY_REF, TREE_TYPE(x), object, x, getter_call,
-			   deprecated_method_prototype);
+			   method_prototype_avail);
       SET_EXPR_LOCATION (expression, input_location);
       TREE_SIDE_EFFECTS (expression) = 1;
 
@@ -1755,7 +1757,9 @@  objc_build_class_component_ref (tree class_name, tree property_ident)
     }
   else
     {
-      if (TREE_DEPRECATED (rtype))
+      if (TREE_UNAVAILABLE (rtype))
+	error ("class %qE is unavailable", class_name);
+      else if (TREE_DEPRECATED (rtype))
 	warning (OPT_Wdeprecated_declarations, "class %qE is deprecated", class_name);
     }
 
@@ -1767,17 +1771,17 @@  objc_build_class_component_ref (tree class_name, tree property_ident)
     {
       tree expression;
       tree getter_call;
-      tree deprecated_method_prototype = NULL_TREE;
+      tree method_prototype_avail = NULL_TREE;
 
       if (PROPERTY_HAS_NO_GETTER (x))
 	getter_call = NULL_TREE;
       else
 	getter_call = objc_finish_message_expr
 	  (object, PROPERTY_GETTER_NAME (x), NULL_TREE,
-	   &deprecated_method_prototype);
+	   &method_prototype_avail);
 
       expression = build4 (PROPERTY_REF, TREE_TYPE(x), object, x, getter_call,
-			   deprecated_method_prototype);
+			   method_prototype_avail);
       SET_EXPR_LOCATION (expression, input_location);
       TREE_SIDE_EFFECTS (expression) = 1;
 
@@ -4548,6 +4552,8 @@  build_private_template (tree klass)
       /* Copy the attributes from the class to the type.  */
       if (TREE_DEPRECATED (klass))
 	TREE_DEPRECATED (record) = 1;
+      if (TREE_UNAVAILABLE (klass))
+	TREE_UNAVAILABLE (record) = 1;
     }
 }
 
@@ -4973,6 +4979,7 @@  objc_decl_method_attributes (tree *node, tree attributes, int flags)
 	  tree name = TREE_PURPOSE (attribute);
 
 	  if (is_attribute_p  ("deprecated", name)
+	      || is_attribute_p ("unavailable", name)
 	      || is_attribute_p ("sentinel", name)
 	      || is_attribute_p ("noreturn", name))
 	    {
@@ -5438,9 +5445,9 @@  lookup_method_in_hash_lists (tree sel_name, int is_class)
    C++ template functions, it is called from 'build_expr_from_tree'
    (in decl2.c) after RECEIVER and METHOD_PARAMS have been expanded.
 
-   If the DEPRECATED_METHOD_PROTOTYPE argument is NULL, then we warn
+   If the method_prototype_avail argument is NULL, then we warn
    if the method being used is deprecated.  If it is not NULL, instead
-   of deprecating, we set *DEPRECATED_METHOD_PROTOTYPE to the method
+   of deprecating, we set *method_prototype_avail to the method
    prototype that was used and is deprecated.  This is useful for
    getter calls that are always generated when compiling dot-syntax
    expressions, even if they may not be used.  In that case, we don't
@@ -5449,7 +5456,7 @@  lookup_method_in_hash_lists (tree sel_name, int is_class)
    used.  */
 tree
 objc_finish_message_expr (tree receiver, tree sel_name, tree method_params,
-			  tree *deprecated_method_prototype)
+			  tree *method_prototype_avail)
 {
   tree method_prototype = NULL_TREE, rprotos = NULL_TREE, rtype;
   tree retval, class_tree;
@@ -5761,10 +5768,17 @@  objc_finish_message_expr (tree receiver, tree sel_name, tree method_params,
 	 In practice this makes sense since casting an object to 'id'
 	 is often used precisely to turn off warnings associated with
 	 the object being of a particular class.  */
-      if (TREE_DEPRECATED (method_prototype) && rtype != NULL_TREE)
+      if (TREE_UNAVAILABLE (method_prototype) && rtype != NULL_TREE)
 	{
-	  if (deprecated_method_prototype)
-	    *deprecated_method_prototype = method_prototype;
+	  if (method_prototype_avail)
+	    *method_prototype_avail = method_prototype;
+	  else
+	    error_unavailable_use (method_prototype, NULL_TREE);
+	}
+      else if (TREE_DEPRECATED (method_prototype) && rtype != NULL_TREE)
+	{
+	  if (method_prototype_avail)
+	    *method_prototype_avail = method_prototype;
 	  else
 	    warn_deprecated_use (method_prototype, NULL_TREE);
 	}
@@ -6936,7 +6950,9 @@  start_class (enum tree_code code, tree class_name, tree super_name,
 	}
       else
 	{
-	  if (TREE_DEPRECATED (super_interface))
+	  if (TREE_UNAVAILABLE (super_interface))
+	    error ("class %qE is not available", super);
+	  else if (TREE_DEPRECATED (super_interface))
 	    warning (OPT_Wdeprecated_declarations, "class %qE is deprecated",
 		     super);
 	  super_name = super;
@@ -7040,7 +7056,9 @@  start_class (enum tree_code code, tree class_name, tree super_name,
 	      /* TODO: Document what the objc_exception attribute is/does.  */
 	      /* We handle the 'deprecated', 'visibility' and (undocumented)
 		 'objc_exception' attributes.  */
-	      if (is_attribute_p  ("deprecated", name))
+	      if (is_attribute_p  ("unavailable", name))
+		TREE_UNAVAILABLE (klass) = 1;
+	      else if (is_attribute_p  ("deprecated", name))
 		TREE_DEPRECATED (klass) = 1;
 	      else if (is_attribute_p  ("objc_exception", name))
 		CLASS_HAS_EXCEPTION_ATTR (klass) = 1;
@@ -7069,7 +7087,9 @@  start_class (enum tree_code code, tree class_name, tree super_name,
 	  }
 	else
 	  {
-	    if (TREE_DEPRECATED (class_category_is_assoc_with))
+	    if (TREE_UNAVAILABLE (class_category_is_assoc_with))
+	      error ("class %qE is unavailable", class_name);
+	    else if (TREE_DEPRECATED (class_category_is_assoc_with))
 	      warning (OPT_Wdeprecated_declarations, "class %qE is deprecated",
 		       class_name);
 
@@ -8096,6 +8116,7 @@  finish_class (tree klass)
 		else
 		  objc_add_method (objc_interface_context, getter_decl, false, false);
 		TREE_DEPRECATED (getter_decl) = TREE_DEPRECATED (x);
+		TREE_UNAVAILABLE (getter_decl) = TREE_UNAVAILABLE (x);
 		METHOD_PROPERTY_CONTEXT (getter_decl) = x;
 	      }
 
@@ -8140,6 +8161,7 @@  finish_class (tree klass)
 		    else
 		      objc_add_method (objc_interface_context, setter_decl, false, false);
 		    TREE_DEPRECATED (setter_decl) = TREE_DEPRECATED (x);
+		    TREE_UNAVAILABLE (setter_decl) = TREE_UNAVAILABLE (x);
 		    METHOD_PROPERTY_CONTEXT (setter_decl) = x;
 		  }
 	      }
@@ -8193,7 +8215,9 @@  lookup_protocol (tree ident, bool warn_if_deprecated, bool definition_required)
   for (chain = protocol_chain; chain; chain = TREE_CHAIN (chain))
     if (ident == PROTOCOL_NAME (chain))
       {
-	if (warn_if_deprecated && TREE_DEPRECATED (chain))
+	if (TREE_UNAVAILABLE (chain))
+	  error ("protocol %qE is unavailable", PROTOCOL_NAME (chain));
+	else if (warn_if_deprecated && TREE_DEPRECATED (chain))
 	  {
 	    /* It would be nice to use warn_deprecated_use() here, but
 	       we are using TREE_CHAIN (which is supposed to be the
@@ -8218,6 +8242,7 @@  void
 objc_declare_protocol (tree name, tree attributes)
 {
   bool deprecated = false;
+  bool unavailable = false;
 
 #ifdef OBJCPLUS
   if (current_namespace != global_namespace) {
@@ -8236,6 +8261,8 @@  objc_declare_protocol (tree name, tree attributes)
 
 	  if (is_attribute_p  ("deprecated", name))
 	    deprecated = true;
+	  else if (is_attribute_p  ("unavailable", name))
+	    unavailable = true;
 	  else
 	    warning (OPT_Wattributes, "%qE attribute directive ignored", name);
 	}
@@ -8260,6 +8287,8 @@  objc_declare_protocol (tree name, tree attributes)
 	  TYPE_ATTRIBUTES (protocol) = attributes;
 	  if (deprecated)
 	    TREE_DEPRECATED (protocol) = 1;
+	  if (unavailable)
+	    TREE_UNAVAILABLE (protocol) = 1;
 	}
     }
 }
@@ -8269,6 +8298,7 @@  start_protocol (enum tree_code code, tree name, tree list, tree attributes)
 {
   tree protocol;
   bool deprecated = false;
+  bool unavailable = false;
 
 #ifdef OBJCPLUS
   if (current_namespace != global_namespace) {
@@ -8287,6 +8317,8 @@  start_protocol (enum tree_code code, tree name, tree list, tree attributes)
 
 	  if (is_attribute_p  ("deprecated", name))
 	    deprecated = true;
+	  else if (is_attribute_p  ("unavailable", name))
+	    unavailable = true;
 	  else
 	    warning (OPT_Wattributes, "%qE attribute directive ignored", name);
 	}
@@ -8326,6 +8358,8 @@  start_protocol (enum tree_code code, tree name, tree list, tree attributes)
       TYPE_ATTRIBUTES (protocol) = attributes;
       if (deprecated)
 	TREE_DEPRECATED (protocol) = 1;
+      if (unavailable)
+	TREE_UNAVAILABLE (protocol) = 1;
     }
 
   return protocol;
@@ -8855,6 +8889,8 @@  really_start_method (tree method,
 		 warnings are produced), but just in case.  */
 	      if (TREE_DEPRECATED (proto))
 		TREE_DEPRECATED (method) = 1;
+	      if (TREE_UNAVAILABLE (proto))
+		TREE_UNAVAILABLE (method) = 1;
 
 	      /* If the method in the @interface was marked as
 		 'noreturn', mark the function implementing the method
@@ -9586,12 +9622,17 @@  objc_gimplify_property_ref (tree *expr_p)
       return;
     }
 
+  /* FIXME, this should be a label indicating availability in general.  */
   if (PROPERTY_REF_DEPRECATED_GETTER (*expr_p))
     {
-      /* PROPERTY_REF_DEPRECATED_GETTER contains the method prototype
+      if (TREE_UNAVAILABLE (PROPERTY_REF_DEPRECATED_GETTER (*expr_p)))
+	error_unavailable_use (PROPERTY_REF_DEPRECATED_GETTER (*expr_p),
+			       NULL_TREE);
+      else
+	/* PROPERTY_REF_DEPRECATED_GETTER contains the method prototype
 	 that is deprecated.  */
-      warn_deprecated_use (PROPERTY_REF_DEPRECATED_GETTER (*expr_p),
-			   NULL_TREE);
+	warn_deprecated_use (PROPERTY_REF_DEPRECATED_GETTER (*expr_p),
+			     NULL_TREE);
     }
 
   call_exp = getter;
diff --git a/gcc/print-tree.c b/gcc/print-tree.c
index 17c88f81770..1443d3f5c39 100644
--- a/gcc/print-tree.c
+++ b/gcc/print-tree.c
@@ -364,6 +364,8 @@  print_node (FILE *file, const char *prefix, tree node, int indent,
     fputs (code == CALL_EXPR ? " must-tail-call" : " static", file);
   if (TREE_DEPRECATED (node))
     fputs (" deprecated", file);
+  if (TREE_UNAVAILABLE (node))
+    fputs (" unavailable", file);
   if (TREE_VISITED (node))
     fputs (" visited", file);
 
diff --git a/gcc/testsuite/g++.dg/ext/attr-unavailable-1.C b/gcc/testsuite/g++.dg/ext/attr-unavailable-1.C
new file mode 100644
index 00000000000..862651f6cbf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-unavailable-1.C
@@ -0,0 +1,113 @@ 
+/* Test __attribute__ ((unavailable)) */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+typedef int INT1 __attribute__((unavailable));
+typedef INT1 INT2 __attribute__ ((__unavailable__));
+
+typedef INT1 INT1a; 			/* { dg-error "'INT1' is unavailable" "" } */
+typedef INT1 INT1b __attribute__ ((unavailable));
+
+INT1 should_be_unavailable; 		/* { dg-error "'INT1' is unavailable" "" } */
+INT1a should_not_be_unavailable;
+
+INT1 f1(void) __attribute__ ((unavailable));
+INT1 f2(void) { return 0; }		/* { dg-error "'INT1' is unavailable" "" } */
+
+INT2 f3(void) __attribute__ ((__unavailable__));
+INT2 f4(void) { return 0; }		/* { dg-error "'INT2' is unavailable" "" } */
+int f5(INT2 x);				/* { dg-error "'INT2' is unavailable" "" } */
+int f6(INT2 x) __attribute__ ((__unavailable__));
+
+typedef enum Color {red, green, blue} Color __attribute__((unavailable));
+
+int g1;
+int g2 __attribute__ ((unavailable));
+int g3 __attribute__ ((__unavailable__));
+Color k;				/* { dg-error "'Color' is unavailable" "" } */
+
+typedef struct {
+  int field1;
+  int field2 __attribute__ ((unavailable));
+  int field3;
+  int field4 __attribute__ ((__unavailable__));
+  union {
+    int field5;
+    int field6 __attribute__ ((unavailable));
+  } u1;
+  int field7:1;
+  int field8:1 __attribute__ ((unavailable));
+  union {
+    int field9;
+    int field10;
+  } u2 __attribute__ ((unavailable));
+} S1;
+
+int func1()
+{
+   INT1 w;				/* { dg-error "'INT1' is unavailable" "" } */
+   int x __attribute__ ((unavailable));
+   int y __attribute__ ((__unavailable__));
+   int z;
+   int (*pf)() = f1;			/* { dg-error "'INT1 f1\\(\\)' is unavailable" "" } */
+
+   z = w + x + y + g1 + g2 + g3;	/* { dg-error "'x' is unavailable" "" } */
+					/* { dg-error "'y' is unavailable" "y" { target *-*-* } .-1 } */
+					/* { dg-error "'g2' is unavailable" "g2" { target *-*-* } .-2 } */
+					/* { dg-error "'g3' is unavailable" "g3" { target *-*-* } .-3 } */
+   return f1(); 			/* { dg-error "'INT1 f1\\(\\)' is unavailable" "f1" } */
+}
+
+int func2(S1 *p)
+{
+  S1 lp;
+
+  if (p->field1)
+     return p->field2;			/* { dg-error "'S1::field2' is unavailable" "" } */
+  else if (lp.field4)			/* { dg-error "'S1::field4' is unavailable" "" } */
+     return p->field3;
+
+  p->u1.field5 = g1 + p->field7;
+  p->u2.field9;				/* { dg-error "'S1::u2' is unavailable" "" } */
+  return p->u1.field6 + p->field8;	/* { dg-error "'S1::<unnamed union>::field6' is unavailable" "" } */
+					/* { dg-error "'S1::field8' is unavailable" "field8" { target *-*-* } .-1 } */
+}
+
+struct SS1 {
+  int x;
+  INT1 y; 				/* { dg-error "'INT1' is unavailable" "" } */
+} __attribute__ ((unavailable));
+
+struct SS1 *p1;				/* { dg-error "'SS1' is unavailable" "" } */
+
+struct __attribute__ ((__unavailable__)) SS2 {
+  int x;
+  INT1 y; 				/* { dg-error "'INT1' is unavailable" "" } */
+};
+
+struct SS2 *p2;				/* { dg-error "'SS2' is unavailable" "" } */
+
+#ifdef __cplusplus
+class T {
+  public:
+    void member1(int) __attribute__ ((unavailable));
+    void member2(INT1) __attribute__ ((__unavailable__));
+    int member3(T *);
+    int x;
+} __attribute__ ((unavailable));
+
+T *p3;				// { dg-error "'T' is unavailable" }
+
+inline void T::member1(int) {}
+
+int T::member3(T *p)		// { dg-error "'T' is unavailable" }
+{
+  p->member1(1);			/* { dg-error "'void T::member1\\(int\\)' is unavailable" "" } */
+  (*p).member1(2);			/* { dg-error "'void T::member1\\(int\\)' is unavailable" "" } */
+  p->member2(1);			/* { dg-error "'void T::member2\\(INT1\\)' is unavailable" "" } */
+  (*p).member2(2);			/* { dg-error "'void T::member2\\(INT1\\)' is unavailable" "" } */
+  p->member3(p);
+  (*p).member3(p);
+  return f1(); 				/* { dg-error "'INT1 f1\\(\\)' is unavailable" "" } */
+}
+#endif
diff --git a/gcc/testsuite/g++.dg/ext/attr-unavailable-2.C b/gcc/testsuite/g++.dg/ext/attr-unavailable-2.C
new file mode 100644
index 00000000000..3de5532817e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-unavailable-2.C
@@ -0,0 +1,10 @@ 
+/* Test __attribute__ ((unavailable)) */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+void func(void);
+void func(void) __attribute__((unavailable));
+
+void f(void) {
+  func(); /* { dg-error "'void func\\(\\)' is unavailable" } */
+}
diff --git a/gcc/testsuite/g++.dg/ext/attr-unavailable-3.C b/gcc/testsuite/g++.dg/ext/attr-unavailable-3.C
new file mode 100644
index 00000000000..1f267ea78c4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-unavailable-3.C
@@ -0,0 +1,14 @@ 
+/* Check operator with __attribute__((unavailable)) */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+struct Foo
+{
+  operator int() __attribute__((unavailable));
+};
+
+void g(void)
+{
+  Foo f;
+  (int)f; // { dg-error "'Foo::operator int\\(\\)' is unavailable" }
+}
diff --git a/gcc/testsuite/g++.dg/ext/attr-unavailable-4.C b/gcc/testsuite/g++.dg/ext/attr-unavailable-4.C
new file mode 100644
index 00000000000..b7f352ee3bd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-unavailable-4.C
@@ -0,0 +1,11 @@ 
+/* Test __attribute__ ((unavailable)) */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+struct B {
+    virtual int foo() __attribute__((unavailable));
+};
+
+int main(void) {
+  ((B*)0)->foo(); 		// { dg-error "unavailable" }
+}
diff --git a/gcc/testsuite/g++.dg/ext/attr-unavailable-5.C b/gcc/testsuite/g++.dg/ext/attr-unavailable-5.C
new file mode 100644
index 00000000000..3beea5d22c5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-unavailable-5.C
@@ -0,0 +1,6 @@ 
+/* Test __attribute__ ((unavailable)) */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+struct Foo { int i; } __attribute__ ((unavailable));
+void foo() { Foo f; }		// { dg-error "unavailable" }
diff --git a/gcc/testsuite/g++.dg/ext/attr-unavailable-6.C b/gcc/testsuite/g++.dg/ext/attr-unavailable-6.C
new file mode 100644
index 00000000000..8a57ea0d88c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-unavailable-6.C
@@ -0,0 +1,110 @@ 
+/* Test __attribute__ ((unavailable)) */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+typedef int INT1 __attribute__((unavailable("You can't use INT1")));
+typedef INT1 INT2 __attribute__ ((__unavailable__("You can't use INT2")));
+
+typedef INT1 INT1a; 			/* { dg-error "'INT1' is unavailable: You can't use INT1" "" } */
+
+INT1 should_be_unavailable; 		/* { dg-error "'INT1' is unavailable: You can't use INT1" "" } */
+INT1a should_not_be_unavailable;
+
+INT1 f1(void) __attribute__ ((unavailable("You can't use f1")));
+INT1 f2(void) { return 0; }		/* { dg-error "'INT1' is unavailable: You can't use INT1" "" } */
+
+INT2 f3(void) __attribute__ ((__unavailable__("You can't use f3")));
+INT2 f4(void) { return 0; }		/* { dg-error "'INT2' is unavailable: You can't use INT2" "" } */
+int f5(INT2 x);				/* { dg-error "'INT2' is unavailable" "" } */
+int f6(INT2 x) __attribute__ ((__unavailable__("You can't use f6")));
+
+typedef enum Color {red, green, blue} Color __attribute__((unavailable("You can't use Color")));
+
+int g1;
+int g2 __attribute__ ((unavailable("You can't use g2")));
+int g3 __attribute__ ((__unavailable__("You can't use g3")));
+Color k;				/* { dg-error "'Color' is unavailable: You can't use Color" "" } */
+
+typedef struct {
+  int field1;
+  int field2 __attribute__ ((unavailable("You can't use field2")));
+  int field3;
+  int field4 __attribute__ ((__unavailable__("You can't use field4")));
+  union {
+    int field5;
+    int field6 __attribute__ ((unavailable("You can't use field6")));
+  } u1;
+  int field7:1;
+  int field8:1 __attribute__ ((unavailable("You can't use field8")));
+  union {
+    int field9;
+    int field10;
+  } u2 __attribute__ ((unavailable("You can't use u2")));
+} S1;
+
+int func1()
+{
+   INT1 w;				/* { dg-error "'INT1' is unavailable: You can't use INT1" "" } */
+   int x __attribute__ ((unavailable("You can't use x")));
+   int y __attribute__ ((__unavailable__("You can't use y")));
+   int z;
+   int (*pf)() = f1;			/* { dg-error "'INT1 f1\\(\\)' is unavailable: You can't use f1" "" } */
+
+   z = w + x + y + g1 + g2 + g3;	/* { dg-error "'x' is unavailable: You can't use x" "" } */
+					/* { dg-error "'y' is unavailable: You can't use y" "y" { target *-*-* } .-1 } */
+					/* { dg-error "'g2' is unavailable: You can't use g2" "g2" { target *-*-* } .-2 } */
+					/* { dg-error "'g3' is unavailable: You can't use g3" "g3" { target *-*-* } .-3 } */
+   return f1(); 			/* { dg-error "'INT1 f1\\(\\)' is unavailable: You can't use f1" "f1" } */
+}
+
+int func2(S1 *p)
+{
+  S1 lp;
+
+  if (p->field1)
+     return p->field2;			/* { dg-error "'S1::field2' is unavailable: You can't use field2" "" } */
+  else if (lp.field4)			/* { dg-error "'S1::field4' is unavailable: You can't use field4" "" } */
+     return p->field3;
+
+  p->u1.field5 = g1 + p->field7;
+  p->u2.field9;				/* { dg-error "'S1::u2' is unavailable: You can't use u2" "" } */
+  return p->u1.field6 + p->field8;	/* { dg-error "'S1::<unnamed union>::field6' is unavailable: You can't use field6" "" } */
+					/* { dg-error "'S1::field8' is unavailable: You can't use field8" "field8" { target *-*-* } .-1 } */
+}
+
+struct SS1 {
+  int x;
+  INT1 y; 				/* { dg-error "'INT1' is unavailable: You can't use INT1" "" } */
+} __attribute__ ((unavailable("You can't use SS1")));
+
+struct SS1 *p1;				/* { dg-error "'SS1' is unavailable: You can't use SS1" "" } */
+
+struct __attribute__ ((__unavailable__("You can't use SS2"))) SS2 {
+  int x;
+  INT1 y; 				/* { dg-error "'INT1' is unavailable: You can't use INT1" "" } */
+};
+
+struct SS2 *p2;				/* { dg-error "'SS2' is unavailable: You can't use SS2" "" } */
+
+class T {
+  public:
+    void member1(int) __attribute__ ((unavailable("You can't use member1")));
+    void member2(INT1) __attribute__ ((__unavailable__("You can't use member2")));
+    int member3(T *);
+    int x;
+} __attribute__ ((unavailable("You can't use T")));
+
+T *p3;				// { dg-error "'T' is unavailable: You can't use T" }
+
+inline void T::member1(int) {}
+
+int T::member3(T *p)		// { dg-error "'T' is unavailable: You can't use T" }
+{
+  p->member1(1);			/* { dg-error "'void T::member1\\(int\\)' is unavailable: You can't use member1" "" } */
+  (*p).member1(2);			/* { dg-error "'void T::member1\\(int\\)' is unavailable: You can't use member1" "" } */
+  p->member2(1);			/* { dg-error "'void T::member2\\(INT1\\)' is unavailable: You can't use member2" "" } */
+  (*p).member2(2);			/* { dg-error "'void T::member2\\(INT1\\)' is unavailable: You can't use member2" "" } */
+  p->member3(p);
+  (*p).member3(p);
+  return f1(); 				/* { dg-error "'INT1 f1\\(\\)' is unavailable: You can't use f1" "" } */
+}
diff --git a/gcc/testsuite/g++.dg/ext/attr-unavailable-7.C b/gcc/testsuite/g++.dg/ext/attr-unavailable-7.C
new file mode 100644
index 00000000000..c061aa3b6a2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-unavailable-7.C
@@ -0,0 +1,19 @@ 
+/* Test __attribute__ ((unavailable)) */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+int g_nn;
+int& g_n __attribute__((unavailable)) = g_nn;
+
+void f()
+{
+  int f_nn;
+  int& f_n __attribute__((unavailable)) = f_nn;
+  f_n = 1;    // { dg-error "'f_n' is unavailable" }
+}
+
+int main()
+{
+  g_n = 1;    // { dg-error "'g_n' is unavailable" }
+  f();
+}
diff --git a/gcc/testsuite/g++.dg/ext/attr-unavailable-8.C b/gcc/testsuite/g++.dg/ext/attr-unavailable-8.C
new file mode 100644
index 00000000000..334a2cf7286
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-unavailable-8.C
@@ -0,0 +1,17 @@ 
+/* Test __attribute__ ((unavailable)) */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+class ToBeunavailable {
+} __attribute__ ((unavailable ("unavailable!")));
+
+typedef ToBeunavailable NotToBeunavailable; // { dg-error "'ToBeunavailable' is unavailable" }
+
+int main() {
+
+  ToBeunavailable();    // { dg-error "'ToBeunavailable' is unavailable" }
+  ToBeunavailable x;    // { dg-error "'ToBeunavailable' is unavailable" }
+
+  NotToBeunavailable();
+  NotToBeunavailable y;
+}
diff --git a/gcc/testsuite/g++.dg/ext/attr-unavailable-9.C b/gcc/testsuite/g++.dg/ext/attr-unavailable-9.C
new file mode 100644
index 00000000000..44161336e78
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-unavailable-9.C
@@ -0,0 +1,17 @@ 
+/* Test __attribute__ ((unavailable)) */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+template<typename T> struct __attribute__ ((unavailable)) S {};
+S<int> s;
+
+template <template <class> class T> struct A { };
+A<S> a;
+
+template <class T> void f() __attribute__ ((unavailable));
+
+int main()
+{
+  f<int>();			// { dg-error "unavailable" }
+  void (*p)() = f<char>;	// { dg-error "unavailable" }
+}
diff --git a/gcc/testsuite/gcc.dg/attr-unavailable-1.c b/gcc/testsuite/gcc.dg/attr-unavailable-1.c
new file mode 100644
index 00000000000..768214fcd3a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-unavailable-1.c
@@ -0,0 +1,88 @@ 
+/* Test __attribute__ ((unavailable)) */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+typedef int INT1 __attribute__((unavailable));
+typedef INT1 INT2 __attribute__ ((__unavailable__));
+
+typedef INT1 INT1a; 			/* { dg-error "'INT1' is unavailable" "" } */
+typedef INT1 INT1b __attribute__ ((unavailable));
+
+INT1 should_be_unavailable; 		/* { dg-error "'INT1' is unavailable" "" } */
+INT1a should_not_be_unavailable;
+
+INT1 f1(void) __attribute__ ((unavailable));
+INT1 f2(void) { return 0; }		/* { dg-error "'INT1' is unavailable" "" } */
+
+INT2 f3(void) __attribute__ ((__unavailable__));
+INT2 f4(void) { return 0; }		/* { dg-error "'INT2' is unavailable" "" } */
+int f5(INT2 x);				/* { dg-error "'INT2' is unavailable" "" } */
+int f6(INT2 x) __attribute__ ((__unavailable__)); /* { dg-error "'INT2' is unavailable" "" } */
+
+typedef enum {red, green, blue} Color __attribute__((unavailable));
+
+int g1;
+int g2 __attribute__ ((unavailable));
+int g3 __attribute__ ((__unavailable__));
+Color k;				/* { dg-error "'Color' is unavailable" "" } */
+
+typedef struct {
+  int field1;
+  int field2 __attribute__ ((unavailable));
+  int field3;
+  int field4 __attribute__ ((__unavailable__));
+  union {
+    int field5;
+    int field6 __attribute__ ((unavailable));
+  } u1;
+  int field7:1;
+  int field8:1 __attribute__ ((unavailable));
+  union {
+    int field9;
+    int field10;
+  } u2 __attribute__ ((unavailable));
+} S1;
+
+int func1()
+{
+   INT1 w;				/* { dg-error "'INT1' is unavailable" "" } */
+   int x __attribute__ ((unavailable));
+   int y __attribute__ ((__unavailable__));
+   int z;
+   int (*pf)() = f1;			/* { dg-error "'f1' is unavailable" "" } */
+
+   z = w + x + y + g1 + g2 + g3;	/* { dg-error "'x' is unavailable" "" } */
+					/* { dg-error "'y' is unavailable" "y" { target *-*-* } .-1 } */
+					/* { dg-error "'g2' is unavailable" "g2" { target *-*-* } .-2 } */
+					/* { dg-error "'g3' is unavailable" "g3" { target *-*-* } .-3 } */
+   return f1(); 			/* { dg-error "'f1' is unavailable" "f1" } */
+}
+
+int func2(S1 *p)
+{
+  S1 lp;
+
+  if (p->field1)
+     return p->field2;			/* { dg-error "'field2' is unavailable" "" } */
+  else if (lp.field4)			/* { dg-error "'field4' is unavailable" "" } */
+     return p->field3;
+
+  p->u1.field5 = g1 + p->field7;
+  p->u2.field9;				/* { dg-error "'u2' is unavailable" "" } */
+  return p->u1.field6 + p->field8;	/* { dg-error "'field6' is unavailable" "" } */
+					/* { dg-error "'field8' is unavailable" "field8" { target *-*-* } .-1 } */
+}
+
+struct SS1 {
+  int x;
+  INT1 y; 				/* { dg-error "'INT1' is unavailable" "" } */
+} __attribute__ ((unavailable));
+
+struct SS1 *p1;				/* { dg-error "'SS1' is unavailable" "" } */
+
+struct __attribute__ ((__unavailable__)) SS2 {
+  int x;
+  INT1 y; 				/* { dg-error "'INT1' is unavailable" "" } */
+};
+
+struct SS2 *p2;				/* { dg-error "'SS2' is unavailable" "" } */
diff --git a/gcc/testsuite/gcc.dg/attr-unavailable-2.c b/gcc/testsuite/gcc.dg/attr-unavailable-2.c
new file mode 100644
index 00000000000..303f973d5db
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-unavailable-2.c
@@ -0,0 +1,6 @@ 
+/* Test __attribute__((unavailable)).  Test types without names.  */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+struct { int a; } __attribute__((unavailable)) x; /* { dg-error "type is unavailable" } */
+typeof(x) y; /* { dg-error "type is unavailable" } */
diff --git a/gcc/testsuite/gcc.dg/attr-unavailable-3.c b/gcc/testsuite/gcc.dg/attr-unavailable-3.c
new file mode 100644
index 00000000000..7274c193f3f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-unavailable-3.c
@@ -0,0 +1,10 @@ 
+/* Test __attribute__((unavailable)). */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+void func(void);
+void func(void) __attribute__((unavailable));
+
+void f(void) {
+  func(); /* { dg-error "'func' is unavailable" } */
+}
diff --git a/gcc/testsuite/gcc.dg/attr-unavailable-4.c b/gcc/testsuite/gcc.dg/attr-unavailable-4.c
new file mode 100644
index 00000000000..9e39c50fec6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-unavailable-4.c
@@ -0,0 +1,88 @@ 
+/* Test __attribute__ ((unavailable("message"))) */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+typedef int INT1 __attribute__((unavailable("You can't use INT1")));
+typedef INT1 INT2 __attribute__ ((__unavailable__("You can't use INT2")));
+
+typedef INT1 INT1a; 			/* { dg-error "'INT1' is unavailable: You can't use INT1" "" } */
+typedef INT1 INT1b __attribute__ ((unavailable("You can't use INT1b")));
+
+INT1 should_be_unavailable; 		/* { dg-error "'INT1' is unavailable: You can't use INT1" "" } */
+INT1a should_not_be_unavailable;
+
+INT1 f1(void) __attribute__ ((unavailable("You can't use f1")));
+INT1 f2(void) { return 0; }		/* { dg-error "'INT1' is unavailable: You can't use INT1" "" } */
+
+INT2 f3(void) __attribute__ ((__unavailable__("You can't use f3")));
+INT2 f4(void) { return 0; }		/* { dg-error "'INT2' is unavailable: You can't use INT2" "" } */
+int f5(INT2 x);				/* { dg-error "'INT2' is unavailable: You can't use INT2" "" } */
+int f6(INT2 x) __attribute__ ((__unavailable__("You can't use f6"))); /* { dg-error "'INT2' is unavailable: You can't use INT2" "" } */
+
+typedef enum {red, green, blue} Color __attribute__((unavailable("You can't use Color")));
+
+int g1;
+int g2 __attribute__ ((unavailable("You can't use g2")));
+int g3 __attribute__ ((__unavailable__("You can't use g3")));
+Color k;				/* { dg-error "'Color' is unavailable: You can't use Color" "" } */
+
+typedef struct {
+  int field1;
+  int field2 __attribute__ ((unavailable("You can't use field2")));
+  int field3;
+  int field4 __attribute__ ((__unavailable__("You can't use field4")));
+  union {
+    int field5;
+    int field6 __attribute__ ((unavailable("You can't use field6")));
+  } u1;
+  int field7:1;
+  int field8:1 __attribute__ ((unavailable("You can't use field8")));
+  union {
+    int field9;
+    int field10;
+  } u2 __attribute__ ((unavailable("You can't use u2")));
+} S1;
+
+int func1()
+{
+   INT1 w;				/* { dg-error "'INT1' is unavailable: You can't use INT1" "" } */
+   int x __attribute__ ((unavailable("Avoid x")));
+   int y __attribute__ ((__unavailable__("Bad y")));
+   int z;
+   int (*pf)() = f1;			/* { dg-error "'f1' is unavailable: You can't use f1" "" } */
+
+   z = w + x + y + g1 + g2 + g3;	/* { dg-error "'x' is unavailable: Avoid x" "" } */
+					/* { dg-error "'y' is unavailable: Bad y" "y" { target *-*-* } .-1  } */
+					/* { dg-error "'g2' is unavailable: You can't use g2" "g2" { target *-*-* } .-2  }  */
+					/* { dg-error "'g3' is unavailable: You can't use g3" "g3" { target *-*-* } .-3  } */
+   return f1(); 			/* { dg-error "'f1' is unavailable: You can't use f1" "" } */
+}
+
+int func2(S1 *p)
+{
+  S1 lp;
+
+  if (p->field1)
+     return p->field2;			/* { dg-error "'field2' is unavailable: You can't use field2" "" } */
+  else if (lp.field4)			/* { dg-error "'field4' is unavailable: You can't use field4" "" } */
+     return p->field3;
+
+  p->u1.field5 = g1 + p->field7;
+  p->u2.field9;				/* { dg-error "'u2' is unavailable: You can't use u2" "" } */
+  return p->u1.field6 + p->field8;	/* { dg-error "'field6' is unavailable: You can't use field6" "" } */
+					/* { dg-error "'field8' is unavailable: You can't use field8" "field8" { target *-*-* } .-1 } */
+}
+
+struct SS1 {
+  int x;
+  INT1 y; 				/* { dg-error "'INT1' is unavailable: You can't use INT1" "" } */
+} __attribute__ ((unavailable("You can't use SS1")));
+
+struct SS1 *p1;				/* { dg-error "'SS1' is unavailable: You can't use SS1" "" } */
+
+struct __attribute__ ((__unavailable__("You can't use SS2"))) SS2 {
+  int x;
+  INT1 y; 				/* { dg-error "'INT1' is unavailable: You can't use INT1" "" } */
+};
+
+struct SS2 *p2;				/* { dg-error "'SS2' is unavailable: You can't use SS2" "" } */
diff --git a/gcc/testsuite/gcc.dg/attr-unavailable-5.c b/gcc/testsuite/gcc.dg/attr-unavailable-5.c
new file mode 100644
index 00000000000..051f960c5cf
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-unavailable-5.c
@@ -0,0 +1,6 @@ 
+/* Test __attribute__((unavailable)).  Test types without names.  */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+struct { int a; } __attribute__((unavailable ("Do not use"))) x; /* { dg-error "type is unavailable" } */
+typeof(x) y; /* { dg-error "type is unavailable: Do not use" } */
diff --git a/gcc/testsuite/gcc.dg/attr-unavailable-6.c b/gcc/testsuite/gcc.dg/attr-unavailable-6.c
new file mode 100644
index 00000000000..f5f4560c735
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-unavailable-6.c
@@ -0,0 +1,11 @@ 
+/* Test __attribute__((unavailable)).  Test merging with multiple
+   declarations. */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+void func(void);
+void func(void) __attribute__((unavailable ("Do not use")));
+
+void f(void) {
+  func(); /* { dg-error "'func' is unavailable: Do not use" } */
+}
diff --git a/gcc/testsuite/obj-c++.dg/attributes/method-unavailable-1.mm b/gcc/testsuite/obj-c++.dg/attributes/method-unavailable-1.mm
new file mode 100644
index 00000000000..e5708c202c0
--- /dev/null
+++ b/gcc/testsuite/obj-c++.dg/attributes/method-unavailable-1.mm
@@ -0,0 +1,34 @@ 
+/* Test __attribute__ ((unavailable)) */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+#include <objc/objc.h>
+
+@interface MyClass
+{
+  Class isa;
+}
++ (int) method;
+- (int) method;
++ (int) unavailableClassMethod __attribute__((unavailable));
+- (int) unavailableInstanceMethod __attribute__((unavailable));
+@end
+
+/* Test that deprecation warnings are produced, but not if the
+   receiver is of type 'id'.  */
+void foo (void)
+{
+  Class c;
+  id object;
+  MyClass *another_object;
+
+  [c method];
+  [object method];
+  [c unavailableClassMethod];
+  [object unavailableInstanceMethod];
+
+  [object method];
+  [another_object method];
+  [MyClass unavailableClassMethod];           /* { dg-error "is unavailable" } */
+  [another_object unavailableInstanceMethod]; /* { dg-error "is unavailable" } */
+}
diff --git a/gcc/testsuite/obj-c++.dg/attributes/method-unavailable-2.mm b/gcc/testsuite/obj-c++.dg/attributes/method-unavailable-2.mm
new file mode 100644
index 00000000000..68ea46d2543
--- /dev/null
+++ b/gcc/testsuite/obj-c++.dg/attributes/method-unavailable-2.mm
@@ -0,0 +1,24 @@ 
+/* Test __attribute__ ((unavailable)) */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+#include <objc/objc.h>
+
+@interface MyClass
+{
+  Class isa;
+}
++ (int) unavailableClassMethod: (id)firstObject, ...    __attribute__((sentinel)) __attribute__((unavailable));
+- (int) unavailableInstanceMethod: (id)firstobject, ... __attribute__((sentinel)) __attribute__((unavailable));
+@end
+
+/* Test that unavailability errors are produced even if the method is
+   also marked with another attribute too (this is to test the
+   processing of multiple attributes).  */
+void foo (void)
+{
+  MyClass *object = nil;
+
+  [MyClass unavailableClassMethod: object, nil];           /* { dg-error "is unavailable" } */
+  [object unavailableInstanceMethod: object, nil];         /* { dg-error "is unavailable" } */
+}
diff --git a/gcc/testsuite/obj-c++.dg/attributes/method-unavailable-3.mm b/gcc/testsuite/obj-c++.dg/attributes/method-unavailable-3.mm
new file mode 100644
index 00000000000..9e55ae11e1f
--- /dev/null
+++ b/gcc/testsuite/obj-c++.dg/attributes/method-unavailable-3.mm
@@ -0,0 +1,22 @@ 
+/* Test __attribute__ ((unavailable)) */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+#include <objc/objc.h>
+
+/* Test that __attribute__ ((__unavailable__)) works as well as __attribute__ ((unavailable)).  */
+@interface MyClass
+{
+  Class isa;
+}
++ (int) unavailableClassMethod: (id)firstObject, ...    __attribute__((__unavailable__));
+- (int) unavailableInstanceMethod: (id)firstobject, ... __attribute__((__unavailable__));
+@end
+
+void foo (void)
+{
+  MyClass *object = nil;
+
+  [MyClass unavailableClassMethod: object, nil];           /* { dg-error "is unavailable" } */
+  [object unavailableInstanceMethod: object, nil];         /* { dg-error "is unavailable" } */
+}
diff --git a/gcc/testsuite/obj-c++.dg/property/at-property-unavailable-1.mm b/gcc/testsuite/obj-c++.dg/property/at-property-unavailable-1.mm
new file mode 100644
index 00000000000..6bb4755220d
--- /dev/null
+++ b/gcc/testsuite/obj-c++.dg/property/at-property-unavailable-1.mm
@@ -0,0 +1,38 @@ 
+/* Test __attribute__ ((unavailable)) */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+/* Test that properties can be unavailable.  */
+
+#include <stdlib.h>
+#include <objc/objc.h>
+#include <objc/runtime.h>
+
+@interface MyRootClass
+{
+  Class isa;
+  int a;
+}
+@property int a __attribute__((unavailable));
++ (id) initialize;
++ (id) alloc;
+- (id) init;
+@end
+
+@implementation MyRootClass
++ (id) initialize { return self; }
++ (id) alloc { return class_createInstance (self, 0); }
+- (id) init { return self; }
+@synthesize a;
+@end
+
+int main (void)
+{
+  MyRootClass *object = [[MyRootClass alloc] init];
+
+  object.a = 40;      /* { dg-error "is unavailable" } */
+  if (object.a != 40) /* { dg-error "is unavailable" } */
+    abort ();
+
+  return (0);
+}
diff --git a/gcc/testsuite/obj-c++.dg/property/at-property-unavailable-2.mm b/gcc/testsuite/obj-c++.dg/property/at-property-unavailable-2.mm
new file mode 100644
index 00000000000..5edc1626c89
--- /dev/null
+++ b/gcc/testsuite/obj-c++.dg/property/at-property-unavailable-2.mm
@@ -0,0 +1,26 @@ 
+/* Test __attribute__ ((unavailable)) */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+/* Test that unavailability errors are produced when a setter/getter of
+   a @property is used directly.  */
+
+#include <objc/objc.h>
+
+@interface MyClass
+{
+  Class isa;
+  int variable;
+}
+@property (assign, nonatomic) int property __attribute__ ((unavailable));
+@end
+
+void foo (void)
+{
+  MyClass *object = nil;
+
+  if ([object property] > 0)  /* { dg-error "is unavailable" } */
+    {
+      [object setProperty: 43]; /* { dg-error "is unavailable" } */
+    }
+}
diff --git a/gcc/testsuite/obj-c++.dg/property/dotsyntax-unavailable-1.mm b/gcc/testsuite/obj-c++.dg/property/dotsyntax-unavailable-1.mm
new file mode 100644
index 00000000000..e2ef2a5b23b
--- /dev/null
+++ b/gcc/testsuite/obj-c++.dg/property/dotsyntax-unavailable-1.mm
@@ -0,0 +1,42 @@ 
+/* Test __attribute__ ((unavailable)) */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+/* Test the 'dot syntax' with unavailable methods.  */
+
+#include <objc/objc.h>
+
+@interface MyClass
+{
+  Class isa;
+}
++ (int) classCount __attribute__ ((unavailable));
++ (void) setClassCount: (int)value __attribute__ ((unavailable));
+
+- (int) count __attribute__ ((unavailable));
+- (void) setCount: (int)value __attribute__ ((unavailable));
+
+- (int) classCount2;
+- (void) setClassCount2: (int)value;
+
+- (int) count2;
+- (void) setCount2: (int)value;
+@end
+
+void foo (void)
+{
+  MyClass *object = nil;
+
+
+  if (object.count > 0)  /* { dg-error "is unavailable" } */
+    object.count = 20;  /* { dg-error "is unavailable" } */
+
+  if (MyClass.classCount < -7)   /* { dg-error "is unavailable" } */
+    MyClass.classCount = 11;  /* { dg-error "is unavailable" } */
+
+  if (object.classCount2 > 0)
+    object.classCount2 = 19;
+
+  if (object.count2 < -7)
+    object.count2 = 74;
+}
diff --git a/gcc/testsuite/objc.dg/attributes/method-unavailable-1.m b/gcc/testsuite/objc.dg/attributes/method-unavailable-1.m
new file mode 100644
index 00000000000..7a3de6b245d
--- /dev/null
+++ b/gcc/testsuite/objc.dg/attributes/method-unavailable-1.m
@@ -0,0 +1,34 @@ 
+/* Test __attribute__ ((unavailable)) */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+#include <objc/objc.h>
+
+@interface MyClass
+{
+  Class isa;
+}
++ (int) method;
+- (int) method;
++ (int) unavailableClassMethod __attribute__((unavailable));
+- (int) unavailableInstanceMethod __attribute__((unavailable));
+@end
+
+/* Test that unavailability errors are produced, but not if the
+   receiver is of type 'id'.  */
+void foo (void)
+{
+  Class c;
+  id object;
+  MyClass *another_object;
+
+  [c method];
+  [object method];
+  [c unavailableClassMethod];
+  [object unavailableInstanceMethod];
+
+  [object method];
+  [another_object method];
+  [MyClass unavailableClassMethod];           /* { dg-error "is unavailable" } */
+  [another_object unavailableInstanceMethod]; /* { dg-error "is unavailable" } */
+}
diff --git a/gcc/testsuite/objc.dg/attributes/method-unavailable-2.m b/gcc/testsuite/objc.dg/attributes/method-unavailable-2.m
new file mode 100644
index 00000000000..68ea46d2543
--- /dev/null
+++ b/gcc/testsuite/objc.dg/attributes/method-unavailable-2.m
@@ -0,0 +1,24 @@ 
+/* Test __attribute__ ((unavailable)) */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+#include <objc/objc.h>
+
+@interface MyClass
+{
+  Class isa;
+}
++ (int) unavailableClassMethod: (id)firstObject, ...    __attribute__((sentinel)) __attribute__((unavailable));
+- (int) unavailableInstanceMethod: (id)firstobject, ... __attribute__((sentinel)) __attribute__((unavailable));
+@end
+
+/* Test that unavailability errors are produced even if the method is
+   also marked with another attribute too (this is to test the
+   processing of multiple attributes).  */
+void foo (void)
+{
+  MyClass *object = nil;
+
+  [MyClass unavailableClassMethod: object, nil];           /* { dg-error "is unavailable" } */
+  [object unavailableInstanceMethod: object, nil];         /* { dg-error "is unavailable" } */
+}
diff --git a/gcc/testsuite/objc.dg/attributes/method-unavailable-3.m b/gcc/testsuite/objc.dg/attributes/method-unavailable-3.m
new file mode 100644
index 00000000000..9e55ae11e1f
--- /dev/null
+++ b/gcc/testsuite/objc.dg/attributes/method-unavailable-3.m
@@ -0,0 +1,22 @@ 
+/* Test __attribute__ ((unavailable)) */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+#include <objc/objc.h>
+
+/* Test that __attribute__ ((__unavailable__)) works as well as __attribute__ ((unavailable)).  */
+@interface MyClass
+{
+  Class isa;
+}
++ (int) unavailableClassMethod: (id)firstObject, ...    __attribute__((__unavailable__));
+- (int) unavailableInstanceMethod: (id)firstobject, ... __attribute__((__unavailable__));
+@end
+
+void foo (void)
+{
+  MyClass *object = nil;
+
+  [MyClass unavailableClassMethod: object, nil];           /* { dg-error "is unavailable" } */
+  [object unavailableInstanceMethod: object, nil];         /* { dg-error "is unavailable" } */
+}
diff --git a/gcc/testsuite/objc.dg/property/at-property-unavailable-1.m b/gcc/testsuite/objc.dg/property/at-property-unavailable-1.m
new file mode 100644
index 00000000000..855ccc6b028
--- /dev/null
+++ b/gcc/testsuite/objc.dg/property/at-property-unavailable-1.m
@@ -0,0 +1,38 @@ 
+/* Test __attribute__ ((unavailable)) */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+/* Test that properties can be unavailable.  */
+
+#include <stdlib.h>
+#include <objc/objc.h>
+#include <objc/runtime.h>
+
+@interface MyRootClass
+{
+  Class isa;
+  int a;
+}
+@property int a __attribute__((unavailable));
++ (id) initialize;
++ (id) alloc;
+- (id) init;
+@end
+
+@implementation MyRootClass
++ (id) initialize { return self; }
++ (id) alloc { return class_createInstance (self, 0); }
+- (id) init { return self; }
+@synthesize a;
+@end
+
+int main (void)
+{
+  MyRootClass *object = [[MyRootClass alloc] init];
+
+  object.a = 40;      /* { dg-error "is unavailable" } */
+  if (object.a != 40) /* { dg-error "is unavailable" } */
+    abort ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/objc.dg/property/at-property-unavailable-2.m b/gcc/testsuite/objc.dg/property/at-property-unavailable-2.m
new file mode 100644
index 00000000000..5edc1626c89
--- /dev/null
+++ b/gcc/testsuite/objc.dg/property/at-property-unavailable-2.m
@@ -0,0 +1,26 @@ 
+/* Test __attribute__ ((unavailable)) */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+/* Test that unavailability errors are produced when a setter/getter of
+   a @property is used directly.  */
+
+#include <objc/objc.h>
+
+@interface MyClass
+{
+  Class isa;
+  int variable;
+}
+@property (assign, nonatomic) int property __attribute__ ((unavailable));
+@end
+
+void foo (void)
+{
+  MyClass *object = nil;
+
+  if ([object property] > 0)  /* { dg-error "is unavailable" } */
+    {
+      [object setProperty: 43]; /* { dg-error "is unavailable" } */
+    }
+}
diff --git a/gcc/testsuite/objc.dg/property/dotsyntax-unavailable-1.m b/gcc/testsuite/objc.dg/property/dotsyntax-unavailable-1.m
new file mode 100644
index 00000000000..e2ef2a5b23b
--- /dev/null
+++ b/gcc/testsuite/objc.dg/property/dotsyntax-unavailable-1.m
@@ -0,0 +1,42 @@ 
+/* Test __attribute__ ((unavailable)) */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+/* Test the 'dot syntax' with unavailable methods.  */
+
+#include <objc/objc.h>
+
+@interface MyClass
+{
+  Class isa;
+}
++ (int) classCount __attribute__ ((unavailable));
++ (void) setClassCount: (int)value __attribute__ ((unavailable));
+
+- (int) count __attribute__ ((unavailable));
+- (void) setCount: (int)value __attribute__ ((unavailable));
+
+- (int) classCount2;
+- (void) setClassCount2: (int)value;
+
+- (int) count2;
+- (void) setCount2: (int)value;
+@end
+
+void foo (void)
+{
+  MyClass *object = nil;
+
+
+  if (object.count > 0)  /* { dg-error "is unavailable" } */
+    object.count = 20;  /* { dg-error "is unavailable" } */
+
+  if (MyClass.classCount < -7)   /* { dg-error "is unavailable" } */
+    MyClass.classCount = 11;  /* { dg-error "is unavailable" } */
+
+  if (object.classCount2 > 0)
+    object.classCount2 = 19;
+
+  if (object.count2 < -7)
+    object.count2 = 74;
+}
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index c9280a8d3b1..97b3cdd7bd9 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -1001,7 +1001,8 @@  struct GTY(()) tree_base {
       unsigned user_align : 1;
       unsigned nameless_flag : 1;
       unsigned atomic_flag : 1;
-      unsigned spare0 : 3;
+      unsigned unavailable_flag : 1;
+      unsigned spare0 : 2;
 
       unsigned spare1 : 8;
 
@@ -1323,6 +1324,12 @@  struct GTY(()) tree_base {
        SSA_NAME_POINTS_TO_READONLY_MEMORY in
 	   SSA_NAME
 
+   unavailable_flag:
+
+       TREE_UNAVAILABLE in
+	   all decls
+	   all types
+
    visited:
 
        TREE_VISITED in
@@ -1370,6 +1377,7 @@  struct GTY(()) tree_base {
 
        CALL_EXPR_BY_DESCRIPTOR in
            CALL_EXPR
+
 */
 
 struct GTY(()) tree_typed {
diff --git a/gcc/tree.c b/gcc/tree.c
index 9260772b846..ea3f87650dd 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -13162,6 +13162,78 @@  warn_deprecated_use (tree node, tree attr)
   return w;
 }
 
+/* Error out with an identifier which was marked 'unavailable'. */
+void
+error_unavailable_use (tree node, tree attr)
+{
+  escaped_string msg;
+
+  if (node == 0)
+    return;
+
+  if (!attr)
+    {
+      if (DECL_P (node))
+	attr = DECL_ATTRIBUTES (node);
+      else if (TYPE_P (node))
+	{
+	  tree decl = TYPE_STUB_DECL (node);
+	  if (decl)
+	    attr = lookup_attribute ("unavailable",
+				     TYPE_ATTRIBUTES (TREE_TYPE (decl)));
+	}
+    }
+
+  if (attr)
+    attr = lookup_attribute ("unavailable", attr);
+
+  if (attr)
+    msg.escape (TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
+
+  if (DECL_P (node))
+    {
+      auto_diagnostic_group d;
+      if (msg)
+	error ("%qD is unavailable: %s", node, (const char *) msg);
+      else
+	error ("%qD is unavailable", node);
+      inform (DECL_SOURCE_LOCATION (node), "declared here");
+    }
+  else if (TYPE_P (node))
+    {
+      tree what = NULL_TREE;
+      tree decl = TYPE_STUB_DECL (node);
+
+      if (TYPE_NAME (node))
+	{
+	  if (TREE_CODE (TYPE_NAME (node)) == IDENTIFIER_NODE)
+	    what = TYPE_NAME (node);
+	  else if (TREE_CODE (TYPE_NAME (node)) == TYPE_DECL
+		   && DECL_NAME (TYPE_NAME (node)))
+	    what = DECL_NAME (TYPE_NAME (node));
+	}
+
+      auto_diagnostic_group d;
+      if (what)
+	{
+	  if (msg)
+	    error ("%qE is unavailable: %s", what, (const char *) msg);
+	  else
+	    error ("%qE is unavailable", what);
+	}
+      else
+	{
+	  if (msg)
+	    error ("type is unavailable: %s", (const char *) msg);
+	  else
+	    error ("type is unavailable");
+	}
+
+      if (decl)
+	inform (DECL_SOURCE_LOCATION (decl), "declared here");
+    }
+}
+
 /* Return true if REF has a COMPONENT_REF with a bit-field field declaration
    somewhere in it.  */
 
diff --git a/gcc/tree.h b/gcc/tree.h
index f8f0a606439..e8a9c90f234 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -952,6 +952,11 @@  extern void omp_clause_range_check_failed (const_tree, const char *, int,
 #define TREE_DEPRECATED(NODE) \
   ((NODE)->base.deprecated_flag)
 
+/* Nonzero in a _DECL if the use of the name is defined as an
+   unavailable feature by __attribute__((unavailable)).  */
+#define TREE_UNAVAILABLE(NODE) \
+  ((NODE)->base.u.bits.unavailable_flag)
+
 /* Nonzero indicates an IDENTIFIER_NODE that names an anonymous
    aggregate, (as created by anon_aggr_name_format).  */
 #define IDENTIFIER_ANON_P(NODE) \
@@ -5120,6 +5125,7 @@  extern const_tree strip_invariant_refs (const_tree);
 extern tree lhd_gcc_personality (void);
 extern void assign_assembler_name_if_needed (tree);
 extern bool warn_deprecated_use (tree, tree);
+extern void error_unavailable_use (tree, tree);
 extern void cache_integer_cst (tree);
 extern const char *combined_fn_name (combined_fn);