Skip to content

Commit a09de4d

Browse files
authored
Fix #7931: Resolve opened namespaces for attributes in recursive scopes (#19502)
1 parent 917e65b commit a09de4d

4 files changed

Lines changed: 255 additions & 2 deletions

File tree

docs/release-notes/.FSharp.Compiler.Service/11.0.100.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Fix attributes on return type of unparenthesized tuple methods being silently dropped from IL. ([Issue #462](https://github.com/dotnet/fsharp/issues/462), [PR #19714](https://github.com/dotnet/fsharp/pull/19714))
44
* Fix internal error FS0073 "Undefined or unsolved type variable" in IlxGen when nested inline SRTP functions with multiple overloads leave unsolved typars in the non-witness codegen path. ([Issue #19709](https://github.com/dotnet/fsharp/issues/19709), [PR #19710](https://github.com/dotnet/fsharp/pull/19710))
55
* Fix NRE when calling virtual Object methods on value types through inline SRTP functions. ([Issue #8098](https://github.com/dotnet/fsharp/issues/8098), [PR #19511](https://github.com/dotnet/fsharp/pull/19511))
6+
* Fix attributes not resolved from opened namespaces in `namespace rec` / `module rec` scopes. ([Issue #7931](https://github.com/dotnet/fsharp/issues/7931), [PR #19502](https://github.com/dotnet/fsharp/pull/19502))
67
* Fix DU case names matching IWSAM member names no longer cause duplicate property entries. (Issue [#14321](https://github.com/dotnet/fsharp/issues/14321), [PR #19341](https://github.com/dotnet/fsharp/pull/19341))
78
* Fix DefaultAugmentation(false) duplicate entry in method table. (Issue [#16565](https://github.com/dotnet/fsharp/issues/16565), [PR #19341](https://github.com/dotnet/fsharp/pull/19341))
89
* Fix abstract event accessors now have SpecialName flag. (Issue [#5834](https://github.com/dotnet/fsharp/issues/5834), [PR #19341](https://github.com/dotnet/fsharp/pull/19341))

src/Compiler/Checking/CheckDeclarations.fs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2735,6 +2735,23 @@ module EstablishTypeDefinitionCores =
27352735
| _ -> () ]
27362736
|> set
27372737

2738+
/// Pre-process open declarations from a list of mutually recursive shapes so that
2739+
/// opened namespaces are available during Phase1A attribute checking.
2740+
/// In recursive scopes, opens are normally processed in Phase1AB (after Phase1A builds
2741+
/// module/type entities), but attributes on modules need the opened namespaces.
2742+
/// Errors are suppressed because some opens may refer to modules being defined in the
2743+
/// current recursive scope, which don't exist yet during Phase1A. Those opens will be
2744+
/// properly processed (with full error reporting) during Phase1AB.
2745+
let private preProcessOpensForPhase1A (cenv: cenv) (env: TcEnv) (shapes: MutRecShapes<_, _, _>) =
2746+
suppressErrorReporting (fun () ->
2747+
use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink
2748+
(env, shapes) ||> List.fold (fun env shape ->
2749+
match shape with
2750+
| MutRecShape.Open(MutRecDataForOpen(SynOpenDeclTarget.ModuleOrNamespace _ as target, openm, moduleRange, _)) ->
2751+
let env, _ = TcOpenDecl cenv openm moduleRange env target
2752+
env
2753+
| _ -> env))
2754+
27382755
let TcTyconDefnCore_Phase1A_BuildInitialModule (cenv: cenv) envInitial parent typeNames compInfo decls =
27392756
let g = cenv.g
27402757
let (SynComponentInfo(Attributes attribs, _, _, longPath, xml, _, vis, im)) = compInfo
@@ -2750,7 +2767,11 @@ module EstablishTypeDefinitionCores =
27502767
CheckForDuplicateConcreteType envInitial id.idText im
27512768
CheckNamespaceModuleOrTypeName g id
27522769

2753-
let envForDecls, moduleTyAcc = MakeInnerEnv true envInitial id moduleKind
2770+
let envForDecls, moduleTyAcc = MakeInnerEnv true envInitial id moduleKind
2771+
2772+
// Pre-process opens from children so nested modules can see opened namespaces during attribute checking
2773+
let envForDecls = preProcessOpensForPhase1A cenv envForDecls decls
2774+
27542775
let moduleTy = Construct.NewEmptyModuleOrNamespaceType moduleKind
27552776

27562777
let checkXmlDocs = cenv.diagnosticOptions.CheckXmlDocs
@@ -4039,12 +4060,18 @@ module EstablishTypeDefinitionCores =
40394060

40404061

40414062
let TcMutRecDefns_Phase1 mkLetInfo (cenv: cenv) envInitial parent typeNames inSig tpenv m scopem mutRecNSInfo (mutRecDefns: MutRecShapes<MutRecDefnsPhase1DataForTycon * 'MemberInfo, 'LetInfo, SynComponentInfo>) =
4063+
// Pre-process top-level opens so they are available during attribute checking in Phase1A.
4064+
// In recursive scopes (namespace rec / module rec), opens are normally processed in Phase1AB
4065+
// after module entities are built, but module attributes need access to opened namespaces.
4066+
// See https://github.com/dotnet/fsharp/issues/7931
4067+
let envWithOpens = preProcessOpensForPhase1A cenv envInitial mutRecDefns
4068+
40424069
// Phase1A - build Entity for type definitions, exception definitions and module definitions.
40434070
// Also for abbreviations of any of these. Augmentations are skipped in this phase.
40444071
let withEntities =
40454072
mutRecDefns
40464073
|> MutRecShapes.mapWithParent
4047-
(parent, typeNames, envInitial)
4074+
(parent, typeNames, envWithOpens)
40484075
// Build the initial entity for each module definition
40494076
(fun (innerParent, typeNames, envForDecls) compInfo decls ->
40504077
TcTyconDefnCore_Phase1A_BuildInitialModule cenv envForDecls innerParent typeNames compInfo decls)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
2+
3+
namespace Conformance.BasicGrammarElements
4+
5+
open Xunit
6+
open FSharp.Test.Compiler
7+
8+
module AttributeResolutionInRecursiveScopes =
9+
10+
// https://github.com/dotnet/fsharp/issues/7931
11+
[<Fact>]
12+
let ``Extension attribute on module in namespace rec`` () =
13+
FSharp """
14+
namespace rec Ns
15+
16+
open System.Runtime.CompilerServices
17+
18+
[<Extension>]
19+
module Module =
20+
[<Extension>]
21+
let ext1 (x: int) = x.ToString()
22+
"""
23+
|> asLibrary
24+
|> typecheck
25+
|> shouldSucceed
26+
27+
// https://github.com/dotnet/fsharp/issues/7931
28+
[<Fact>]
29+
let ``Extension attribute on type in namespace rec`` () =
30+
FSharp """
31+
namespace rec Ns
32+
33+
open System.Runtime.CompilerServices
34+
35+
[<Extension>]
36+
type T() =
37+
class end
38+
"""
39+
|> asLibrary
40+
|> typecheck
41+
|> shouldSucceed
42+
43+
// Custom attribute defined in same rec module used on type and let declarations
44+
[<Fact>]
45+
let ``Custom attribute on type and let in rec module`` () =
46+
FSharp """
47+
module rec M
48+
49+
type CustomAttribute() =
50+
inherit System.Attribute()
51+
52+
[<Custom>] type A = | A
53+
[<Custom>] let a = ()
54+
"""
55+
|> typecheck
56+
|> shouldSucceed
57+
58+
// https://github.com/dotnet/fsharp/issues/5795 - attribute on union case in rec module is not yet resolved
59+
[<Fact>]
60+
let ``Issue 5795 - attribute on union case in rec module`` () =
61+
FSharp """
62+
module rec M
63+
64+
type CustomAttribute() =
65+
inherit System.Attribute()
66+
67+
type A = | [<CustomAttribute>] A
68+
"""
69+
|> typecheck
70+
|> shouldFail
71+
|> withDiagnostics
72+
[ Error 1133, Line 7, Col 14, Line 7, Col 29, "No constructors are available for the type 'CustomAttribute'" ]
73+
74+
// https://github.com/dotnet/fsharp/issues/5795 - attribute on type parameter in rec module is not yet resolved
75+
[<Fact>]
76+
let ``Issue 5795 - attribute on type parameter in rec module`` () =
77+
FSharp """
78+
module rec M
79+
80+
type CustomAttribute() =
81+
inherit System.Attribute()
82+
83+
type B<[<CustomAttribute>]'a> = | B of 'a
84+
"""
85+
|> typecheck
86+
|> shouldFail
87+
|> withErrorCode 39
88+
89+
// Nested module case: open inside outer module, attribute on inner module
90+
[<Fact>]
91+
let ``Open inside nested module resolves for attribute on inner module in namespace rec`` () =
92+
FSharp """
93+
namespace rec Ns
94+
95+
module Outer =
96+
open System.Runtime.CompilerServices
97+
98+
[<Extension>]
99+
module Inner =
100+
[<Extension>]
101+
let ext1 (x: int) = x.ToString()
102+
"""
103+
|> asLibrary
104+
|> typecheck
105+
|> shouldSucceed
106+
107+
// Non-recursive baseline: should always work
108+
[<Fact>]
109+
let ``Extension attribute works without rec - baseline`` () =
110+
FSharp """
111+
namespace Ns
112+
113+
open System.Runtime.CompilerServices
114+
115+
[<Extension>]
116+
module Module =
117+
[<Extension>]
118+
let ext1 (x: int) = x.ToString()
119+
"""
120+
|> asLibrary
121+
|> typecheck
122+
|> shouldSucceed
123+
124+
// Multiple opens in namespace rec
125+
[<Fact>]
126+
let ``Multiple opens resolve for attributes in namespace rec`` () =
127+
FSharp """
128+
namespace rec Ns
129+
130+
open System
131+
open System.Runtime.CompilerServices
132+
133+
[<Extension>]
134+
module Module =
135+
[<Extension>]
136+
[<Obsolete("test")>]
137+
let ext1 (x: int) = x.ToString()
138+
"""
139+
|> asLibrary
140+
|> typecheck
141+
|> shouldSucceed
142+
143+
// Open in module rec resolves for module attributes
144+
[<Fact>]
145+
let ``Open in module rec resolves for nested module attribute`` () =
146+
FSharp """
147+
module rec M
148+
149+
open System.Runtime.CompilerServices
150+
151+
[<Extension>]
152+
module Inner =
153+
[<Extension>]
154+
let ext1 (x: int) = x.ToString()
155+
"""
156+
|> typecheck
157+
|> shouldSucceed
158+
159+
// Open with Obsolete attribute in namespace rec
160+
[<Fact>]
161+
let ``Obsolete attribute resolves via open in namespace rec`` () =
162+
FSharp """
163+
namespace rec Ns
164+
165+
open System
166+
167+
[<Obsolete("deprecated")>]
168+
module DeprecatedModule =
169+
let x = 42
170+
"""
171+
|> asLibrary
172+
|> typecheck
173+
|> shouldSucceed
174+
175+
// Negative test: undefined attribute still errors in namespace rec
176+
[<Fact>]
177+
let ``Undefined attribute still errors in namespace rec`` () =
178+
FSharp """
179+
namespace rec Ns
180+
181+
[<DoesNotExist>]
182+
module M =
183+
let x = 1
184+
"""
185+
|> asLibrary
186+
|> typecheck
187+
|> shouldFail
188+
|> withErrorCode 39
189+
190+
// Negative test: invalid open target still errors despite error suppression in pre-pass
191+
[<Fact>]
192+
let ``Invalid open target still errors in namespace rec`` () =
193+
FSharp """
194+
namespace rec Ns
195+
196+
open DoesNotExist.Namespace
197+
198+
[<System.Obsolete>]
199+
module M =
200+
let x = 1
201+
"""
202+
|> asLibrary
203+
|> typecheck
204+
|> shouldFail
205+
|> withErrorCode 39
206+
207+
// Forward-reference open to sibling module in the same recursive scope
208+
[<Fact>]
209+
let ``Forward reference open to sibling module in namespace rec`` () =
210+
FSharp """
211+
namespace rec Ns
212+
213+
open Ns.Later
214+
215+
module Earlier =
216+
let x = Later.y
217+
218+
module Later =
219+
let y = 42
220+
"""
221+
|> asLibrary
222+
|> withOptions [ "--nowarn:22"; "--nowarn:40" ]
223+
|> typecheck
224+
|> shouldSucceed

tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
<Compile Include="Conformance\BasicGrammarElements\AccessibilityAnnotations\PermittedLocations\PermittedLocations.fs" />
4040
<Compile Include="Conformance\BasicGrammarElements\CustomAttributes\AttributeInheritance\AttributeInheritance.fs" />
4141
<Compile Include="Conformance\BasicGrammarElements\CustomAttributes\AttributeUsage\AttributeUsage.fs" />
42+
<Compile Include="Conformance\BasicGrammarElements\CustomAttributes\AttributeUsage\AttributeResolutionInRecursiveScopes.fs" />
4243
<Compile Include="Conformance\BasicGrammarElements\CustomAttributes\Basic\Basic.fs" />
4344
<Compile Include="Conformance\BasicGrammarElements\CustomAttributes\ImportedAttributes\ImportedAttributes.fs" />
4445
<Compile Include="Conformance\BasicGrammarElements\CustomAttributes\ArgumentsOfAllTypes\ArgumentsOfAllTypes.fs" />

0 commit comments

Comments
 (0)