From b208532ac90addae4c52047525dead1b607b885b Mon Sep 17 00:00:00 2001 From: Zach Jibben Date: Mon, 21 Sep 2020 12:07:37 -0600 Subject: [PATCH 1/7] Add revised protected components proposal --- proposals/protected-components.txt | 245 +++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 proposals/protected-components.txt diff --git a/proposals/protected-components.txt b/proposals/protected-components.txt new file mode 100644 index 0000000..1e4e120 --- /dev/null +++ b/proposals/protected-components.txt @@ -0,0 +1,245 @@ +To: J3 J3/XX-XXX +From: Zach Jibben +Subject: Protected components & types: specifications, & syntax +Date: 2020-October-1 +Reference: 20-121 20-106 19-214r1 19-161 19-135r1 18-265 + + +1. Introduction +=============== + +This paper contains the formal specifications & syntax for protected +components of a type, and protected types. This supersedes the +specifications outlined by previous papers, as subgroup discussion revealed +a more flexible feature set was needed to meet competing user requirements. + +Previous protected-components specifications fell into one of two camps. +Papers 18-265 and 20-106 envisioned components inaccessible for *direct* +modification outside the module in which the parent type was defined, but +did allow a type containing a protected potential subobject to appear in a +variable-definition context. These papers propose an access specifier +roughly in-between PUBLIC and PRIVATE, by effectively prohibiting the name +of a protected component from appearing in a variable-definition context, +not protecting the variable itself. Other papers, 19-135r1, 19-161, +19-214r1, and 20-121 offered stronger protection, protecting variables +themselves from being modified by virtually any means outside the module +where that component was defined. + +This paper aims to satisfy both parties by teasing apart the competing +goals into two separate features: protected components, and protected +types. Protected components provide an access specification allowing code +outside the module defining the type to read, but not *directly* modify +components. These components will not impose any restrictions on the type +containing them, beyond anything that might be imposed by the analogous +PRIVATE or PUBLIC access specifiers. Protected types may be used to protect +the data from appearing in variable-definition contexts. + +The use cases for both features have been presented in previous papers +(protected components 18-265, 20-106, 19-214r1; protected types 19-135r1). + +Although this is a formal syntax paper, the syntax will be defined by prose +and by example, not by BNF, to aid comprehension. + + +2. Specifications +================= + +To help keep a record of how specs have changed and to aid discussion, +specification labels are preserved from papers 20-121 and 19-214r1. + + +2.1 Protected Components + +B. The name of a protected component shall not appear in a + variable-definition context, except within the module wherein its type + is defined. + +G. A protected component or a subobject of a protected component can only + be argument associated with INTENT(IN) dummy, even if the referenced + procedure is in the module in which the type is defined. + +H. A protected component or subobject of a protected component cannot be + the target in a pointer assignment outside the module, as that would + lose the protection. + - Here I think it would be valuable to somehow expose an immutable + reference, which could tie into the const-pointer proposals. + +I. A protected component or subobject of a protected component cannot be + explicitly allocated or deallocated except within the module in which + the type is defined. + - Otherwise it's too easy to bypass the protection. + - Allocating/deallocating a type containing a protected component is + still permitted, provided it's not subject to some other rule. + - Automatic deallocation on scope exit will occur as it would for any + unprotected component. + +J1. A dummy variable of a type with a protected component can be INTENT(IN) + or INTENT(INOUT) or INTENT(OUT) in any procedure anywhere, unless it's + subject to some other rule. + +L1. No intrinsic assignment to a protected component or subobject thereof, + outside the module in which the protected component is defined. (cf. + 19-135r1) + - This does not prohibit intrinsic assignment to a type containing a + protected component. It is only meant to prevent the protected + component from being modified by name. + +M1. A function defined outside the module may have a result variable of a + type with a protected component. + +N. Structure constructor outside the module in which the type is allowed if + and only if no value is supplied for any protected component (otherwise + this would subvert the module's control over what values are acceptable + in the protected component). + +P1. Protected components are inherited through type extension. + - In this regard, PROTECTED is like PUBLIC, not PRIVATE. + +R. Type-wide default protected status, like PRIVATE, is provided. Default + is PUBLIC, unchanged from the current standard. PROTECTED statement + changes the default. Attributes in a component definition stmt confirm + or override the default. + +U. SEQUENCE and BIND(C) types shall not have protected components. + - This includes any level of component selection, because non-SEQUENCE + types are not allowed in SEQUENCE, and similarly BIND(C)). + +AA. A component may be PROTECTED or PRIVATE or PUBLIC, but not a + combination of more than one of these attributes. + + +2.2 Protected Types + +A. A variable whose declared type is protected shall not appear in a + variable-definition context, except within the module wherein its type + is defined. + +C. A local variable whose declared type is protected is allowed outside the + module in which the type is defined. + - If some action is needed when the variable goes out of scope (and is + thus destroyed unless it has the SAVE attribute) its type should have + a final procedure. + +D. A local pointer of a protected type is allowed outside the module in + which the type is defined. + - Otherwise use case 2.1 in 19-135r1 is not satisfied. + +E. Deallocating an object with a protected declared type outside the module + in which the type is defined is prohibited. + - Otherwise for pointer, use case 2.1 in 19-135r1 is not satisfied. + - Allowing it for remote/dummy allocatable would prohibit an allocatable + variable in the module where the type is defined to be PUBLIC. + PROTECTED attribute for the object would prevent modifying + nonprotected components. + - Allowing for local allocatable breaks rule (A) about not appearing + in a variable definition context. + +F. Allocating an object with a protected declared type outside the module + in which the type is defined is prohibited. + - Pointer would almost certainly leak memory. + - Allowing for remote/dummy allocatable would have same problems as (E). + - Allowing for local allocatable would have same problems as (E). + +J2. A dummy variable of a protected type can be INTENT(IN) or INTENT(INOUT) + in any procedure anywhere. A dummy variable of such a type shall not + have INTENT(OUT) or unspecified intent except within the module in + which the type is defined. + +K. Can modify (or allocate/deallocate) the *components* of a protected type + anywhere, provided the component is not subject to some other rule + (e.g., a protected component, or the component is itself of protected + type). + +L2. Polymorphic allocatable assignment of a parent type without a protected + attribute is permitted even when the dynamic type has a protected + attribute. + - This presents a loophole which may allow a programmer to write to a + protected component outside the module in which it is defined. + However, subgroup did not like the alternative of disallowing + protected components in extensions of parent types without any + protected component. + +L3. No intrinsic assignment to an object whose declared type is protected, + or that has an ultimate component of protected type, outside the module + in which the protected component is defined. (cf. 19-135r1) + +M2. A function defined outside the module may have a result variable of a + protected type. + - This is same as ordinary local variables (case A). + - The function result shall not be a pointer. + - The function result will (hopefully) be finalised after its use. + +N2. Structure constructor outside the module in which the type is allowed. + +O. Another type may have a component of protected type, and thereby + inherits the protected type attribute. + - The protection rules apply to types with "protected potential + subobject components" not just types with immediate protected + components. + +P2. Extension of a protected type is permitted. + +Q. An extension type may have a protected attribute even if the parent type + does not have a protected attribute. Requiring the parent type to have + be protected is too restricted for many uses. + +S. An object with a protected declared type is prohibited to be the target + of an unlimited polymorphic pointer. + - This rule applies even inside the module, even if the polymorphic + pointer is private, because its target might thereafter become + associated with a public polymorphic pointer. + +T. If an actual argument with a protected declared type is associated with + an unlimited polymorphic dummy, the dummy shall have INTENT(IN) or + INTENT(INOUT) and shall not have TARGET (or POINTER, which implies + TARGET). + - If the dummy has TARGET it might become associated with an unlimited + polymorphic pointer. + - If the dummy has POINTER, its target might eventually become + associated with an unlimited polymorphic pointer (see P.) + + +3. Syntax +========= + +3.1 Protected Components + +One may add the PROTECTED attribute to component definitions. + + type :: foo + real, protected :: a + real, private :: b + real, public :: c + end type foo + +The requirement R allows a type-wide PROTECTED status, which may be +overridden for an individual component using another access specifier. + + type :: foo + protected + real, private :: a + real :: b + end type foo + + +3.2 Protected Types + +One may add the PROTECTED attribute to type definitions. One may +independently control the access specifier of each component. + + type, protected :: foo + real, public :: a + real, private :: b + real, protected :: c + end type foo + +The PROTECTED keyword meaning different things in these contexts may be +confusing. It's worth considering different keywords (e.g., readonly +components). + + type, protected :: bar + protected + type(foo), protected :: a + end type bar + +===END=== From 06cfaea6dd38102e580df4bf6a1368e243126ea8 Mon Sep 17 00:00:00 2001 From: Zach Jibben Date: Thu, 24 Sep 2020 12:37:43 -0600 Subject: [PATCH 2/7] Add use-cases and corrections based on feedback --- proposals/protected-components.txt | 139 +++++++++++++++++++++++------ 1 file changed, 111 insertions(+), 28 deletions(-) diff --git a/proposals/protected-components.txt b/proposals/protected-components.txt index 1e4e120..2985dc0 100644 --- a/proposals/protected-components.txt +++ b/proposals/protected-components.txt @@ -34,28 +34,101 @@ containing them, beyond anything that might be imposed by the analogous PRIVATE or PUBLIC access specifiers. Protected types may be used to protect the data from appearing in variable-definition contexts. +Although this is a formal syntax paper, the syntax will be defined by prose +and by example, not by BNF, to aid comprehension. + + +2. Use Cases +============ + The use cases for both features have been presented in previous papers (protected components 18-265, 20-106, 19-214r1; protected types 19-135r1). +Here is presented two possible use-cases. For more, refer to the above +papers. -Although this is a formal syntax paper, the syntax will be defined by prose -and by example, not by BNF, to aid comprehension. +2.1 Protected Components + +Protected components are useful in any situation where a user would want a +derived type to expose a large dataset for reading, but does not want the +client to modify the data except through provided procedures. They may also +want the client to be able to store copies of these objects or deallocate +them, while still controlling their internal consistency. Furthermore, a +"getter" routine would require an expensive copy of the data. Protected +components allow the programmer to design a derived type API which reflects +the intended use of certain objects -- exposing components without also +giving the ability to modify them directly. + +For example, a CFD application's mesh may contain a large connectivity +dataset: + + type :: mesh + integer, allocatable, protected :: nbr(:) + contains + procedure :: init + end type mesh + +A client must be able to create and initialize this mesh object through +provided procedures. They might need copies to preserve some history. But +the client absolutely must not be able to modify the nbr component +directly. To summarize: + + subroutine ex1() + type(mesh) :: m ! local variables allowed + type(mesh), allocatable :: history(:) ! local allocatables allowed + + call m%init() ! modifying through module routines allowed + print *, m%nbr ! reading protected components allowed + allocate(history(1)) ! local allocation allowed + history(1) = m ! intrinsic assignment allowed + call safe(m%nbr) ! passing protected component as intent(in) allowed + call new_mesh(m) ! passing object as intent(out) or intent(inout) + ! allowed + + m%nbr(1) = huge(1) ! FAIL: modifying protected component not allowed + call change(m%nbr(1)) ! FAIL: intent(out) and intent(inout) not + ! allowed on protected components + m = mesh(nbr = [huge(1)]) ! FAIL: setting protected component + ! via structure constructor not allowed + allocate(m%nbr(1)) ! FAIL + deallocate(m%nbr(1)) ! FAIL + + ! automatic deallocation & finalization allowed + ! even without a user-defined finalizer + end subroutine ex1 + +2.2 Protected Types -2. Specifications +Type protection disables intrinsic assignment, allocation, deallocation, +and finalization outside the module where that type is defined. It does not +impose any restrictions on modifying components -- that can be done +independently with component protection. + +This provides compile-time feedback when a client attempts to use a +protected derived type in a way the author intends to disallow, and gives +the author the ability to provide custom alternatives to only those +features they intend to allow. + +One example is a linked list, in which one may wish to disallow intrinsic +assignment entirely. Otherwise, a client could inadvertantly produce a +shallow-copy. + + +3. Specifications ================= To help keep a record of how specs have changed and to aid discussion, specification labels are preserved from papers 20-121 and 19-214r1. -2.1 Protected Components +3.1 Protected Components B. The name of a protected component shall not appear in a variable-definition context, except within the module wherein its type is defined. G. A protected component or a subobject of a protected component can only - be argument associated with INTENT(IN) dummy, even if the referenced + be argument associated with an INTENT(IN) dummy, even if the referenced procedure is in the module in which the type is defined. H. A protected component or subobject of a protected component cannot be @@ -87,10 +160,17 @@ L1. No intrinsic assignment to a protected component or subobject thereof, M1. A function defined outside the module may have a result variable of a type with a protected component. -N. Structure constructor outside the module in which the type is allowed if - and only if no value is supplied for any protected component (otherwise - this would subvert the module's control over what values are acceptable - in the protected component). +N. A structure constructor outside the module in which the type is defined + is allowed if and only if no value is supplied for any protected + component (otherwise this would subvert the module's control over what + values are acceptable in the protected component). + - This describes the same user-facing behavior for PROTECTED and PRIVATE + components. However, the logic in the standard must be different. + C7102 and 7.5.4.8p2 prevent a structure constructor from specifying + a private component by restricting access to the name. For a PROTECTED + component, the name is accessible, yet we still prohibit setting the + value of such a component outside the module where the parent type is + defined. P1. Protected components are inherited through type extension. - In this regard, PROTECTED is like PUBLIC, not PRIVATE. @@ -108,7 +188,7 @@ AA. A component may be PROTECTED or PRIVATE or PUBLIC, but not a combination of more than one of these attributes. -2.2 Protected Types +3.2 Protected Types A. A variable whose declared type is protected shall not appear in a variable-definition context, except within the module wherein its type @@ -129,8 +209,6 @@ E. Deallocating an object with a protected declared type outside the module - Otherwise for pointer, use case 2.1 in 19-135r1 is not satisfied. - Allowing it for remote/dummy allocatable would prohibit an allocatable variable in the module where the type is defined to be PUBLIC. - PROTECTED attribute for the object would prevent modifying - nonprotected components. - Allowing for local allocatable breaks rule (A) about not appearing in a variable definition context. @@ -149,15 +227,19 @@ K. Can modify (or allocate/deallocate) the *components* of a protected type anywhere, provided the component is not subject to some other rule (e.g., a protected component, or the component is itself of protected type). + - This is a point for discussion. Type protection here would effectively + disable intrinsic assignment, allocation, deallocation, and + finalization outside the module where it's defined, but would not + impose any restrictions on the components. Component protection + (above) would be used to enforce both. L2. Polymorphic allocatable assignment of a parent type without a protected attribute is permitted even when the dynamic type has a protected attribute. - - This presents a loophole which may allow a programmer to write to a - protected component outside the module in which it is defined. - However, subgroup did not like the alternative of disallowing - protected components in extensions of parent types without any - protected component. + - This presents a loophole which may allow a programmer to write to a + protected type outside the module in which it is defined. However, + subgroup did not like the alternative of disallowing the protected + attribute in extensions of unprotected parent types. L3. No intrinsic assignment to an object whose declared type is protected, or that has an ultimate component of protected type, outside the module @@ -169,13 +251,14 @@ M2. A function defined outside the module may have a result variable of a - The function result shall not be a pointer. - The function result will (hopefully) be finalised after its use. -N2. Structure constructor outside the module in which the type is allowed. +N2. A structure constructor outside the module in which the type is defined + is not allowed. -O. Another type may have a component of protected type, and thereby - inherits the protected type attribute. - - The protection rules apply to types with "protected potential - subobject components" not just types with immediate protected - components. +O. A type may have a component of protected type, and thereby inherits the + protected type attribute. + - The protection rules apply to types with "potential subobject + components of protected type" not just types with immediate components + of protected type. See E, F, L3. P2. Extension of a protected type is permitted. @@ -199,10 +282,10 @@ T. If an actual argument with a protected declared type is associated with associated with an unlimited polymorphic pointer (see P.) -3. Syntax +4. Syntax ========= -3.1 Protected Components +4.1 Protected Components One may add the PROTECTED attribute to component definitions. @@ -222,7 +305,7 @@ overridden for an individual component using another access specifier. end type foo -3.2 Protected Types +4.2 Protected Types One may add the PROTECTED attribute to type definitions. One may independently control the access specifier of each component. @@ -234,8 +317,8 @@ independently control the access specifier of each component. end type foo The PROTECTED keyword meaning different things in these contexts may be -confusing. It's worth considering different keywords (e.g., readonly -components). +confusing. It's worth considering different keywords (e.g., +readonly/visible components, persistent/restricted/controlled types, etc). type, protected :: bar protected From a431705664693db35745715a87c84b2a0ea5aaf9 Mon Sep 17 00:00:00 2001 From: Zach Jibben Date: Thu, 24 Sep 2020 12:42:16 -0600 Subject: [PATCH 3/7] Add reference to motivation in 19-135r1 --- proposals/protected-components.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/protected-components.txt b/proposals/protected-components.txt index 2985dc0..7c3abad 100644 --- a/proposals/protected-components.txt +++ b/proposals/protected-components.txt @@ -232,6 +232,7 @@ K. Can modify (or allocate/deallocate) the *components* of a protected type finalization outside the module where it's defined, but would not impose any restrictions on the components. Component protection (above) would be used to enforce both. + - This allows for the use case 2.3 presented in 19-135r1. L2. Polymorphic allocatable assignment of a parent type without a protected attribute is permitted even when the dynamic type has a protected From ed3f95b35350ec20e3bbfb72caaeb2f93396c3e4 Mon Sep 17 00:00:00 2001 From: Zach Jibben Date: Tue, 15 Jun 2021 16:05:50 -0600 Subject: [PATCH 4/7] Add discussion to the protected components & types proposal --- proposals/protected-components.txt | 42 ++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/proposals/protected-components.txt b/proposals/protected-components.txt index 7c3abad..726de3b 100644 --- a/proposals/protected-components.txt +++ b/proposals/protected-components.txt @@ -54,10 +54,10 @@ derived type to expose a large dataset for reading, but does not want the client to modify the data except through provided procedures. They may also want the client to be able to store copies of these objects or deallocate them, while still controlling their internal consistency. Furthermore, a -"getter" routine would require an expensive copy of the data. Protected -components allow the programmer to design a derived type API which reflects -the intended use of certain objects -- exposing components without also -giving the ability to modify them directly. +"getter" routine would require an expensive copy of the data, thus is +suboptimal. Protected components allow the programmer to design a derived +type API which reflects the intended use of certain objects -- exposing +components without also giving the ability to modify them directly. For example, a CFD application's mesh may contain a large connectivity dataset: @@ -81,7 +81,7 @@ directly. To summarize: print *, m%nbr ! reading protected components allowed allocate(history(1)) ! local allocation allowed history(1) = m ! intrinsic assignment allowed - call safe(m%nbr) ! passing protected component as intent(in) allowed + call read(m%nbr) ! passing protected component as intent(in) allowed call new_mesh(m) ! passing object as intent(out) or intent(inout) ! allowed @@ -317,6 +317,12 @@ independently control the access specifier of each component. real, protected :: c end type foo + +5. Comments +=========== + +5.1 Alternative keywords + The PROTECTED keyword meaning different things in these contexts may be confusing. It's worth considering different keywords (e.g., readonly/visible components, persistent/restricted/controlled types, etc). @@ -326,4 +332,30 @@ readonly/visible components, persistent/restricted/controlled types, etc). type(foo), protected :: a end type bar + +5.2 Is an unprotected type with only protected components the same as a +protected type? + +A: No, an unprotected type with all protected components may be copied via +intrinsic assignment, allocated, deallocated, and automatically deallocated +outside the module wherein that type is defined. A protected type disables +all of these "type-level" definition contexts, and thus is more +restrictive. + +This distinction is the motivation for having both protected components and +protected types. In some cases a user may want to protect data in a type +from any outside tampering, in which case a protected type is warranted. In +other cases, the user wants to ensure internal consistency in a derived +type, but doesn't want to prevent the user from creating or destroying the +type altogether. A user might also want to avoid expensive copies in getter +procedures, which would be needed for private components. See the use-cases +above. + + +5.3 Is a public component of a protected type any different from a +protected component of a protected type? + +A: I believe the answer here is no. A public component of a protected type +has all the same restrictions as a protected component of the same type. + ===END=== From cc55b2e223ea113a97c48d977a7227e156149754 Mon Sep 17 00:00:00 2001 From: Zach Jibben Date: Tue, 15 Jun 2021 18:34:48 -0600 Subject: [PATCH 5/7] Fix a mistake in protected-components proposal --- proposals/protected-components.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/proposals/protected-components.txt b/proposals/protected-components.txt index 726de3b..9fdb691 100644 --- a/proposals/protected-components.txt +++ b/proposals/protected-components.txt @@ -355,7 +355,9 @@ above. 5.3 Is a public component of a protected type any different from a protected component of a protected type? -A: I believe the answer here is no. A public component of a protected type -has all the same restrictions as a protected component of the same type. +A: I believe the answer here is yes. Specification K allows modification of +the components of a protected type; i.e. the protection of a type really +only restricts what can be done to the type as a whole. This is up for +discussion, though, and could be changed. ===END=== From 11f665b9be9591a5b660eb33c99c8e9ea9f26d1b Mon Sep 17 00:00:00 2001 From: Zach Jibben Date: Thu, 17 Jun 2021 11:12:30 -0600 Subject: [PATCH 6/7] Update proposals/protected-components.txt Co-authored-by: Milan Curcic --- proposals/protected-components.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/protected-components.txt b/proposals/protected-components.txt index 9fdb691..af92b15 100644 --- a/proposals/protected-components.txt +++ b/proposals/protected-components.txt @@ -43,7 +43,7 @@ and by example, not by BNF, to aid comprehension. The use cases for both features have been presented in previous papers (protected components 18-265, 20-106, 19-214r1; protected types 19-135r1). -Here is presented two possible use-cases. For more, refer to the above +Here are presented two possible use-cases. For more, refer to the above papers. From 5a822d720fb9efce5b74d08c0cf4ecb8c296adcb Mon Sep 17 00:00:00 2001 From: Zach Jibben Date: Thu, 17 Jun 2021 11:13:31 -0600 Subject: [PATCH 7/7] Update protected components proposal --- proposals/protected-components.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/protected-components.txt b/proposals/protected-components.txt index af92b15..a218fe4 100644 --- a/proposals/protected-components.txt +++ b/proposals/protected-components.txt @@ -91,7 +91,7 @@ directly. To summarize: m = mesh(nbr = [huge(1)]) ! FAIL: setting protected component ! via structure constructor not allowed allocate(m%nbr(1)) ! FAIL - deallocate(m%nbr(1)) ! FAIL + deallocate(m%nbr) ! FAIL ! automatic deallocation & finalization allowed ! even without a user-defined finalizer @@ -110,7 +110,7 @@ the author the ability to provide custom alternatives to only those features they intend to allow. One example is a linked list, in which one may wish to disallow intrinsic -assignment entirely. Otherwise, a client could inadvertantly produce a +assignment entirely. Otherwise, a client could inadvertently produce a shallow-copy. @@ -355,9 +355,9 @@ above. 5.3 Is a public component of a protected type any different from a protected component of a protected type? -A: I believe the answer here is yes. Specification K allows modification of -the components of a protected type; i.e. the protection of a type really -only restricts what can be done to the type as a whole. This is up for +A: The answer here is yes. Specification K allows modification of the +components of a protected type; i.e. the protection of a type really only +restricts what can be done to the type as a whole. This is up for discussion, though, and could be changed. ===END===