Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/10.0.300.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* Fix FS0229 B-stream misalignment when reading metadata from assemblies compiled with LangVersion < 9.0, introduced by [#17706](https://github.com/dotnet/fsharp/pull/17706). ([PR #19260](https://github.com/dotnet/fsharp/pull/19260))
* Fix FS3356 false positive for instance extension members with same name on different types, introduced by [#18821](https://github.com/dotnet/fsharp/pull/18821). ([PR #19260](https://github.com/dotnet/fsharp/pull/19260))
* F# Scripts: Fix default reference paths resolving when an SDK directory is specified. ([PR #19270](https://github.com/dotnet/fsharp/pull/19270))
* Fix `AttributeUsage.AllowMultiple` not being inherited for attributes subclassed in C#. ([Issue #17107](https://github.com/dotnet/fsharp/issues/17107), [PR #19315](https://github.com/dotnet/fsharp/pull/19315))

### Added
* FSharpType: add ImportILType ([PR #19300](https://github.com/dotnet/fsharp/pull/19300))
Expand Down
11 changes: 10 additions & 1 deletion src/Compiler/Checking/PostInferenceChecks.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2037,7 +2037,16 @@ and CheckAttribs cenv env (attribs: Attribs) =
|> Seq.map fst
|> Seq.toList
// Filter for allowMultiple = false
|> List.filter (fun (tcref, _, m) -> TryFindAttributeUsageAttribute cenv.g m tcref <> Some true)
|> List.filter (fun (tcref, _, m) ->
let getSuper (tcref: TyconRef) =
let ty = generalizedTyconRef cenv.g tcref
match GetSuperTypeOfType cenv.g cenv.amap m ty with
| Some superTy ->
match tryTcrefOfAppTy cenv.g superTy with
| ValueSome sup -> Some sup
| ValueNone -> None
| None -> None
TryFindAttributeUsageAttribute cenv.g m getSuper tcref <> Some true)

if cenv.reportErrors then
for tcref, _, m in duplicates do
Expand Down
17 changes: 9 additions & 8 deletions src/Compiler/TypedTree/TypedTreeOps.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3489,12 +3489,12 @@ let superOfTycon (g: TcGlobals) (tycon: Tycon) =
| None -> g.obj_ty_noNulls
| Some ty -> ty

/// walk a TyconRef's inheritance tree, yielding any parent types as an array
let supersOfTyconRef (tcref: TyconRef) =
/// Walk a TyconRef's inheritance tree using the provided super-type resolver, yielding parent types as an array.
let supersOfTyconRefWith (getSuper: TyconRef -> TyconRef option) (tcref: TyconRef) =
tcref |> Array.unfold (fun tcref ->
match tcref.TypeContents.tcaug_super with
| Some (TType_app(sup, _, _)) -> Some(sup, sup)
| _ -> None)
match getSuper tcref with
| Some sup -> Some(sup, sup)
| None -> None)

//----------------------------------------------------------------------------
// Detect attributes
Expand Down Expand Up @@ -3629,10 +3629,11 @@ let TryFindTyconRefBoolAttribute g m attribSpec tcref =
| [ Some (:? bool as v : obj) ], _ -> Some v
| _ -> None)

/// Try to find the resolved attributeusage for an type by walking its inheritance tree and picking the correct attribute usage value
let TryFindAttributeUsageAttribute g m tcref =
/// Try to find the resolved AttributeUsage for a type by walking its inheritance tree and picking the correct attribute usage value.
/// The getSuper function is used to resolve the super-type of each type in the chain, allowing correct handling of both F# and IL-imported types.
let TryFindAttributeUsageAttribute g m (getSuper: TyconRef -> TyconRef option) tcref =
[| yield tcref
yield! supersOfTyconRef tcref |]
yield! supersOfTyconRefWith getSuper tcref |]
|> Array.tryPick (fun tcref ->
TryBindTyconRefAttribute g m g.attrib_AttributeUsageAttribute tcref
(fun (_, named) -> named |> List.tryPick (function "AllowMultiple", _, _, ILAttribElem.Bool res -> Some res | _ -> None))
Expand Down
5 changes: 3 additions & 2 deletions src/Compiler/TypedTree/TypedTreeOps.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -2400,8 +2400,9 @@ val TyconRefHasAttribute: TcGlobals -> range -> BuiltinAttribInfo -> TyconRef ->
/// Try to find an attribute with a specific full name on a type definition
val TyconRefHasAttributeByName: range -> string -> TyconRef -> bool

/// Try to find the AttributeUsage attribute, looking for the value of the AllowMultiple named parameter
val TryFindAttributeUsageAttribute: TcGlobals -> range -> TyconRef -> bool option
/// Try to find the AttributeUsage attribute, looking for the value of the AllowMultiple named parameter.
/// The getSuper function is used to walk the inheritance chain for both F# and IL-imported types.
val TryFindAttributeUsageAttribute: TcGlobals -> range -> (TyconRef -> TyconRef option) -> TyconRef -> bool option

#if !NO_TYPEPROVIDERS
/// returns Some(assemblyName) for success
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,114 @@ type C() =
|> withReferences [csharpBaseClass]
|> compile
|> shouldSucceed

[<FSharp.Test.FactForNETCOREAPP>]
let ``C# attribute subclass inherits AllowMultiple true from base`` () =
let csharpLib =
CSharp """
using System;

[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class BaseAttribute : Attribute { }
public class ChildAttribute : BaseAttribute { }
""" |> withName "csAttrLib"

FSharp """
module Test

[<Child; Child>]
type C() = class end
"""
|> withReferences [csharpLib]
|> compile
|> shouldSucceed

[<FSharp.Test.FactForNETCOREAPP>]
let ``C# attribute subclass inherits AllowMultiple false from base`` () =
let csharpLib =
CSharp """
using System;

[AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
public class BaseAttribute : Attribute { }
public class ChildAttribute : BaseAttribute { }
""" |> withName "csAttrLib"

FSharp """
module Test

[<Child; Child>]
type C() = class end
"""
|> withReferences [csharpLib]
|> compile
|> shouldFail
|> withSingleDiagnostic (Error 429, Line 4, Col 10, Line 4, Col 15, "The attribute type 'ChildAttribute' has 'AllowMultiple=false'. Multiple instances of this attribute cannot be attached to a single language element.")

[<FSharp.Test.FactForNETCOREAPP>]
let ``C# attribute multi-level inheritance inherits AllowMultiple true`` () =
let csharpLib =
CSharp """
using System;

[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class BaseAttribute : Attribute { }
public class MiddleAttribute : BaseAttribute { }
public class LeafAttribute : MiddleAttribute { }
""" |> withName "csAttrLib"

FSharp """
module Test

[<Leaf; Leaf>]
type C() = class end
"""
|> withReferences [csharpLib]
|> compile
|> shouldSucceed

[<FSharp.Test.FactForNETCOREAPP>]
let ``C# attribute subclass with own AttributeUsage overrides base AllowMultiple`` () =
let csharpLib =
CSharp """
using System;

[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class BaseAttribute : Attribute { }

[AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
public class ChildAttribute : BaseAttribute { }
""" |> withName "csAttrLib"

FSharp """
module Test

[<Child; Child>]
type C() = class end
"""
|> withReferences [csharpLib]
|> compile
|> shouldFail
|> withSingleDiagnostic (Error 429, Line 4, Col 10, Line 4, Col 15, "The attribute type 'ChildAttribute' has 'AllowMultiple=false'. Multiple instances of this attribute cannot be attached to a single language element.")

[<FSharp.Test.FactForNETCOREAPP>]
let ``F# attribute subclass of C# base inherits AllowMultiple true`` () =
let csharpLib =
CSharp """
using System;

[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class BaseAttribute : Attribute { }
""" |> withName "csAttrLib"

FSharp """
module Test

type ChildAttribute() = inherit BaseAttribute()

[<Child; Child>]
type C() = class end
"""
|> withReferences [csharpLib]
|> compile
|> shouldSucceed
Loading