From 1222deaa512105b5bd41801b3cffee902a6e8406 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Wed, 19 Oct 2022 12:58:23 +0200 Subject: [PATCH 01/15] Merge upstream/main --- src/Compiler/AbstractIL/ilwrite.fs | 1 + src/Compiler/Checking/ConstraintSolver.fs | 33 ++- src/Compiler/CodeGen/IlxGen.fs | 2 +- src/Compiler/FSComp.txt | 1 + src/Compiler/TypedTree/TypedTreeOps.fs | 26 ++- src/Compiler/TypedTree/TypedTreeOps.fsi | 4 + src/Compiler/xlf/FSComp.txt.cs.xlf | 5 + src/Compiler/xlf/FSComp.txt.de.xlf | 5 + src/Compiler/xlf/FSComp.txt.es.xlf | 5 + src/Compiler/xlf/FSComp.txt.fr.xlf | 5 + src/Compiler/xlf/FSComp.txt.it.xlf | 5 + src/Compiler/xlf/FSComp.txt.ja.xlf | 5 + src/Compiler/xlf/FSComp.txt.ko.xlf | 5 + src/Compiler/xlf/FSComp.txt.pl.xlf | 5 + src/Compiler/xlf/FSComp.txt.pt-BR.xlf | 5 + src/Compiler/xlf/FSComp.txt.ru.xlf | 5 + src/Compiler/xlf/FSComp.txt.tr.xlf | 5 + src/Compiler/xlf/FSComp.txt.zh-Hans.xlf | 5 + src/Compiler/xlf/FSComp.txt.zh-Hant.xlf | 5 + .../Conformance/Constraints/Unmanaged.fs | 194 ++++++++++++++++++ .../FSharp.Compiler.ComponentTests.fsproj | 17 +- tests/fsharp/typecheck/sigs/neg30.bsl | 4 - .../E_UnmanagedConstraint01.fs | 5 +- 23 files changed, 315 insertions(+), 37 deletions(-) create mode 100644 tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs diff --git a/src/Compiler/AbstractIL/ilwrite.fs b/src/Compiler/AbstractIL/ilwrite.fs index 80bb791c25f..97f3ed6e419 100644 --- a/src/Compiler/AbstractIL/ilwrite.fs +++ b/src/Compiler/AbstractIL/ilwrite.fs @@ -2491,6 +2491,7 @@ let rec GetGenericParamAsGenericParamRow cenv _env idx owner gp = (if gp.HasReferenceTypeConstraint then 0x0004 else 0x0000) ||| (if gp.HasNotNullableValueTypeConstraint then 0x0008 else 0x0000) ||| (if gp.HasDefaultConstructorConstraint then 0x0010 else 0x0000) + let mdVersionMajor, _ = metadataSchemaVersionSupportedByCLRVersion cenv.desiredMetadataVersion if (mdVersionMajor = 1) then diff --git a/src/Compiler/Checking/ConstraintSolver.fs b/src/Compiler/Checking/ConstraintSolver.fs index c69db854c24..f1608cfbe5c 100644 --- a/src/Compiler/Checking/ConstraintSolver.fs +++ b/src/Compiler/Checking/ConstraintSolver.fs @@ -2163,6 +2163,10 @@ and EnforceConstraintConsistency (csenv: ConstraintSolverEnv) ndeep m2 trace ret | TyparConstraint.IsNonNullableStruct _, TyparConstraint.IsReferenceType _ | TyparConstraint.IsReferenceType _, TyparConstraint.IsNonNullableStruct _ -> return! ErrorD (Error(FSComp.SR.csStructConstraintInconsistent(), m)) + + | TyparConstraint.IsUnmanaged _, TyparConstraint.IsReferenceType _ + | TyparConstraint.IsReferenceType _, TyparConstraint.IsUnmanaged _ -> + return! ErrorD (Error(FSComp.SR.csUnmanagedConstraintInconsistent(), m)) | TyparConstraint.SupportsComparison _, TyparConstraint.SupportsComparison _ | TyparConstraint.SupportsEquality _, TyparConstraint.SupportsEquality _ @@ -2445,17 +2449,24 @@ and SolveTypeIsNonNullableValueType (csenv: ConstraintSolverEnv) ndeep m2 trace } and SolveTypeIsUnmanaged (csenv: ConstraintSolverEnv) ndeep m2 trace ty = - let g = csenv.g - let m = csenv.m - let denv = csenv.DisplayEnv - match tryDestTyparTy g ty with - | ValueSome destTypar -> - AddConstraint csenv ndeep m2 trace destTypar (TyparConstraint.IsUnmanaged m) - | _ -> - if isUnmanagedTy g ty then - CompleteD - else - ErrorD (ConstraintSolverError(FSComp.SR.csGenericConstructRequiresUnmanagedType(NicePrint.minimalStringOfType denv ty), m, m2)) + trackErrors { + let g = csenv.g + let m = csenv.m + let denv = csenv.DisplayEnv + match tryDestTyparTy g ty with + | ValueSome destTypar -> + return! AddConstraint csenv ndeep m2 trace destTypar (TyparConstraint.IsUnmanaged m) + | _ -> + if isStructAnonRecdTy g ty then + return! destStructAnonRecdTy g ty |> IterateD (SolveTypeIsUnmanaged csenv (ndeep + 1) m2 trace) + else if isStructTupleTy g ty then + return! destStructTupleTy g ty |> IterateD (SolveTypeIsUnmanaged csenv (ndeep + 1) m2 trace) + else + if isUnmanagedTy g ty then + return! CompleteD + else + return! ErrorD (ConstraintSolverError(FSComp.SR.csGenericConstructRequiresUnmanagedType(NicePrint.minimalStringOfType denv ty), m, m2)) + } and SolveTypeChoice (csenv: ConstraintSolverEnv) ndeep m2 trace ty choiceTys = diff --git a/src/Compiler/CodeGen/IlxGen.fs b/src/Compiler/CodeGen/IlxGen.fs index 829b7e03d20..2ca07c4b341 100644 --- a/src/Compiler/CodeGen/IlxGen.fs +++ b/src/Compiler/CodeGen/IlxGen.fs @@ -5582,7 +5582,7 @@ and GenGenericParam cenv eenv (tp: Typar) = let refTypeConstraint = tp.Constraints |> List.exists (function - | TyparConstraint.IsReferenceType _ -> true + | TyparConstraint.IsReferenceType _ | TyparConstraint.SupportsNull _ -> true | _ -> false) diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index 48b39055581..3ddcfedfbd3 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -314,6 +314,7 @@ csTypeDoesNotSupportConversion,"The type '%s' does not support a conversion to t csMethodFoundButIsStatic,"The type '%s' has a method '%s' (full name '%s'), but the method is static" csMethodFoundButIsNotStatic,"The type '%s' has a method '%s' (full name '%s'), but the method is not static" 472,csStructConstraintInconsistent,"The constraints 'struct' and 'not struct' are inconsistent" +473,csUnmanagedConstraintInconsistent,"The constraints 'unmanaged' and 'not struct' are inconsistent" csTypeDoesNotHaveNull,"The type '%s' does not have 'null' as a proper value" csNullableTypeDoesNotHaveNull,"The type '%s' does not have 'null' as a proper value. To create a null value for a Nullable type use 'System.Nullable()'." csTypeDoesNotSupportComparison1,"The type '%s' does not support the 'comparison' constraint because it has the 'NoComparison' attribute" diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index c166230e718..deda2c9d72e 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -829,6 +829,10 @@ let destAnyParTy g ty = ty |> stripTyEqns g |> (function TType_var (v, _) -> v | let destMeasureTy g ty = ty |> stripTyEqns g |> (function TType_measure m -> m | _ -> failwith "destMeasureTy: not a unit-of-measure type") +let destAnonRecdTy g ty = ty |> stripTyEqns g |> (function TType_anon (anonInfo, tys) -> anonInfo, tys | _ -> failwith "destAnonRecdTy: not an anonymous record type") + +let destStructAnonRecdTy g ty = ty |> stripTyEqns g |> (function TType_anon (anonInfo, tys) when evalAnonInfoIsStruct anonInfo -> tys | _ -> failwith "destAnonRecdTy: not a struct anonymous record type") + let isFunTy g ty = ty |> stripTyEqns g |> (function TType_fun _ -> true | _ -> false) let isForallTy g ty = ty |> stripTyEqns g |> (function TType_forall _ -> true | _ -> false) @@ -1983,22 +1987,20 @@ let isForallFunctionTy g ty = let _, tau = tryDestForallTy g ty isFunTy g tau -// ECMA C# LANGUAGE SPECIFICATION, 27.2 // An unmanaged-type is any type that isn't a reference-type, a type-parameter, or a generic struct-type and // contains no fields whose type is not an unmanaged-type. In other words, an unmanaged-type is one of the // following: // - sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, or bool. // - Any enum-type. // - Any pointer-type. -// - Any non-generic user-defined struct-type that contains fields of unmanaged-types only. -// [Note: Constructed types and type-parameters are never unmanaged-types. end note] +// - Any generic user-defined struct-type that can be statically determined to be 'unmanaged' at construction. let rec isUnmanagedTy g ty = let ty = stripTyEqnsAndMeasureEqns g ty match tryTcrefOfAppTy g ty with | ValueSome tcref -> - let isEq tcref2 = tyconRefEq g tcref tcref2 + let isEq tcref2 = tyconRefEq g tcref tcref2 if isEq g.nativeptr_tcr || isEq g.nativeint_tcr || - isEq g.sbyte_tcr || isEq g.byte_tcr || + isEq g.sbyte_tcr || isEq g.byte_tcr || isEq g.int16_tcr || isEq g.uint16_tcr || isEq g.int32_tcr || isEq g.uint32_tcr || isEq g.int64_tcr || isEq g.uint64_tcr || @@ -2010,15 +2012,19 @@ let rec isUnmanagedTy g ty = true else let tycon = tcref.Deref - if tycon.IsEnumTycon then + if tycon.IsEnumTycon then true elif tycon.IsStructOrEnumTycon then - match tycon.TyparsNoRange with - | [] -> tycon.AllInstanceFieldsAsList |> List.forall (fun r -> isUnmanagedTy g r.rfield_type) - | _ -> false // generic structs are never + let tinst = mkInstForAppTy g ty + tycon.AllInstanceFieldsAsList |> List.forall (fun r -> isUnmanagedTy g (actualTyOfRecdField tinst r)) else false | ValueNone -> - false + if isStructTupleTy g ty then + (destStructTupleTy g ty) |> List.forall (isUnmanagedTy g) + else if isStructAnonRecdTy g ty then + (destStructAnonRecdTy g ty) |> List.forall (isUnmanagedTy g) + else + false let isInterfaceTycon x = isILInterfaceTycon x || x.IsFSharpInterfaceTycon diff --git a/src/Compiler/TypedTree/TypedTreeOps.fsi b/src/Compiler/TypedTree/TypedTreeOps.fsi index 00d838e04d4..ff7d38f66bb 100755 --- a/src/Compiler/TypedTree/TypedTreeOps.fsi +++ b/src/Compiler/TypedTree/TypedTreeOps.fsi @@ -633,6 +633,10 @@ val destAnyParTy: TcGlobals -> TType -> Typar val destMeasureTy: TcGlobals -> TType -> Measure +val destAnonRecdTy: TcGlobals -> TType -> AnonRecdTypeInfo * TTypes + +val destStructAnonRecdTy: TcGlobals -> TType -> TTypes + val tryDestForallTy: TcGlobals -> TType -> Typars * TType val isFunTy: TcGlobals -> TType -> bool diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index 04cb5e27259..a4684e479de 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -102,6 +102,11 @@ Argument {0} neodpovídá + + The constraints 'unmanaged' and 'not struct' are inconsistent + The constraints 'unmanaged' and 'not struct' are inconsistent + + The type provider designer assembly '{0}' could not be loaded from folder '{1}' because a dependency was missing or could not loaded. All dependencies of the type provider designer assembly must be located in the same folder as that assembly. The exception reported was: {2} - {3} Navržené sestavení poskytovatele typu {0} nešlo načíst ze složky {1}, protože chyběla závislost nebo ji nešlo načíst. Všechny závislosti tohoto sestavení se musí nacházet ve stejné složce jako toto sestavení. Ohlášená výjimka: {2} – {3} diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index c7f65de13a0..86571016820 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -102,6 +102,11 @@ Das Argument "{0}" stimmt nicht überein. + + The constraints 'unmanaged' and 'not struct' are inconsistent + The constraints 'unmanaged' and 'not struct' are inconsistent + + The type provider designer assembly '{0}' could not be loaded from folder '{1}' because a dependency was missing or could not loaded. All dependencies of the type provider designer assembly must be located in the same folder as that assembly. The exception reported was: {2} - {3} Die Typanbieter-Designerassembly "{0}" konnte aus dem Ordner "{1}" nicht geladen werden, weil eine Abhängigkeit fehlte oder nicht geladen werden konnte. Alle Abhängigkeiten der Typanbieter-Designerassembly müssen sich in demselben Ordner wie die Assembly befinden. Gemeldete Ausnahme: {2} – {3} diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index c76f988d7a5..64d7b34e02d 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -102,6 +102,11 @@ El argumento "{0}" no coincide. + + The constraints 'unmanaged' and 'not struct' are inconsistent + The constraints 'unmanaged' and 'not struct' are inconsistent + + The type provider designer assembly '{0}' could not be loaded from folder '{1}' because a dependency was missing or could not loaded. All dependencies of the type provider designer assembly must be located in the same folder as that assembly. The exception reported was: {2} - {3} No se pudo cargar el ensamblado del diseñador de proveedores de tipos "{0}" desde la carpeta "{1}" porque falta una dependencia o no se pudo cargar. Todas las dependencias del ensamblado del diseñador de proveedores de tipos deben encontrarse en la misma carpeta que el ensamblado. Se notificó la excepción: {2} - {3}. diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index a89ad510309..d1d2eb85e9a 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -102,6 +102,11 @@ L'argument '{0}' ne correspond pas + + The constraints 'unmanaged' and 'not struct' are inconsistent + The constraints 'unmanaged' and 'not struct' are inconsistent + + The type provider designer assembly '{0}' could not be loaded from folder '{1}' because a dependency was missing or could not loaded. All dependencies of the type provider designer assembly must be located in the same folder as that assembly. The exception reported was: {2} - {3} Impossible de charger l'assembly de concepteur de fournisseur de type '{0}' à partir du dossier '{1}', car une dépendance est manquante ou n'a pas pu être chargée. Toutes les dépendances de l'assembly de concepteur de fournisseur de type doivent se trouver dans le même dossier que cet assembly. Exception signalée : {2} - {3} diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index a4044cee248..18e8c07bdca 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -102,6 +102,11 @@ L'argomento '{0}' non corrisponde + + The constraints 'unmanaged' and 'not struct' are inconsistent + The constraints 'unmanaged' and 'not struct' are inconsistent + + The type provider designer assembly '{0}' could not be loaded from folder '{1}' because a dependency was missing or could not loaded. All dependencies of the type provider designer assembly must be located in the same folder as that assembly. The exception reported was: {2} - {3} Non è stato possibile caricare l'assembly '{0}' della finestra di progettazione del provider di tipi dalla cartella '{1}' perché una dipendenza non è presente o non è stato possibile caricarla. Tutte le dipendenze dell'assembly della finestra di progettazione del provider di tipi devono trovarsi nella stessa cartella dell'assembly. L'eccezione restituita è {2} - {3} diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index 06c51c429d4..f297de2e604 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -102,6 +102,11 @@ 引数 '{0}' が一致しません + + The constraints 'unmanaged' and 'not struct' are inconsistent + The constraints 'unmanaged' and 'not struct' are inconsistent + + The type provider designer assembly '{0}' could not be loaded from folder '{1}' because a dependency was missing or could not loaded. All dependencies of the type provider designer assembly must be located in the same folder as that assembly. The exception reported was: {2} - {3} 依存関係がないか、または読み込めなかったため、型プロバイダーのデザイナー アセンブリ '{0}' をフォルダー '{1}' から読み込めませんでした。型プロバイダーのデザイナー アセンブリのすべての依存関係は、そのアセンブリと同じフォルダーに配置されている必要があります。次の例外が報告されました: {2} - {3} diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index f6cfde3fd98..e841236beeb 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -102,6 +102,11 @@ '{0}' 인수가 일치하지 않습니다. + + The constraints 'unmanaged' and 'not struct' are inconsistent + The constraints 'unmanaged' and 'not struct' are inconsistent + + The type provider designer assembly '{0}' could not be loaded from folder '{1}' because a dependency was missing or could not loaded. All dependencies of the type provider designer assembly must be located in the same folder as that assembly. The exception reported was: {2} - {3} 종속성이 없거나 로드되지 않았으므로 '{0}' 형식 공급자 디자이너 어셈블리를 '{1}' 폴더에서 로드할 수 없습니다. 형식 공급자 디자이너 어셈블리의 모든 종속성은 해당 어셈블리와 동일한 폴더에 있어야 합니다. 보고된 예외: {2} - {3} diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index 95a8895c109..75e30638922 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -102,6 +102,11 @@ Argument „{0}” nie jest zgodny + + The constraints 'unmanaged' and 'not struct' are inconsistent + The constraints 'unmanaged' and 'not struct' are inconsistent + + The type provider designer assembly '{0}' could not be loaded from folder '{1}' because a dependency was missing or could not loaded. All dependencies of the type provider designer assembly must be located in the same folder as that assembly. The exception reported was: {2} - {3} Nie można załadować zestawu projektanta dostawców typów „{0}” z folderu „{1}”, ponieważ brakuje zależności lub nie można jej załadować. Wszystkie zależności zestawu projektanta dostawców typów muszą znajdować się w tym samym folderze co ten zestaw. Zgłoszony wyjątek: {2} — {3} diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index f8e182c61d8..4350465579a 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -102,6 +102,11 @@ O argumento '{0}' não corresponde + + The constraints 'unmanaged' and 'not struct' are inconsistent + The constraints 'unmanaged' and 'not struct' are inconsistent + + The type provider designer assembly '{0}' could not be loaded from folder '{1}' because a dependency was missing or could not loaded. All dependencies of the type provider designer assembly must be located in the same folder as that assembly. The exception reported was: {2} - {3} Não foi possível carregar o assembly do designer do provedor de tipos '{0}' da pasta '{1}' porque uma dependência estava ausente ou não pôde ser carregada. Todas as dependências do assembly do designer do provedor de tipos precisam estar localizadas na mesma pasta que esse assembly. A exceção relatada foi: {2} – {3} diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index 6041cda9ec8..5706f96ee5c 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -102,6 +102,11 @@ Аргумент "{0}" не соответствует + + The constraints 'unmanaged' and 'not struct' are inconsistent + The constraints 'unmanaged' and 'not struct' are inconsistent + + The type provider designer assembly '{0}' could not be loaded from folder '{1}' because a dependency was missing or could not loaded. All dependencies of the type provider designer assembly must be located in the same folder as that assembly. The exception reported was: {2} - {3} Не удалось загрузить сборку конструктора поставщика типа "{0}" из папки "{1}", так как зависимость отсутствует или не может быть загружена. Все зависимости для сборки конструктора поставщика типа должны находиться в папке сборки. Получено исключение: {2} — {3} diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index d9738db5a5f..c3c49bcdb16 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -102,6 +102,11 @@ '{0}' bağımsız değişkeni eşleşmiyor + + The constraints 'unmanaged' and 'not struct' are inconsistent + The constraints 'unmanaged' and 'not struct' are inconsistent + + The type provider designer assembly '{0}' could not be loaded from folder '{1}' because a dependency was missing or could not loaded. All dependencies of the type provider designer assembly must be located in the same folder as that assembly. The exception reported was: {2} - {3} '{0}' tür sağlayıcısı tasarımcı bütünleştirilmiş kodu, bir bağımlılık eksik olduğundan veya yüklenemediğinden '{1}' klasöründen yüklenemedi. Tür sağlayıcısı tasarımcısı bütünleştirilmiş kodunun tüm bağımlılıkları, ilgili bütünleştirilmiş kodun bulunduğu klasörde bulunmalıdır. Bildirilen özel durum: {2} - {3} diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index 0bf4e080159..0119c38d1da 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -102,6 +102,11 @@ 参数 "{0}" 不匹配 + + The constraints 'unmanaged' and 'not struct' are inconsistent + The constraints 'unmanaged' and 'not struct' are inconsistent + + The type provider designer assembly '{0}' could not be loaded from folder '{1}' because a dependency was missing or could not loaded. All dependencies of the type provider designer assembly must be located in the same folder as that assembly. The exception reported was: {2} - {3} 无法从文件夹“{1}”加载类型提供程序设计器程序集“{0}”,因为依赖项缺失或无法加载。类型提供程序设计器程序集的所有依赖项必须与该程序集位于同一文件夹中。报告的异常是: {2} - {3} diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index 24faf41e95c..3cf9111c44b 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -102,6 +102,11 @@ 引數 '{0}' 不相符 + + The constraints 'unmanaged' and 'not struct' are inconsistent + The constraints 'unmanaged' and 'not struct' are inconsistent + + The type provider designer assembly '{0}' could not be loaded from folder '{1}' because a dependency was missing or could not loaded. All dependencies of the type provider designer assembly must be located in the same folder as that assembly. The exception reported was: {2} - {3} 因為缺少相依性或相依性無法載入,導致無法從資料夾 '{1}' 載入類型提供者設計工具組件 '{0}'。類型提供者設計工具組件的所有相依性都必須位於該組件所在的資料夾內。回報的例外狀況: {2} - {3} diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs new file mode 100644 index 00000000000..bba2a7e18c2 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs @@ -0,0 +1,194 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Compiler.ComponentTests.Conformance.Constraints + +open Xunit +open FSharp.Test.Compiler + +module Unmanaged = + + [] + let ``User-defined struct types considered unmanaged when all members are unmanaged`` () = + Fsx """ +[] +type MyStruct(x: int, y: int) = + member _.X = x + member _.Y = y +[] +type MyStructGeneric<'T when 'T: unmanaged>(x: 'T, y: 'T) = + member _.X = x + member _.Y = y +[] +type MyStructGenericWithNoConstraint<'T>(x: 'T, y: 'T) = + member _.X = x + member _.Y = y +[] +type Test<'T when 'T: unmanaged> = + val element: 'T +[] +type S<'T> = + val X : 'T + new (x) = { X = x } +let test (x: 'T when 'T : unmanaged) = () +test(Unchecked.defaultof>) +test(S(1)) +test(S(MyStruct(1,2))) +test(S>(MyStructGeneric(1,2))) +test(S>(MyStructGenericWithNoConstraint(1,2))) +let _ = Test() +let _ = Test() +let _ = Test>() +let _ = Test>() + """ + |> typecheck + |> shouldSucceed + |> ignore + + [] + let ``Struct tuples considered unmanaged when all elements are unmanaged`` () = + Fsx """ +[] +type Test<'T when 'T: unmanaged> = + val element: 'T +let test (x: 'T when 'T : unmanaged) = () +let x = struct(1, 2) +test x +test (struct(1, 2)) + """ + |> typecheck + |> shouldSucceed + |> ignore + + [] + let ``User-defined struct (anonymous)records considered unmanaged when all fields are unmanaged`` () = + Fsx """ +[] +type MyRecd = { X: int; Y: int; } +[] +type MyRecdGeneric<'T when 'T: unmanaged> = { X:'T; Y: 'T; } +[] +type MyRecdGenericWithNoConstraint<'T> = { X:'T; Y: 'T; } +[] +type Test<'T when 'T: unmanaged> = + val element: 'T +[] +type S<'T> = + val X : 'T + new (x) = { X = x } +let test (x: 'T when 'T : unmanaged) = () +test(Unchecked.defaultof>) +test(S(1)) +test(S({ X = 1; Y = 1 })) +test(S>({ X = 1; Y = 1 })) +test(S>({ X = 1; Y = 1 })) +let x = struct {| X = 1; Y = 1 |} +test(x) +test(struct {| X = 1; Y = 1 |}) +let _ = Test() +let _ = Test() +let _ = Test>() +let _ = Test>() + """ + |> typecheck + |> shouldSucceed + |> ignore + + [] + let ``Struct single- and multi-case unions considered unmanaged when all cases are all unmanaged`` () = + Fsx """ +[] +type SingleCaseUnion = X +[] +type MultiCaseUnion = A | B +[] +type Single<'T> = C of 'T +[] +type SingleC<'T when 'T: unmanaged> = CC of 'T +[] +type Result<'T,'TError> = + | Ok of ok: 'T + | Error of error: 'TError +[] +type ResultC<'T,'TError> = + | OkC of ok: 'T + | ErrorC of error: 'TError +[] +type Test<'T when 'T: unmanaged> = + val element: 'T +let test (x: 'T when 'T : unmanaged) = () +test(SingleCaseUnion.X) +test(MultiCaseUnion.A) +test(MultiCaseUnion.B) +test(C 1) +test(CC 1) +test(Ok 1) +test(Error 2) +test(OkC 1) +test(ErrorC 1) +let _ = Test() +let _ = Test() +let _ = Test>() +let _ = Test>() +let _ = Test>() +let _ = Test>() + """ + |> typecheck + |> shouldSucceed + |> ignore + + [] + let ``Generic user-defined type with non-unmanaged types is NOT considered unmanaged`` () = + Fsx """ +type NonStructRecd = { X: int } +type NonStructRecdC<'T when 'T : unmanaged> = { X: 'T } +[] +type Test<'T when 'T: unmanaged> = + val element: 'T +[] +type W<'T> = { x: 'T } +[] +type S<'T> = { x: W<'T> } +[] +type X<'T> = + val Z : 'T + new (x) = { Z = x } + +[] +type A<'T, 'U> = + [] val X : 'T +let test (x: 'T when 'T : unmanaged) = () +test(Unchecked.defaultof>) +test(X(obj())) +test (A()) +let foo<'T> () = test (A<'T, obj>()) +let _ = Test() +let _ = Test() +let _ = Test>() + """ + |> typecheck + |> shouldFail + |> withDiagnostics[ + (Error 1, Line 20, Col 6, Line 20, Col 33, "A generic construct requires that the type 'S' is an unmanaged type") + (Error 193, Line 21, Col 6, Line 21, Col 14, "A generic construct requires that the type 'X<'a>' is an unmanaged type") + (Error 193, Line 22, Col 7, Line 22, Col 20, "A generic construct requires that the type 'A' is an unmanaged type") + (Error 193, Line 23, Col 24, Line 23, Col 36, "A generic construct requires that the type 'A<'T,obj>' is an unmanaged type") + (Error 1, Line 24, Col 9, Line 24, Col 18, "A generic construct requires that the type 'obj' is an unmanaged type") + (Error 1, Line 25, Col 9, Line 25, Col 28, "A generic construct requires that the type 'NonStructRecd' is an unmanaged type") + (Error 1, Line 26, Col 9, Line 26, Col 34, "A generic construct requires that the type 'NonStructRecdC' is an unmanaged type")] + + [] + let ``Disallow both 'unmanaged' and 'not struct' constraints`` () = + Fsx "type X<'T when 'T: unmanaged and 'T: not struct> = class end" + |> typecheck + |> shouldFail + |> withDiagnostics[ + (Error 43, Line 1, Col 34, Line 1, Col 48, "The constraints 'unmanaged' and 'not struct' are inconsistent")] + + [] + let ``Modreq is emitted for unmanaged type parameters`` () = + Fsx "[] type Test<'T when 'T: unmanaged and 'T: struct> = struct end" + |> compile + |> shouldSucceed + |> verifyIL ["foo"] + // let ``Attribute is emitted for unmanaged`` () = ignore + // let ``C# <-> F# cross-project unmanaged usage`` () = ignore \ No newline at end of file diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index 0b5e005d7b7..99ee45771b5 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -27,6 +27,12 @@ FsUnit.fs + + %(RelativeDir)\TestSource\%(Filename)%(Extension) + + + %(RelativeDir)\TestSource\%(Filename)%(Extension) + @@ -40,7 +46,8 @@ - + + @@ -159,7 +166,7 @@ - + @@ -173,12 +180,6 @@ - - %(RelativeDir)\TestSource\%(Filename)%(Extension) - - - %(RelativeDir)\TestSource\%(Filename)%(Extension) - diff --git a/tests/fsharp/typecheck/sigs/neg30.bsl b/tests/fsharp/typecheck/sigs/neg30.bsl index b958db2e367..b57ca6bcb82 100644 --- a/tests/fsharp/typecheck/sigs/neg30.bsl +++ b/tests/fsharp/typecheck/sigs/neg30.bsl @@ -11,8 +11,6 @@ neg30.fs(46,7,46,26): typecheck error FS0001: A generic construct requires that neg30.fs(47,6,47,8): typecheck error FS0001: A generic construct requires that the type 'FSharpUnion' is an unmanaged type -neg30.fs(48,7,48,26): typecheck error FS0001: A generic construct requires that the type 'SGeneric' is an unmanaged type - neg30.fs(59,13,59,24): typecheck error FS0001: A generic construct requires that the type 'SManaged' is an unmanaged type neg30.fs(60,13,60,33): typecheck error FS0001: A generic construct requires that the type 'SManagedRecursive' is an unmanaged type @@ -23,8 +21,6 @@ neg30.fs(62,13,62,29): typecheck error FS0001: A generic construct requires that neg30.fs(63,13,63,27): typecheck error FS0001: A generic construct requires that the type 'FSharpUnion' is an unmanaged type -neg30.fs(64,13,64,29): typecheck error FS0001: A generic construct requires that the type 'SGeneric' is an unmanaged type - neg30.fs(71,12,71,35): typecheck error FS0120: hello! neg30.fs(71,12,71,35): typecheck error FS0120: hello! diff --git a/tests/fsharpqa/Source/Conformance/TypesAndTypeConstraints/CheckingSyntacticTypes/E_UnmanagedConstraint01.fs b/tests/fsharpqa/Source/Conformance/TypesAndTypeConstraints/CheckingSyntacticTypes/E_UnmanagedConstraint01.fs index a913ce9a79c..ccbb5da2580 100644 --- a/tests/fsharpqa/Source/Conformance/TypesAndTypeConstraints/CheckingSyntacticTypes/E_UnmanagedConstraint01.fs +++ b/tests/fsharpqa/Source/Conformance/TypesAndTypeConstraints/CheckingSyntacticTypes/E_UnmanagedConstraint01.fs @@ -1,8 +1,7 @@ // #Conformance #TypeConstraints // Simple baselines for unmanaged constraint -//A generic construct requires that the type ''a S' is an unmanaged type -//A generic construct requires that the type 'D' is an unmanaged type -//A generic construct requires that the type 'C' is an unmanaged type +//A generic construct requires that the type 'D' is an unmanaged type +//A generic construct requires that the type 'C' is an unmanaged type let testFunc<'a when 'a:unmanaged> (x : 'a) = let z = x From 9314da6c2146dda77104c7941be7f77c16952938 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Thu, 20 Jul 2023 13:53:17 +0200 Subject: [PATCH 02/15] fix build --- src/Compiler/xlf/FSComp.txt.cs.xlf | 1 + src/Compiler/xlf/FSComp.txt.de.xlf | 1 + src/Compiler/xlf/FSComp.txt.es.xlf | 1 + src/Compiler/xlf/FSComp.txt.fr.xlf | 1 + src/Compiler/xlf/FSComp.txt.it.xlf | 1 + src/Compiler/xlf/FSComp.txt.ja.xlf | 1 + src/Compiler/xlf/FSComp.txt.ko.xlf | 1 + src/Compiler/xlf/FSComp.txt.pl.xlf | 1 + src/Compiler/xlf/FSComp.txt.pt-BR.xlf | 1 + src/Compiler/xlf/FSComp.txt.ru.xlf | 1 + src/Compiler/xlf/FSComp.txt.tr.xlf | 1 + src/Compiler/xlf/FSComp.txt.zh-Hans.xlf | 1 + src/Compiler/xlf/FSComp.txt.zh-Hant.xlf | 1 + .../Conformance/Constraints/Unmanaged.fs | 6 +-- .../FSharp.Compiler.ComponentTests.fsproj | 52 +------------------ 15 files changed, 18 insertions(+), 53 deletions(-) diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index 1c41da0da73..6a162622c8c 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -166,6 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n Všechny větve výrazu if musí vracet hodnoty implicitně převoditelné na typ první větve, což je řazená kolekce členů o délce {0} typu\n {1} \nTato větev vrací řazenou kolekci členů o délce {2} typu\n {3} \n diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index 60ad6ebf45b..a50f5cdc5f3 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -166,6 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n Alle Verzweigungen eines „If-Ausdrucks“ müssen Werte zurückgeben, die implizit in den Typ der ersten Verzweigung konvertierbar sind. In diesem Fall handelt es sich dabei um ein Tupel der Länge {0} des Typs.\n {1} \nDiese Verzweigung gibt ein Tupel der Länge {2} des Typs\n {3} \nzurück. diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index d231fc08795..5cd51ce1ebc 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -166,6 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n Todas las ramas de una expresión 'if' deben devolver valores implícitamente convertibles al tipo de la primera rama, que aquí es una tupla de longitud {0} de tipo\n {1} \nEsta rama devuelve una tupla de longitud {2} de tipo\n {3} \n diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index 0f7707876e1..f14a174362a 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -166,6 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n Toutes les branches d’une expression « if » doivent retourner des valeurs implicitement convertibles au type de la première branche, qui est ici un tuple de longueur {0} de type\n {1} \nCette branche renvoie un tuple de longueur {2} de type\n {3} \n diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index 7234b916f2a..ff7c8ab693c 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -166,6 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n Tutti i rami di un'espressione 'if' devono restituire valori convertibili in modo implicito nel tipo del primo ramo, che è una tupla di lunghezza {0} di tipo\n {1} \nQuesto ramo restituisce una tupla di lunghezza {2} di tipo\n {3} \n diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index 87f86055d13..17df353a2e5 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -166,6 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n 'if' 式のすべての分岐は、最初の分岐の型に暗黙的に変換できる値を返す必要があります。これは、型の長さ {0} のタプルです\n {1} \nこの分岐は、型の長さ {2} のタプルを返します\n {3} \n diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index 61bbf5b5b3a..53a4bfd74b1 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -166,6 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n 'if' 식의 모든 분기는 첫 번째 분기의 유형으로 암시적으로 변환 가능한 값을 반환해야 합니다. 여기서는 형식이 \n {1}이고 길이가 {0}인 튜플입니다. \n이 분기는 형식이 \n {3}이고 길이가 {2}인 튜플을 반환합니다. \n diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index dbeb819e3b8..ba32df5753f 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -166,6 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n Wszystkie gałęzie wyrażenia „if” muszą zwracać wartości niejawnie konwertowalne na typ pierwszej gałęzi, która tutaj jest krotką o długości {0} typu\n {1} \nTa gałąź zwraca krotkę o długości {2} typu\n {3} \n diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 4303279713d..f30c15872b1 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -166,6 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n Todas as ramificações de uma expressão 'if' devem retornar valores implicitamente conversíveis ao tipo da primeira ramificação, que aqui é uma tupla de comprimento {0} do tipo\n {1} \nEsta ramificação retorna uma tupla de comprimento {2} do tipo\n {3} \n diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index a61aa3bb6be..373d66b4901 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -166,6 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n Все ветви выражения "if" должны возвращать значения, поддерживающие неявное преобразование в тип первой ветви, которым здесь является кортеж длиной {0} типа\n {1} \nЭта ветвь возвращает кортеж длиной {2} типа\n {3} \n diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index 7d3bc24a52d..6b55c2d3e15 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -166,6 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n Bir 'if' ifadesinin tüm dalları, örtük olarak ilk dalın türüne dönüştürülebilir değerler döndürmelidir. Burada ilk dal {0} uzunluğunda türü\n {1} olan bir demet \nBu dal {2} uzunluğunda türü\n {3} \nolan bir demet döndürüyor diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index 326b7d84134..30c50df4d88 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -166,6 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n “if” 表达式的所有分支必须返回可隐式转换为第一个分支类型的值,这是一个长度为 {0} 的类型的元组\n {1} \n此分支会返回长度为 {2} 的类型的元组\n {3} \n diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index 3afbd142d87..36311cc8b6e 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -166,6 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n 'if' 運算式的所有分支都傳回可隱含轉換為第一個分支的類型的值,這是類型為\n {1} \n的元組長度 {0}此分支傳回的是類型為\n {3} \n的元組長度 {2} diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs index bba2a7e18c2..86a3b0a8bdf 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ComponentTests.Conformance.Constraints +namespace Conformance.Constraints open Xunit open FSharp.Test.Compiler @@ -167,7 +167,7 @@ let _ = Test>() """ |> typecheck |> shouldFail - |> withDiagnostics[ + |> withDiagnostics [ (Error 1, Line 20, Col 6, Line 20, Col 33, "A generic construct requires that the type 'S' is an unmanaged type") (Error 193, Line 21, Col 6, Line 21, Col 14, "A generic construct requires that the type 'X<'a>' is an unmanaged type") (Error 193, Line 22, Col 7, Line 22, Col 20, "A generic construct requires that the type 'A' is an unmanaged type") @@ -181,7 +181,7 @@ let _ = Test>() Fsx "type X<'T when 'T: unmanaged and 'T: not struct> = class end" |> typecheck |> shouldFail - |> withDiagnostics[ + |> withDiagnostics [ (Error 43, Line 1, Col 34, Line 1, Col 48, "The constraints 'unmanaged' and 'not struct' are inconsistent")] [] diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index 6501a685d9a..1a0480ed9d5 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -24,55 +24,7 @@ FsUnit.fs - - - %(RelativeDir)\TestSource\%(Filename)%(Extension) - - - %(RelativeDir)\TestSource\%(Filename)%(Extension) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -114,6 +66,7 @@ + @@ -216,7 +169,6 @@ - From f4f39203490c65a961ba7bdae5f2ee015c0a62da Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Thu, 20 Jul 2023 14:29:35 +0200 Subject: [PATCH 03/15] deeper tests added --- .../Conformance/Constraints/Unmanaged.fs | 64 +++++++++++++++++-- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs index 86a3b0a8bdf..e9f532994f9 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs @@ -38,7 +38,9 @@ test(S>(MyStructGenericWithNoConstraint() let _ = Test() let _ = Test>() +let _ = Test>>>() let _ = Test>() +let _ = Test>>() """ |> typecheck |> shouldSucceed @@ -54,6 +56,14 @@ let test (x: 'T when 'T : unmanaged) = () let x = struct(1, 2) test x test (struct(1, 2)) +test (struct(1, 2, 3)) +test (struct(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, struct(12,13))) + +// test that constraint is being propagated +let functionUsingTestInternally (struct(a,b) as s) = + test s + +functionUsingTestInternally (struct(42,42)) """ |> typecheck |> shouldSucceed @@ -84,10 +94,23 @@ test(S>({ X = 1; Y = 1 })) let x = struct {| X = 1; Y = 1 |} test(x) test(struct {| X = 1; Y = 1 |}) + +// test that constraint is being propagated +let createAndTestAnonRecd (x) = + let created = struct{|OnlyValue = x|} + test(created) + () + +createAndTestAnonRecd 15 +createAndTestAnonRecd (struct{|Nested = 15|}) +createAndTestAnonRecd (struct{|Nested = struct(15,S>({ X = 1; Y = 1 }))|}) + let _ = Test() let _ = Test() let _ = Test>() +let _ = Test>>>() let _ = Test>() +let _ = Test>>() """ |> typecheck |> shouldSucceed @@ -109,7 +132,7 @@ type Result<'T,'TError> = | Ok of ok: 'T | Error of error: 'TError [] -type ResultC<'T,'TError> = +type ResultC<'T,'TError when 'T: unmanaged and 'TError: unmanaged> = | OkC of ok: 'T | ErrorC of error: 'TError [] @@ -123,14 +146,29 @@ test(C 1) test(CC 1) test(Ok 1) test(Error 2) -test(OkC 1) -test(ErrorC 1) +test(ResultC.OkC 1) +test(ResultC.ErrorC 1) let _ = Test() let _ = Test() let _ = Test>() +let _ = Test>>>() +let _ = Test>>>() let _ = Test>() +let _ = Test>>() let _ = Test>() +let _ = Test, Single>>() let _ = Test>() + +// test that constraint is being propagated +let resultCreatingFunction (x) = + try + let capturedVal = Ok x + test capturedVal + capturedVal + with _ -> Error 15 + +let _ = resultCreatingFunction A + """ |> typecheck |> shouldSucceed @@ -185,10 +223,26 @@ let _ = Test>() (Error 43, Line 1, Col 34, Line 1, Col 48, "The constraints 'unmanaged' and 'not struct' are inconsistent")] [] - let ``Modreq is emitted for unmanaged type parameters`` () = + let ``Modreq is not emitted for unmanaged type parameters`` () = Fsx "[] type Test<'T when 'T: unmanaged and 'T: struct> = struct end" |> compile |> shouldSucceed - |> verifyIL ["foo"] + |> verifyIL [""" + .class public abstract auto ansi sealed Test + extends [runtime]System.Object + { + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) + .class sequential ansi serializable sealed nested public beforefieldinit Test`1 + extends [runtime]System.ValueType + { + .pack 0 + .size 1 + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.StructAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.NoEqualityAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.NoComparisonAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 03 00 00 00 00 00 ) + } + + }"""] // let ``Attribute is emitted for unmanaged`` () = ignore // let ``C# <-> F# cross-project unmanaged usage`` () = ignore \ No newline at end of file From cfbeb951b79e4b1c595d7186374d9706c15ec332 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Thu, 20 Jul 2023 16:14:10 +0200 Subject: [PATCH 04/15] Codegen IsUnmanagedAttribute for typar; codegen the attribute class if needed --- src/Compiler/CodeGen/IlxGen.fs | 16 ++++++++- src/Compiler/CodeGen/IlxGenSupport.fs | 12 +++++-- src/Compiler/CodeGen/IlxGenSupport.fsi | 1 + src/Compiler/TypedTree/TcGlobals.fs | 2 ++ .../Conformance/Constraints/Unmanaged.fs | 35 +++++++++++++++++-- 5 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/Compiler/CodeGen/IlxGen.fs b/src/Compiler/CodeGen/IlxGen.fs index 27e43b88bec..f373e8abeaf 100644 --- a/src/Compiler/CodeGen/IlxGen.fs +++ b/src/Compiler/CodeGen/IlxGen.fs @@ -5571,6 +5571,12 @@ and GenGenericParam cenv eenv (tp: Typar) = | TyparConstraint.RequiresDefaultConstructor _ -> true | _ -> false) + let unmanagedConstraint = + tp.Constraints + |> List.exists (function + | TyparConstraint.IsUnmanaged _ -> true + | _ -> false) + let tpName = // use the CompiledName if given // Inference variables get given an IL name "TA, TB" etc. @@ -5598,7 +5604,15 @@ and GenGenericParam cenv eenv (tp: Typar) = else nm - let tpAttrs = mkILCustomAttrs (GenAttrs cenv eenv tp.Attribs) + let attributeList = + let defined = GenAttrs cenv eenv tp.Attribs + + if unmanagedConstraint then + (GetIsUnmanagedAttribute g) :: defined + else + defined + + let tpAttrs = mkILCustomAttrs (attributeList) { Name = tpName diff --git a/src/Compiler/CodeGen/IlxGenSupport.fs b/src/Compiler/CodeGen/IlxGenSupport.fs index f90371d7e44..6fd2074c4e6 100644 --- a/src/Compiler/CodeGen/IlxGenSupport.fs +++ b/src/Compiler/CodeGen/IlxGenSupport.fs @@ -155,10 +155,16 @@ let mkLocalPrivateInt32Enum (g: TcGlobals, tref: ILTypeRef, values: (string * in // Generate Local embeddable versions of framework types when necessary //-------------------------------------------------------------------------- -let GetReadOnlyAttribute (g: TcGlobals) = - let tref = g.attrib_IsReadOnlyAttribute.TypeRef +let private getPotentiallyEmbedableAttribute (g: TcGlobals) (info: BuiltinAttribInfo) = + let tref = info.TypeRef g.TryEmbedILType(tref, (fun () -> mkLocalPrivateAttributeWithDefaultConstructor (g, tref.Name))) - mkILCustomAttribute (g.attrib_IsReadOnlyAttribute.TypeRef, [], [], []) + mkILCustomAttribute (info.TypeRef, [], [], []) + +let GetReadOnlyAttribute (g: TcGlobals) = + getPotentiallyEmbedableAttribute g g.attrib_IsReadOnlyAttribute + +let GetIsUnmanagedAttribute (g: TcGlobals) = + getPotentiallyEmbedableAttribute g g.attrib_IsUnmanagedAttribute let GenReadOnlyAttributeIfNecessary g ty = if isInByrefTy g ty then diff --git a/src/Compiler/CodeGen/IlxGenSupport.fsi b/src/Compiler/CodeGen/IlxGenSupport.fsi index 4e2d9351c8f..24968a25ec6 100644 --- a/src/Compiler/CodeGen/IlxGenSupport.fsi +++ b/src/Compiler/CodeGen/IlxGenSupport.fsi @@ -21,3 +21,4 @@ val GetDynamicDependencyAttribute: g: TcGlobals -> memberTypes: int32 -> ilType: val GenReadOnlyModReqIfNecessary: g: TcGlobals -> ty: TypedTree.TType -> ilTy: ILType -> ILType val GenReadOnlyAttributeIfNecessary: g: TcGlobals -> ty: TypedTree.TType -> ILAttribute option val GetReadOnlyAttribute: g: TcGlobals -> ILAttribute +val GetIsUnmanagedAttribute: g: TcGlobals -> ILAttribute diff --git a/src/Compiler/TypedTree/TcGlobals.fs b/src/Compiler/TypedTree/TcGlobals.fs index 563add19a67..6715d0f5d0d 100755 --- a/src/Compiler/TypedTree/TcGlobals.fs +++ b/src/Compiler/TypedTree/TcGlobals.fs @@ -336,6 +336,7 @@ type TcGlobals( static let isInEmbeddableKnownSet name = match name with | "System.Runtime.CompilerServices.IsReadOnlyAttribute" + | "System.Runtime.CompilerServices.IsUnmanagedAttribute" | "System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute" | "System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes" -> true | _ -> false @@ -1422,6 +1423,7 @@ type TcGlobals( // We use 'findSysAttrib' here because lookup on attribute is done by name comparison, and can proceed // even if the type is not found in a system assembly. member val attrib_IsReadOnlyAttribute = findOrEmbedSysPublicType "System.Runtime.CompilerServices.IsReadOnlyAttribute" + member val attrib_IsUnmanagedAttribute = findOrEmbedSysPublicType "System.Runtime.CompilerServices.IsUnmanagedAttribute" member val attrib_DynamicDependencyAttribute = findOrEmbedSysPublicType "System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute" member val enum_DynamicallyAccessedMemberTypes = findOrEmbedSysPublicType "System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes" diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs index e9f532994f9..2fff10e28f1 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs @@ -223,7 +223,7 @@ let _ = Test>() (Error 43, Line 1, Col 34, Line 1, Col 48, "The constraints 'unmanaged' and 'not struct' are inconsistent")] [] - let ``Modreq is not emitted for unmanaged type parameters`` () = + let ``IsUnmanagedAttribute Attribute is emitted and generated for unmanaged constraint on type`` () = Fsx "[] type Test<'T when 'T: unmanaged and 'T: struct> = struct end" |> compile |> shouldSucceed @@ -241,8 +241,37 @@ let _ = Test>() .custom instance void [FSharp.Core]Microsoft.FSharp.Core.NoEqualityAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [FSharp.Core]Microsoft.FSharp.Core.NoComparisonAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 03 00 00 00 00 00 ) + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) } - }"""] - // let ``Attribute is emitted for unmanaged`` () = ignore + }""";""" +.class private auto ansi beforefieldinit System.Runtime.CompilerServices.IsUnmanagedAttribute + extends [runtime]System.Attribute"""] + + [] + let ``IsUnmanagedAttribute Attribute is emitted for function with unmanaged constraint`` () = + Fsx "let testMyFunction (x: 'TUnmanaged when 'TUnmanaged : unmanaged) = struct(x,1)" + |> compile + |> shouldSucceed + |> verifyIL [""" + .method public static valuetype [runtime]System.ValueTuple`2 + testMyFunction(!!TUnmanaged x) cil managed + { + .param type TUnmanaged + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 + IL_0002: newobj instance void valuetype [runtime]System.ValueTuple`2::.ctor(!0, + !1) + IL_0007: ret + } """;""" +.class private auto ansi beforefieldinit System.Runtime.CompilerServices.IsUnmanagedAttribute + extends [runtime]System.Attribute"""] + + + + // let ``C# <-> F# cross-project unmanaged usage`` () = ignore \ No newline at end of file From db1f5fd723857332d0b67240cd9abba12a9aff33 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Thu, 20 Jul 2023 16:40:11 +0200 Subject: [PATCH 05/15] covering builtin types with tests --- .../Conformance/Constraints/Unmanaged.fs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs index 2fff10e28f1..e4148fcac9e 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs @@ -38,6 +38,8 @@ test(S>(MyStructGenericWithNoConstraint() let _ = Test() let _ = Test>() +let _ = Test>() +let _ = Test>() let _ = Test>>>() let _ = Test>() let _ = Test>>() @@ -138,6 +140,11 @@ type ResultC<'T,'TError when 'T: unmanaged and 'TError: unmanaged> = [] type Test<'T when 'T: unmanaged> = val element: 'T +[] +type DateOrTimeStampOrUnixOrCustom = + | DuDateTime of dt:System.DateTime + | DuTimeSpan of ts:System.TimeSpan + | DuUnix of i:int let test (x: 'T when 'T : unmanaged) = () test(SingleCaseUnion.X) test(MultiCaseUnion.A) @@ -148,6 +155,8 @@ test(Ok 1) test(Error 2) test(ResultC.OkC 1) test(ResultC.ErrorC 1) +test(DuUnix 132456) +test(DuDateTime System.DateTime.Now) let _ = Test() let _ = Test() let _ = Test>() From 89663103ac4d3b33022e0a3a6fb14ecb4c0e557e Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Fri, 21 Jul 2023 13:55:19 +0200 Subject: [PATCH 06/15] import unmanaged constraint from Il attrib to F# --- src/Compiler/Checking/import.fs | 16 ++++++++++++---- .../Conformance/Constraints/Unmanaged.fs | 17 ++++++++++++++++- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/Compiler/Checking/import.fs b/src/Compiler/Checking/import.fs index bf6c9e73006..d40bcc60199 100644 --- a/src/Compiler/Checking/import.fs +++ b/src/Compiler/Checking/import.fs @@ -465,10 +465,18 @@ let ImportILGenericParameters amap m scoref tinst (gps: ILGenericParameterDefs) let tptys = tps |> List.map mkTyparTy let importInst = tinst@tptys (tps, gps) ||> List.iter2 (fun tp gp -> - let constraints = gp.Constraints |> List.map (fun ilTy -> TyparConstraint.CoercesTo(ImportILType amap m importInst (rescopeILType scoref ilTy), m) ) - let constraints = if gp.HasReferenceTypeConstraint then (TyparConstraint.IsReferenceType(m) :: constraints) else constraints - let constraints = if gp.HasNotNullableValueTypeConstraint then (TyparConstraint.IsNonNullableStruct(m) :: constraints) else constraints - let constraints = if gp.HasDefaultConstructorConstraint then (TyparConstraint.RequiresDefaultConstructor(m) :: constraints) else constraints + let constraints = + [ for ilTy in gp.Constraints do + TyparConstraint.CoercesTo(ImportILType amap m importInst (rescopeILType scoref ilTy), m) + if gp.HasReferenceTypeConstraint then + TyparConstraint.IsReferenceType(m) + if gp.HasNotNullableValueTypeConstraint then + TyparConstraint.IsNonNullableStruct(m) + if gp.HasDefaultConstructorConstraint then + TyparConstraint.RequiresDefaultConstructor(m) + if gp.CustomAttrs |> TryFindILAttribute amap.g.attrib_IsUnmanagedAttribute then + TyparConstraint.IsUnmanaged(m) ] + tp.SetConstraints constraints) tps diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs index e4148fcac9e..5fefbb9b9d4 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs @@ -4,6 +4,7 @@ namespace Conformance.Constraints open Xunit open FSharp.Test.Compiler +open FSharp.Test module Unmanaged = @@ -282,5 +283,19 @@ let _ = Test>() + [] + let ``Consume C#-defined unmanaged constraint incorrectly in F# - report error`` () = + let csLib = + CSharp "namespace CsLib{ public record struct CsharpStruct(T item) where T:unmanaged;}" + |> withCSharpLanguageVersion CSharpLanguageVersion.Preview + |> withName "csLib" + + let app = FSharp """module MyFsharpApp +open CsLib +let y = new CsharpStruct(struct(1,"this is string")) + """ |> withReferences [csLib] - // let ``C# <-> F# cross-project unmanaged usage`` () = ignore \ No newline at end of file + app + |> compile + |> shouldFail + |> withDiagnostics [(Error 1, Line 3, Col 13, Line 3, Col 45, "A generic construct requires that the type 'string' is an unmanaged type")] \ No newline at end of file From 9c4d064a2ad63696cdcee001b277275eabd4a437 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Fri, 21 Jul 2023 16:20:13 +0200 Subject: [PATCH 07/15] fix DU handling, voption tests, C# interop tests --- src/Compiler/Checking/ConstraintSolver.fs | 7 ++ src/Compiler/TypedTree/TypedTreeOps.fs | 12 +- src/Compiler/TypedTree/TypedTreeOps.fsi | 2 + .../Conformance/Constraints/Unmanaged.fs | 113 ++++++++++++++++-- 4 files changed, 122 insertions(+), 12 deletions(-) diff --git a/src/Compiler/Checking/ConstraintSolver.fs b/src/Compiler/Checking/ConstraintSolver.fs index 6fb051791e8..ec92c2d20cb 100644 --- a/src/Compiler/Checking/ConstraintSolver.fs +++ b/src/Compiler/Checking/ConstraintSolver.fs @@ -2463,6 +2463,13 @@ and SolveTypeIsUnmanaged (csenv: ConstraintSolverEnv) ndeep m2 trace ty = return! destStructAnonRecdTy g ty |> IterateD (SolveTypeIsUnmanaged csenv (ndeep + 1) m2 trace) else if isStructTupleTy g ty then return! destStructTupleTy g ty |> IterateD (SolveTypeIsUnmanaged csenv (ndeep + 1) m2 trace) + else if isStructUnionTy g ty then + let tcref = tryTcrefOfAppTy g ty |> ValueOption.get + let tinst = mkInstForAppTy g ty + return! + tcref.UnionCasesAsRefList + |> List.collect (actualTysOfUnionCaseFields tinst) + |> IterateD (SolveTypeIsUnmanaged csenv (ndeep + 1) m2 trace) else if isUnmanagedTy g ty then return! CompleteD diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index 2cdbf821064..8ced9d1b4eb 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -829,6 +829,8 @@ let isStructAnonRecdTy g ty = ty |> stripTyEqns g |> (function TType_anon (anonI let isUnionTy g ty = ty |> stripTyEqns g |> (function TType_app(tcref, _, _) -> tcref.IsUnionTycon | _ -> false) +let isStructUnionTy g ty = ty |> stripTyEqns g |> (function TType_app(tcref, _, _) -> tcref.IsUnionTycon && tcref.Deref.entity_flags.IsStructRecordOrUnionType | _ -> false) + let isReprHiddenTy g ty = ty |> stripTyEqns g |> (function TType_app(tcref, _, _) -> tcref.IsHiddenReprTycon | _ -> false) let isFSharpObjModelTy g ty = ty |> stripTyEqns g |> (function TType_app(tcref, _, _) -> tcref.IsFSharpObjectModelTycon | _ -> false) @@ -1969,6 +1971,9 @@ let isForallFunctionTy g ty = // - Any pointer-type. // - Any generic user-defined struct-type that can be statically determined to be 'unmanaged' at construction. let rec isUnmanagedTy g ty = + let isUnmanagedRecordField tinst rf = + isUnmanagedTy g (actualTyOfRecdField tinst rf) + let ty = stripTyEqnsAndMeasureEqns g ty match tryTcrefOfAppTy g ty with | ValueSome tcref -> @@ -1988,9 +1993,14 @@ let rec isUnmanagedTy g ty = let tycon = tcref.Deref if tycon.IsEnumTycon then true + elif isStructUnionTy g ty then + let tinst = mkInstForAppTy g ty + tcref.UnionCasesAsRefList + |> List.forall (fun c -> c |> actualTysOfUnionCaseFields tinst |> List.forall (isUnmanagedTy g)) elif tycon.IsStructOrEnumTycon then let tinst = mkInstForAppTy g ty - tycon.AllInstanceFieldsAsList |> List.forall (fun r -> isUnmanagedTy g (actualTyOfRecdField tinst r)) + tycon.AllInstanceFieldsAsList + |> List.forall (isUnmanagedRecordField tinst) else false | ValueNone -> if isStructTupleTy g ty then diff --git a/src/Compiler/TypedTree/TypedTreeOps.fsi b/src/Compiler/TypedTree/TypedTreeOps.fsi index d01868d7379..f161935ca46 100755 --- a/src/Compiler/TypedTree/TypedTreeOps.fsi +++ b/src/Compiler/TypedTree/TypedTreeOps.fsi @@ -655,6 +655,8 @@ val isAnonRecdTy: TcGlobals -> TType -> bool val isUnionTy: TcGlobals -> TType -> bool +val isStructUnionTy: TcGlobals -> TType -> bool + val isReprHiddenTy: TcGlobals -> TType -> bool val isFSharpObjModelTy: TcGlobals -> TType -> bool diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs index 5fefbb9b9d4..ae6d80db6a9 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs @@ -8,6 +8,45 @@ open FSharp.Test module Unmanaged = + [] + let ``voption considered unmanaged when inner type is unmanaged`` () = + Fsx """ +let test (x: 'T when 'T : unmanaged) = () +test (ValueSome 15) +test (ValueSome (ValueSome 42)) +test (ValueSome (struct {|Field = 42|})) + """ + |> typecheck + |> shouldSucceed + + [] + let ``voption not considered unmanaged when inner type is reference type`` () = + Fsx """ +let test (x: 'T when 'T : unmanaged) = () +test (ValueSome "xxx") +test (ValueSome (ValueSome "xxx")) +test (ValueSome (struct {|Field = Some 42|})) + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + Error 1, Line 3, Col 17, Line 3, Col 22, "A generic construct requires that the type 'string' is an unmanaged type" + Error 1, Line 4, Col 28, Line 4, Col 33, "A generic construct requires that the type 'string' is an unmanaged type" + Error 1, Line 5, Col 35, Line 5, Col 42, "A generic construct requires that the type ''a option' is an unmanaged type" ] + + [] + let ``Option not considered unmanaged`` () = + Fsx """ +let test (x: 'T when 'T : unmanaged) = () +test (None) +test (Some 42 ) + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + Error 1, Line 3, Col 7, Line 3, Col 11, "A generic construct requires that the type ''a option' is an unmanaged type" + Error 1, Line 4, Col 7, Line 4, Col 14, "A generic construct requires that the type ''a option' is an unmanaged type" ] + [] let ``User-defined struct types considered unmanaged when all members are unmanaged`` () = Fsx """ @@ -152,8 +191,8 @@ test(MultiCaseUnion.A) test(MultiCaseUnion.B) test(C 1) test(CC 1) -test(Ok 1) -test(Error 2) +test(Result.Ok 1) +test(Result.Error 2) test(ResultC.OkC 1) test(ResultC.ErrorC 1) test(DuUnix 132456) @@ -184,6 +223,27 @@ let _ = resultCreatingFunction A |> shouldSucceed |> ignore + [] + let ``Typechecker can handle recursive unions passed in and will not stack overflow`` () = + Fsx """ +type RefTree = + | Tip + | Node of i:int * left:RefTree * right:RefTree + +[] +type StructTree = + | Tip + | Node of i:int * left:StructTree * right:StructTree + +[] +type ComboTree = + | Tip + | Node of i:int * left:RefTree * right:RefTree + """ + |> typecheck + |> shouldFail + |> withDiagnostics [Error 954, Line 7, Col 6, Line 7, Col 16, "This type definition involves an immediate cyclic reference through a struct field or inheritance relation"] + [] let ``Generic user-defined type with non-unmanaged types is NOT considered unmanaged`` () = Fsx """ @@ -200,6 +260,9 @@ type S<'T> = { x: W<'T> } type X<'T> = val Z : 'T new (x) = { Z = x } + +[] +type MyDu<'T1,'T2> = DuA of first:'T1 | DuB of second:'T2 [] type A<'T, 'U> = @@ -211,18 +274,18 @@ test (A()) let foo<'T> () = test (A<'T, obj>()) let _ = Test() let _ = Test() -let _ = Test>() +let _ = Test>>() """ |> typecheck |> shouldFail |> withDiagnostics [ - (Error 1, Line 20, Col 6, Line 20, Col 33, "A generic construct requires that the type 'S' is an unmanaged type") - (Error 193, Line 21, Col 6, Line 21, Col 14, "A generic construct requires that the type 'X<'a>' is an unmanaged type") - (Error 193, Line 22, Col 7, Line 22, Col 20, "A generic construct requires that the type 'A' is an unmanaged type") - (Error 193, Line 23, Col 24, Line 23, Col 36, "A generic construct requires that the type 'A<'T,obj>' is an unmanaged type") - (Error 1, Line 24, Col 9, Line 24, Col 18, "A generic construct requires that the type 'obj' is an unmanaged type") - (Error 1, Line 25, Col 9, Line 25, Col 28, "A generic construct requires that the type 'NonStructRecd' is an unmanaged type") - (Error 1, Line 26, Col 9, Line 26, Col 34, "A generic construct requires that the type 'NonStructRecdC' is an unmanaged type")] + Error 1, Line 23, Col 6, Line 23, Col 33, "A generic construct requires that the type 'S' is an unmanaged type" + Error 193, Line 24, Col 6, Line 24, Col 14, "A generic construct requires that the type 'X<'a>' is an unmanaged type" + Error 193, Line 25, Col 7, Line 25, Col 20, "A generic construct requires that the type 'A' is an unmanaged type" + Error 193, Line 26, Col 24, Line 26, Col 36, "A generic construct requires that the type 'A<'T,obj>' is an unmanaged type" + Error 1, Line 27, Col 9, Line 27, Col 18, "A generic construct requires that the type 'obj' is an unmanaged type" + Error 1, Line 28, Col 9, Line 28, Col 28, "A generic construct requires that the type 'NonStructRecd' is an unmanaged type" + Error 1, Line 29, Col 9, Line 29, Col 49, "A generic construct requires that the type 'string' is an unmanaged type" ] [] let ``Disallow both 'unmanaged' and 'not struct' constraints`` () = @@ -298,4 +361,32 @@ let y = new CsharpStruct(struct(1,"this is string")) app |> compile |> shouldFail - |> withDiagnostics [(Error 1, Line 3, Col 13, Line 3, Col 45, "A generic construct requires that the type 'string' is an unmanaged type")] \ No newline at end of file + |> withDiagnostics [(Error 1, Line 3, Col 13, Line 3, Col 45, "A generic construct requires that the type 'string' is an unmanaged type")] + + [] + let ``F# can consume C#-defined unmanaged constraint and call method with modreq`` () = + let csLib = + CSharp """ +namespace CsLib +{ + public record struct CsharpStruct(T item) where T:unmanaged + { + public static string Hi() where TOther:unmanaged + { + return typeof(TOther).Name; + }}}""" + |> withCSharpLanguageVersion CSharpLanguageVersion.Preview + |> withName "csLib" + + let app = FSharp """module MyFsharpApp +open CsLib +[] +type MultiCaseUnion = A | B of i:int +let _ = new CsharpStruct(B 42) +printf "%s" (CsharpStruct.Hi()) + """ |> withReferences [csLib] + + app + |> asExe + |> compileAndRun + |> verifyOutput "MultiCaseUnion" \ No newline at end of file From 683e5c7c5824c3ce0fad5b8e97e625e5cbdf4dd1 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Fri, 21 Jul 2023 16:23:18 +0200 Subject: [PATCH 08/15] fix tests --- .../Conformance/Constraints/Unmanaged.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs index ae6d80db6a9..a1309d0c900 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs @@ -209,9 +209,9 @@ let _ = Test, Single>>() let _ = Test>() // test that constraint is being propagated -let resultCreatingFunction (x) = +let resultCreatingFunction (x:'a) = try - let capturedVal = Ok x + let capturedVal = Result<'a,int>.Ok x test capturedVal capturedVal with _ -> Error 15 From 03f0b5ad3fdb772e6ece6300a23113075c302e07 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Fri, 21 Jul 2023 20:58:25 +0200 Subject: [PATCH 09/15] could it be failing because of typar constraint order? --- src/Compiler/Checking/import.fs | 18 +++++++++--------- .../Conformance/Constraints/Unmanaged.fs | 5 ++++- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Compiler/Checking/import.fs b/src/Compiler/Checking/import.fs index d40bcc60199..23edca4fafb 100644 --- a/src/Compiler/Checking/import.fs +++ b/src/Compiler/Checking/import.fs @@ -459,23 +459,23 @@ let ImportILGenericParameters amap m scoref tinst (gps: ILGenericParameterDefs) match gps with | [] -> [] | _ -> - let amap = amap() + let amap : ImportMap = amap() let tps = gps |> List.map (fun gp -> Construct.NewRigidTypar gp.Name m) let tptys = tps |> List.map mkTyparTy let importInst = tinst@tptys (tps, gps) ||> List.iter2 (fun tp gp -> let constraints = - [ for ilTy in gp.Constraints do - TyparConstraint.CoercesTo(ImportILType amap m importInst (rescopeILType scoref ilTy), m) - if gp.HasReferenceTypeConstraint then - TyparConstraint.IsReferenceType(m) - if gp.HasNotNullableValueTypeConstraint then - TyparConstraint.IsNonNullableStruct(m) + [ if gp.CustomAttrs |> TryFindILAttribute amap.g.attrib_IsUnmanagedAttribute then + TyparConstraint.IsUnmanaged(m) if gp.HasDefaultConstructorConstraint then TyparConstraint.RequiresDefaultConstructor(m) - if gp.CustomAttrs |> TryFindILAttribute amap.g.attrib_IsUnmanagedAttribute then - TyparConstraint.IsUnmanaged(m) ] + if gp.HasNotNullableValueTypeConstraint then + TyparConstraint.IsNonNullableStruct(m) + if gp.HasReferenceTypeConstraint then + TyparConstraint.IsReferenceType(m) + for ilTy in gp.Constraints do + TyparConstraint.CoercesTo(ImportILType amap m importInst (rescopeILType scoref ilTy), m) ] tp.SetConstraints constraints) tps diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs index a1309d0c900..468e985b5af 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs @@ -389,4 +389,7 @@ printf "%s" (CsharpStruct.Hi()) app |> asExe |> compileAndRun - |> verifyOutput "MultiCaseUnion" \ No newline at end of file + |> verifyOutput "MultiCaseUnion" + + + // TODO tests - interop other direction \ No newline at end of file From ed23d2bbc6d829e7c65caa96eefa035326fe794c Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Mon, 24 Jul 2023 14:19:05 +0200 Subject: [PATCH 10/15] Fixing net472 tests with csharp --- .../Conformance/Constraints/Unmanaged.fs | 40 ++++++++++++++++++- tests/FSharp.Test.Utilities/CompilerAssert.fs | 3 ++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs index 468e985b5af..165f62bb5e0 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs @@ -8,6 +8,43 @@ open FSharp.Test module Unmanaged = + [] + let ``Struct with private field can be unmanaged`` () = + Fsx """ +[] +type Test<'T when 'T: unmanaged> = + val element: 'T +type DoubleType<'T> = + struct + val x: float + val private y: 'T + end +let _ = Test>() +let _ = Test>>() + """ + |> typecheck + |> shouldSucceed + + [] + let ``Struct with private managed field cannot be unmanaged`` () = + Fsx """ +[] +type Test<'T when 'T: unmanaged> = + val element: 'T +type DoubleType<'T> = + struct + val x: float + val private y: 'T + end +let _ = Test>() +let _ = Test>>() + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + Error 1, Line 10, Col 9, Line 10, Col 33, "A generic construct requires that the type 'DoubleType' is an unmanaged type" + Error 1, Line 11, Col 9, Line 11, Col 49, "A generic construct requires that the type 'DoubleType>' is an unmanaged type" ] + [] let ``voption considered unmanaged when inner type is unmanaged`` () = Fsx """ @@ -388,7 +425,8 @@ printf "%s" (CsharpStruct.Hi()) app |> asExe - |> compileAndRun + |> compile + |> run |> verifyOutput "MultiCaseUnion" diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index f9af77086df..ac593abf497 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -290,6 +290,9 @@ module rec CompilerAssertHelpers = AppDomain.CurrentDomain.add_AssemblyResolve(ResolveEventHandler(fun _ args -> deps |> Array.tryFind (fun (x: string) -> Path.GetFileNameWithoutExtension x = args.Name) + |> Option.orElseWith (fun () -> + deps + |> Array.tryFind (fun (x: string) -> args.Name.StartsWith(Path.GetFileNameWithoutExtension(x) + ", Version"))) |> Option.bind (fun x -> if FileSystem.FileExistsShim x then Some x else None) |> Option.map Assembly.LoadFile |> Option.defaultValue null)) From 72465863f7105eb3720bbb17dc52247c24fe3fc6 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Mon, 24 Jul 2023 16:44:00 +0200 Subject: [PATCH 11/15] codegen to make code usable from C# --- src/Compiler/CodeGen/IlxGen.fs | 11 +- src/Compiler/TypedTree/TcGlobals.fs | 3 + .../Conformance/Constraints/Unmanaged.fs | 136 +++++++++++++++--- 3 files changed, 128 insertions(+), 22 deletions(-) diff --git a/src/Compiler/CodeGen/IlxGen.fs b/src/Compiler/CodeGen/IlxGen.fs index 5876712e49e..e70137e3a53 100644 --- a/src/Compiler/CodeGen/IlxGen.fs +++ b/src/Compiler/CodeGen/IlxGen.fs @@ -5614,14 +5614,21 @@ and GenGenericParam cenv eenv (tp: Typar) = let tpAttrs = mkILCustomAttrs (attributeList) + let modreqValueType () = + ILType.Modified(true, g.iltyp_UnmanagedType.TypeRef, g.iltyp_ValueType) + { Name = tpName - Constraints = subTypeConstraints + Constraints = + if unmanagedConstraint then + (modreqValueType () :: subTypeConstraints) + else + subTypeConstraints Variance = NonVariant CustomAttrsStored = storeILCustomAttrs tpAttrs MetadataIndex = NoMetadataIdx HasReferenceTypeConstraint = refTypeConstraint - HasNotNullableValueTypeConstraint = notNullableValueTypeConstraint + HasNotNullableValueTypeConstraint = notNullableValueTypeConstraint || unmanagedConstraint HasDefaultConstructorConstraint = defaultConstructorConstraint } diff --git a/src/Compiler/TypedTree/TcGlobals.fs b/src/Compiler/TypedTree/TcGlobals.fs index 6715d0f5d0d..7d93e3385a7 100755 --- a/src/Compiler/TypedTree/TcGlobals.fs +++ b/src/Compiler/TypedTree/TcGlobals.fs @@ -165,6 +165,8 @@ let tname_CompilerGeneratedAttribute = "System.Runtime.CompilerServices.Compiler [] let tname_ReferenceAssemblyAttribute = "System.Runtime.CompilerServices.ReferenceAssemblyAttribute" [] +let tname_UnmanagedType = "System.Runtime.InteropServices.UnmanagedType" +[] let tname_DebuggableAttribute = "System.Diagnostics.DebuggableAttribute" [] let tname_AsyncCallback = "System.AsyncCallback" @@ -1415,6 +1417,7 @@ type TcGlobals( member val iltyp_RuntimeMethodHandle = findSysILTypeRef tname_RuntimeMethodHandle |> mkILNonGenericValueTy member val iltyp_RuntimeTypeHandle = findSysILTypeRef tname_RuntimeTypeHandle |> mkILNonGenericValueTy member val iltyp_ReferenceAssemblyAttributeOpt = tryFindSysILTypeRef tname_ReferenceAssemblyAttribute |> Option.map mkILNonGenericBoxedTy + member val iltyp_UnmanagedType = findSysILTypeRef tname_UnmanagedType |> mkILNonGenericValueTy member val attrib_AttributeUsageAttribute = findSysAttrib "System.AttributeUsageAttribute" member val attrib_ParamArrayAttribute = findSysAttrib "System.ParamArrayAttribute" member val attrib_IDispatchConstantAttribute = tryFindSysAttrib "System.Runtime.CompilerServices.IDispatchConstantAttribute" diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs index 165f62bb5e0..c2ba64824d9 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs @@ -333,8 +333,8 @@ let _ = Test>>() (Error 43, Line 1, Col 34, Line 1, Col 48, "The constraints 'unmanaged' and 'not struct' are inconsistent")] [] - let ``IsUnmanagedAttribute Attribute is emitted and generated for unmanaged constraint on type`` () = - Fsx "[] type Test<'T when 'T: unmanaged and 'T: struct> = struct end" + let ``Multi constraint IL test together with struct and interface constraints`` () = + Fsx "[] type Test<'T when 'T: unmanaged and 'T: struct and 'T:>System.IComparable> = struct end" |> compile |> shouldSucceed |> verifyIL [""" @@ -342,7 +342,7 @@ let _ = Test>>() extends [runtime]System.Object { .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) - .class sequential ansi serializable sealed nested public beforefieldinit Test`1 + .class sequential ansi serializable sealed nested public beforefieldinit Test`1 extends [runtime]System.ValueType { .pack 0 @@ -355,7 +355,7 @@ let _ = Test>>() .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) } - }""";""" + } """;""" .class private auto ansi beforefieldinit System.Runtime.CompilerServices.IsUnmanagedAttribute extends [runtime]System.Attribute"""] @@ -365,21 +365,19 @@ let _ = Test>>() |> compile |> shouldSucceed |> verifyIL [""" - .method public static valuetype [runtime]System.ValueTuple`2 - testMyFunction(!!TUnmanaged x) cil managed - { - .param type TUnmanaged - .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) - - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: ldc.i4.1 - IL_0002: newobj instance void valuetype [runtime]System.ValueTuple`2::.ctor(!0, - !1) - IL_0007: ret - } """;""" -.class private auto ansi beforefieldinit System.Runtime.CompilerServices.IsUnmanagedAttribute - extends [runtime]System.Attribute"""] + .method public static valuetype [runtime]System.ValueTuple`2 + testMyFunction(!!TUnmanaged x) cil managed + { + .param type TUnmanaged + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 + IL_0002: newobj instance void valuetype [runtime]System.ValueTuple`2::.ctor(!0, + !1) + IL_0007: ret + }"""] @@ -429,5 +427,103 @@ printf "%s" (CsharpStruct.Hi()) |> run |> verifyOutput "MultiCaseUnion" + [] + let ``F# generates modreq for C# to consume`` () = + Fsx "let testMyFunction (x: 'TUnmanaged when 'TUnmanaged : unmanaged) = ()" + |> compile + |> shouldSucceed + |> verifyIL [""" + .method public static void testMyFunction(!!TUnmanaged x) cil managed + { + .param type TUnmanaged + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ret + } """] + + + [] + let ``C# can consume F#-defined struct with unmanaged constraint - valid`` () = + let fsharpLib = + Fs """namespace MyFsLib +[] +type FsharpStructWrapper<'TInner when 'TInner: unmanaged> = + val Item : 'TInner + with static member Hi() = typeof<'TInner>.Name""" + |> asLibrary + |> withName "fsLib" - // TODO tests - interop other direction \ No newline at end of file + let app = + CSharp """ + using System; + using MyFsLib; + public record struct CSharpStruct(int first, long second); + public class C { + public static void Main() { + var text = FsharpStructWrapper.Hi(); + Console.Write(text); + } + } + """ + |> withReferences [fsharpLib] + |> withCSharpLanguageVersion CSharpLanguageVersion.Preview + |> asExe + |> withName "myCsharpApp" + + app + |> compile + |> run + |> verifyOutput "CSharpStruct" + + [] + let ``C# can consume F#-defined struct with unmanaged constraint - and report error when invalid`` () = + let fsharpLib = + Fs """namespace MyFsLib +[] +type FsharpStructWrapper<'T when 'T: unmanaged> = + val Item : 'T + with static member Hi() = typeof<'T>.Name + +[] +type MultiDu = A | B of s:string + +module FsharpFunc = + let test (x: 'T when 'T : unmanaged) = () + +module FsharpPreparedData = + let structTuple = struct(1,2,"42") + let structDuExample1 = A + let structDuExample2 = B "42" + """ + |> asLibrary + |> withName "fsLib" + + let app = + CSharp """ + using System; + using MyFsLib; + public record struct CSharpStructWithString(int first, string second); + public class C { + public static void Main() { + var text = FsharpStructWrapper.Hi(); + FsharpFunc.test(FsharpPreparedData.structTuple); + FsharpFunc.test(FsharpPreparedData.structDuExample1); + FsharpFunc.test(FsharpPreparedData.structDuExample2); + Console.WriteLine(text); + } + } + """ + |> withReferences [fsharpLib] + |> withCSharpLanguageVersion CSharpLanguageVersion.Preview + |> asExe + |> withName "myCsharpApp" + + app + |> compile + |> shouldFail + |> withDiagnostics [ + Error 8377, Line 6, Col 44, Line 6, Col 66, "The type 'CSharpStructWithString' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'FsharpStructWrapper'" + Error 8377, Line 7, Col 24, Line 7, Col 28, "The type '(int, int, string)' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'FsharpFunc.test(T)'" + Error 8377, Line 8, Col 24, Line 8, Col 28, "The type 'MultiDu' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'FsharpFunc.test(T)'" + Error 8377, Line 9, Col 24, Line 9, Col 28, "The type 'MultiDu' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'FsharpFunc.test(T)'" ] \ No newline at end of file From 1dd60eb86e758e67c04501776be9b136527f8c60 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Mon, 24 Jul 2023 17:22:42 +0200 Subject: [PATCH 12/15] wrap in languagefeature --- src/Compiler/CodeGen/IlxGen.fs | 9 ++++---- src/Compiler/FSComp.txt | 3 ++- src/Compiler/Facilities/LanguageFeatures.fs | 3 +++ src/Compiler/Facilities/LanguageFeatures.fsi | 1 + src/Compiler/xlf/FSComp.txt.cs.xlf | 7 +++++- src/Compiler/xlf/FSComp.txt.de.xlf | 7 +++++- src/Compiler/xlf/FSComp.txt.es.xlf | 7 +++++- src/Compiler/xlf/FSComp.txt.fr.xlf | 7 +++++- src/Compiler/xlf/FSComp.txt.it.xlf | 7 +++++- src/Compiler/xlf/FSComp.txt.ja.xlf | 7 +++++- src/Compiler/xlf/FSComp.txt.ko.xlf | 7 +++++- src/Compiler/xlf/FSComp.txt.pl.xlf | 7 +++++- src/Compiler/xlf/FSComp.txt.pt-BR.xlf | 7 +++++- src/Compiler/xlf/FSComp.txt.ru.xlf | 7 +++++- src/Compiler/xlf/FSComp.txt.tr.xlf | 7 +++++- src/Compiler/xlf/FSComp.txt.zh-Hans.xlf | 7 +++++- src/Compiler/xlf/FSComp.txt.zh-Hant.xlf | 7 +++++- .../Conformance/Constraints/Unmanaged.fs | 23 ++++++++++++++++++- 18 files changed, 111 insertions(+), 19 deletions(-) diff --git a/src/Compiler/CodeGen/IlxGen.fs b/src/Compiler/CodeGen/IlxGen.fs index e70137e3a53..8abc6369f59 100644 --- a/src/Compiler/CodeGen/IlxGen.fs +++ b/src/Compiler/CodeGen/IlxGen.fs @@ -5571,7 +5571,8 @@ and GenGenericParam cenv eenv (tp: Typar) = | TyparConstraint.RequiresDefaultConstructor _ -> true | _ -> false) - let unmanagedConstraint = + let emitUnmanagedInIlOutput = + cenv.g.langVersion.SupportsFeature(LanguageFeature.UnmanagedConstraintCsharpInterop) && tp.Constraints |> List.exists (function | TyparConstraint.IsUnmanaged _ -> true @@ -5607,7 +5608,7 @@ and GenGenericParam cenv eenv (tp: Typar) = let attributeList = let defined = GenAttrs cenv eenv tp.Attribs - if unmanagedConstraint then + if emitUnmanagedInIlOutput then (GetIsUnmanagedAttribute g) :: defined else defined @@ -5620,7 +5621,7 @@ and GenGenericParam cenv eenv (tp: Typar) = { Name = tpName Constraints = - if unmanagedConstraint then + if emitUnmanagedInIlOutput then (modreqValueType () :: subTypeConstraints) else subTypeConstraints @@ -5628,7 +5629,7 @@ and GenGenericParam cenv eenv (tp: Typar) = CustomAttrsStored = storeILCustomAttrs tpAttrs MetadataIndex = NoMetadataIdx HasReferenceTypeConstraint = refTypeConstraint - HasNotNullableValueTypeConstraint = notNullableValueTypeConstraint || unmanagedConstraint + HasNotNullableValueTypeConstraint = notNullableValueTypeConstraint || emitUnmanagedInIlOutput HasDefaultConstructorConstraint = defaultConstructorConstraint } diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index 5ae2bdf04b3..c8e98b2d8b1 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1709,4 +1709,5 @@ featureAccessorFunctionShorthand,"underscore dot shorthand for accessor only fun 3569,chkNotTailRecursive,"The member or function '%s' has the 'TailCallAttribute' attribute, but is not being used in a tail recursive way." 3570,tcStaticBindingInExtrinsicAugmentation,"Static bindings cannot be added to extrinsic augmentations. Consider using a 'static member' instead." 3571,pickleFsharpCoreBackwardsCompatible,"Newly added pickle state cannot be used in FSharp.Core, since it must be working in older compilers+tooling as well. The time window is at least 3 years after feature introduction. Violation: %s . Context: \n %s " -3577,tcOverrideUsesMultipleArgumentsInsteadOfTuple,"This override takes a tuple instead of multiple arguments. Try to add an additional layer of parentheses at the method definition (e.g. 'member _.Foo((x, y))'), or remove parentheses at the abstract method declaration (e.g. 'abstract member Foo: 'a * 'b -> 'c')." \ No newline at end of file +3577,tcOverrideUsesMultipleArgumentsInsteadOfTuple,"This override takes a tuple instead of multiple arguments. Try to add an additional layer of parentheses at the method definition (e.g. 'member _.Foo((x, y))'), or remove parentheses at the abstract method declaration (e.g. 'abstract member Foo: 'a * 'b -> 'c')." +featureUnmanagedConstraintCsharpInterop,"Interop betwee C#'s and F#'s unmanaged generic constraint" \ No newline at end of file diff --git a/src/Compiler/Facilities/LanguageFeatures.fs b/src/Compiler/Facilities/LanguageFeatures.fs index fb055f12c9e..9464f83dcae 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fs +++ b/src/Compiler/Facilities/LanguageFeatures.fs @@ -74,6 +74,7 @@ type LanguageFeature = | DiagnosticForObjInference | StaticLetInRecordsDusEmptyTypes | WarningWhenTailRecAttributeButNonTailRecUsage + | UnmanagedConstraintCsharpInterop /// LanguageVersion management type LanguageVersion(versionText) = @@ -171,6 +172,7 @@ type LanguageVersion(versionText) = LanguageFeature.WarningWhenTailRecAttributeButNonTailRecUsage, previewVersion LanguageFeature.StaticLetInRecordsDusEmptyTypes, previewVersion LanguageFeature.StrictIndentation, previewVersion + LanguageFeature.UnmanagedConstraintCsharpInterop, previewVersion ] static let defaultLanguageVersion = LanguageVersion("default") @@ -300,6 +302,7 @@ type LanguageVersion(versionText) = | LanguageFeature.StaticLetInRecordsDusEmptyTypes -> FSComp.SR.featureStaticLetInRecordsDusEmptyTypes () | LanguageFeature.StrictIndentation -> FSComp.SR.featureStrictIndentation () | LanguageFeature.WarningWhenTailRecAttributeButNonTailRecUsage -> FSComp.SR.featureChkNotTailRecursive () + | LanguageFeature.UnmanagedConstraintCsharpInterop -> FSComp.SR.featureUnmanagedConstraintCsharpInterop () /// Get a version string associated with the given feature. static member GetFeatureVersionString feature = diff --git a/src/Compiler/Facilities/LanguageFeatures.fsi b/src/Compiler/Facilities/LanguageFeatures.fsi index ee05ed4bcea..2ff12cd3711 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fsi +++ b/src/Compiler/Facilities/LanguageFeatures.fsi @@ -64,6 +64,7 @@ type LanguageFeature = | DiagnosticForObjInference | StaticLetInRecordsDusEmptyTypes | WarningWhenTailRecAttributeButNonTailRecUsage + | UnmanagedConstraintCsharpInterop /// LanguageVersion management type LanguageVersion = diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index 24d29cc987f..7c2fe454dfd 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -166,7 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent - + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n Všechny větve výrazu if musí vracet hodnoty implicitně převoditelné na typ první větve, což je řazená kolekce členů o délce {0} typu\n {1} \nTato větev vrací řazenou kolekci členů o délce {2} typu\n {3} \n @@ -452,6 +452,11 @@ Podpora try-with ve výrazech pořadí + + Interop betwee C#'s and F#'s unmanaged generic constraint + Interop betwee C#'s and F#'s unmanaged generic constraint + + Raises warnings when an copy-and-update record expression changes all fields of a record. Vyvolá upozornění, když výraz záznamu kopírování a aktualizace změní všechna pole záznamu. diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index aa241450839..846c30e3ce7 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -166,7 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent - + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n Alle Verzweigungen eines „If-Ausdrucks“ müssen Werte zurückgeben, die implizit in den Typ der ersten Verzweigung konvertierbar sind. In diesem Fall handelt es sich dabei um ein Tupel der Länge {0} des Typs.\n {1} \nDiese Verzweigung gibt ein Tupel der Länge {2} des Typs\n {3} \nzurück. @@ -452,6 +452,11 @@ Unterstützung für "try-with" in Sequenzausdrücken + + Interop betwee C#'s and F#'s unmanaged generic constraint + Interop betwee C#'s and F#'s unmanaged generic constraint + + Raises warnings when an copy-and-update record expression changes all fields of a record. Löst Warnungen aus, wenn ein Ausdruck zum Kopieren und Aktualisieren von Datensätzen alle Felder eines Datensatzes ändert. diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index e103d9f1e53..55bb1f4c3c3 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -166,7 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent - + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n Todas las ramas de una expresión 'if' deben devolver valores implícitamente convertibles al tipo de la primera rama, que aquí es una tupla de longitud {0} de tipo\n {1} \nEsta rama devuelve una tupla de longitud {2} de tipo\n {3} \n @@ -452,6 +452,11 @@ Compatibilidad con try-with en expresiones secuenciales + + Interop betwee C#'s and F#'s unmanaged generic constraint + Interop betwee C#'s and F#'s unmanaged generic constraint + + Raises warnings when an copy-and-update record expression changes all fields of a record. Emite advertencias cuando una expresión de copiar y actualizar registros cambia todos los campos de un registro. diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index a1eca4dd315..fd919f59596 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -166,7 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent - + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n Toutes les branches d’une expression « if » doivent retourner des valeurs implicitement convertibles au type de la première branche, qui est ici un tuple de longueur {0} de type\n {1} \nCette branche renvoie un tuple de longueur {2} de type\n {3} \n @@ -452,6 +452,11 @@ Prise en charge de try-with dans les expressions de séquence + + Interop betwee C#'s and F#'s unmanaged generic constraint + Interop betwee C#'s and F#'s unmanaged generic constraint + + Raises warnings when an copy-and-update record expression changes all fields of a record. Génère des avertissements lorsqu'une expression d'enregistrement de copie et de mise à jour modifie tous les champs d'un enregistrement. diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index 36fc84e2f81..0f91163729d 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -166,7 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent - + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n Tutti i rami di un'espressione 'if' devono restituire valori convertibili in modo implicito nel tipo del primo ramo, che è una tupla di lunghezza {0} di tipo\n {1} \nQuesto ramo restituisce una tupla di lunghezza {2} di tipo\n {3} \n @@ -452,6 +452,11 @@ Supporto per try-with nelle espressioni di sequenza + + Interop betwee C#'s and F#'s unmanaged generic constraint + Interop betwee C#'s and F#'s unmanaged generic constraint + + Raises warnings when an copy-and-update record expression changes all fields of a record. Genera avvisi quando un'espressione di record di copia e aggiornamento modifica tutti i campi di un record. diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index 4c543afed01..edc61f725b8 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -166,7 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent - + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n 'if' 式のすべての分岐は、最初の分岐の型に暗黙的に変換できる値を返す必要があります。これは、型の長さ {0} のタプルです\n {1} \nこの分岐は、型の長さ {2} のタプルを返します\n {3} \n @@ -452,6 +452,11 @@ シーケンス式内の try-with のサポート + + Interop betwee C#'s and F#'s unmanaged generic constraint + Interop betwee C#'s and F#'s unmanaged generic constraint + + Raises warnings when an copy-and-update record expression changes all fields of a record. copy-and-update レコード式によってレコードのすべてのフィールドが変更されたときに警告を表示します。 diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index 1ea44ac8968..b68a75b349a 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -166,7 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent - + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n 'if' 식의 모든 분기는 첫 번째 분기의 유형으로 암시적으로 변환 가능한 값을 반환해야 합니다. 여기서는 형식이 \n {1}이고 길이가 {0}인 튜플입니다. \n이 분기는 형식이 \n {3}이고 길이가 {2}인 튜플을 반환합니다. \n @@ -452,6 +452,11 @@ 시퀀스 식에서 try-with 지원 + + Interop betwee C#'s and F#'s unmanaged generic constraint + Interop betwee C#'s and F#'s unmanaged generic constraint + + Raises warnings when an copy-and-update record expression changes all fields of a record. 레코드 복사 및 업데이트 식이 레코드의 모든 필드를 변경할 때 경고를 발생합니다. diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index 758d25ded68..01eb905d79b 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -166,7 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent - + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n Wszystkie gałęzie wyrażenia „if” muszą zwracać wartości niejawnie konwertowalne na typ pierwszej gałęzi, która tutaj jest krotką o długości {0} typu\n {1} \nTa gałąź zwraca krotkę o długości {2} typu\n {3} \n @@ -452,6 +452,11 @@ Obsługa instrukcji try-with w wyrażeniach sekwencji + + Interop betwee C#'s and F#'s unmanaged generic constraint + Interop betwee C#'s and F#'s unmanaged generic constraint + + Raises warnings when an copy-and-update record expression changes all fields of a record. Zgłasza ostrzeżenia, gdy wyrażenie rekordu kopiowania i aktualizacji zmieni wszystkie pola rekordu. diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index d2b24da5a4b..3b8289662a3 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -166,7 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent - + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n Todas as ramificações de uma expressão 'if' devem retornar valores implicitamente conversíveis ao tipo da primeira ramificação, que aqui é uma tupla de comprimento {0} do tipo\n {1} \nEsta ramificação retorna uma tupla de comprimento {2} do tipo\n {3} \n @@ -452,6 +452,11 @@ Suporte para try-with em expressões de sequência + + Interop betwee C#'s and F#'s unmanaged generic constraint + Interop betwee C#'s and F#'s unmanaged generic constraint + + Raises warnings when an copy-and-update record expression changes all fields of a record. Gera avisos quando uma expressão de registro de cópia e atualização altera todos os campos de um registro. diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index 3c23d07c9ae..3afa4fb909f 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -166,7 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent - + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n Все ветви выражения "if" должны возвращать значения, поддерживающие неявное преобразование в тип первой ветви, которым здесь является кортеж длиной {0} типа\n {1} \nЭта ветвь возвращает кортеж длиной {2} типа\n {3} \n @@ -452,6 +452,11 @@ Поддержка try-with в выражениях последовательности + + Interop betwee C#'s and F#'s unmanaged generic constraint + Interop betwee C#'s and F#'s unmanaged generic constraint + + Raises warnings when an copy-and-update record expression changes all fields of a record. Создает предупреждения, когда выражение копирования и обновления записи изменяет все поля записи. diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index 358286fcb92..ebb04e90e9a 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -166,7 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent - + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n Bir 'if' ifadesinin tüm dalları, örtük olarak ilk dalın türüne dönüştürülebilir değerler döndürmelidir. Burada ilk dal {0} uzunluğunda türü\n {1} olan bir demet \nBu dal {2} uzunluğunda türü\n {3} \nolan bir demet döndürüyor @@ -452,6 +452,11 @@ Dizi ifadelerinde try-with desteği + + Interop betwee C#'s and F#'s unmanaged generic constraint + Interop betwee C#'s and F#'s unmanaged generic constraint + + Raises warnings when an copy-and-update record expression changes all fields of a record. Bir kopyalama ve güncelleştirme kayıt ifadesi bir kaydın tüm alanlarını değiştirdiğinde uyarı oluşturur. diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index 876b89155e2..4c8e8fde67d 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -166,7 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent - + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n “if” 表达式的所有分支必须返回可隐式转换为第一个分支类型的值,这是一个长度为 {0} 的类型的元组\n {1} \n此分支会返回长度为 {2} 的类型的元组\n {3} \n @@ -452,6 +452,11 @@ 支持在序列表达式中试用 + + Interop betwee C#'s and F#'s unmanaged generic constraint + Interop betwee C#'s and F#'s unmanaged generic constraint + + Raises warnings when an copy-and-update record expression changes all fields of a record. 复制和更新记录表达式更改记录的所有字段时引发警告。 diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index 4c4f54c4662..c7eb266c825 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -166,7 +166,7 @@ The constraints 'unmanaged' and 'not struct' are inconsistent The constraints 'unmanaged' and 'not struct' are inconsistent - + All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n {1} \nThis branch returns a tuple of length {2} of type\n {3} \n 'if' 運算式的所有分支都傳回可隱含轉換為第一個分支的類型的值,這是類型為\n {1} \n的元組長度 {0}此分支傳回的是類型為\n {3} \n的元組長度 {2} @@ -452,6 +452,11 @@ 支援循序運算式中的 try-with + + Interop betwee C#'s and F#'s unmanaged generic constraint + Interop betwee C#'s and F#'s unmanaged generic constraint + + Raises warnings when an copy-and-update record expression changes all fields of a record. 當複製和更新記錄運算式變更記錄的所有欄位時引發警告。 diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs index c2ba64824d9..8abe1f6c70e 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs @@ -335,6 +335,7 @@ let _ = Test>>() [] let ``Multi constraint IL test together with struct and interface constraints`` () = Fsx "[] type Test<'T when 'T: unmanaged and 'T: struct and 'T:>System.IComparable> = struct end" + |> withLangVersionPreview |> compile |> shouldSucceed |> verifyIL [""" @@ -362,6 +363,7 @@ let _ = Test>>() [] let ``IsUnmanagedAttribute Attribute is emitted for function with unmanaged constraint`` () = Fsx "let testMyFunction (x: 'TUnmanaged when 'TUnmanaged : unmanaged) = struct(x,1)" + |> withLangVersionPreview |> compile |> shouldSucceed |> verifyIL [""" @@ -392,6 +394,7 @@ let _ = Test>>() open CsLib let y = new CsharpStruct(struct(1,"this is string")) """ |> withReferences [csLib] + |> withLangVersionPreview app |> compile @@ -422,14 +425,16 @@ printf "%s" (CsharpStruct.Hi()) """ |> withReferences [csLib] app + |> withLangVersionPreview |> asExe |> compile |> run |> verifyOutput "MultiCaseUnion" [] - let ``F# generates modreq for C# to consume`` () = + let ``FSharp generates modreq for CSharp to consume in preview`` () = Fsx "let testMyFunction (x: 'TUnmanaged when 'TUnmanaged : unmanaged) = ()" + |> withLangVersionPreview |> compile |> shouldSucceed |> verifyIL [""" @@ -442,6 +447,20 @@ printf "%s" (CsharpStruct.Hi()) IL_0000: ret } """] + [] + let ``FSharp does not generate modreq for VBNET to consume in v7`` () = + Fsx "let testMyFunction (x: 'TUnmanaged when 'TUnmanaged : unmanaged) = ()" + |> withLangVersion70 + |> compile + |> shouldSucceed + |> verifyIL [""" + .method public static void testMyFunction(!!TUnmanaged x) cil managed + { + + .maxstack 8 + IL_0000: ret + } """] + [] let ``C# can consume F#-defined struct with unmanaged constraint - valid`` () = @@ -452,6 +471,7 @@ type FsharpStructWrapper<'TInner when 'TInner: unmanaged> = val Item : 'TInner with static member Hi() = typeof<'TInner>.Name""" |> asLibrary + |> withLangVersionPreview |> withName "fsLib" let app = @@ -497,6 +517,7 @@ module FsharpPreparedData = let structDuExample2 = B "42" """ |> asLibrary + |> withLangVersionPreview |> withName "fsLib" let app = From e4726b470519e02ee6b5d3beaa3def26dc8885f9 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Mon, 24 Jul 2023 17:27:04 +0200 Subject: [PATCH 13/15] fantom --- src/Compiler/CodeGen/IlxGen.fs | 10 +++++----- src/Compiler/Facilities/LanguageFeatures.fs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Compiler/CodeGen/IlxGen.fs b/src/Compiler/CodeGen/IlxGen.fs index 8abc6369f59..33b39ce7f73 100644 --- a/src/Compiler/CodeGen/IlxGen.fs +++ b/src/Compiler/CodeGen/IlxGen.fs @@ -5572,11 +5572,11 @@ and GenGenericParam cenv eenv (tp: Typar) = | _ -> false) let emitUnmanagedInIlOutput = - cenv.g.langVersion.SupportsFeature(LanguageFeature.UnmanagedConstraintCsharpInterop) && - tp.Constraints - |> List.exists (function - | TyparConstraint.IsUnmanaged _ -> true - | _ -> false) + cenv.g.langVersion.SupportsFeature(LanguageFeature.UnmanagedConstraintCsharpInterop) + && tp.Constraints + |> List.exists (function + | TyparConstraint.IsUnmanaged _ -> true + | _ -> false) let tpName = // use the CompiledName if given diff --git a/src/Compiler/Facilities/LanguageFeatures.fs b/src/Compiler/Facilities/LanguageFeatures.fs index 9464f83dcae..0d589757fab 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fs +++ b/src/Compiler/Facilities/LanguageFeatures.fs @@ -172,7 +172,7 @@ type LanguageVersion(versionText) = LanguageFeature.WarningWhenTailRecAttributeButNonTailRecUsage, previewVersion LanguageFeature.StaticLetInRecordsDusEmptyTypes, previewVersion LanguageFeature.StrictIndentation, previewVersion - LanguageFeature.UnmanagedConstraintCsharpInterop, previewVersion + LanguageFeature.UnmanagedConstraintCsharpInterop, previewVersion ] static let defaultLanguageVersion = LanguageVersion("default") From 9dec2d9d25ac93e56837148ca77e2abb55c9033d Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 25 Jul 2023 11:25:41 +0200 Subject: [PATCH 14/15] Update src/Compiler/FSComp.txt Co-authored-by: Vlad Zarytovskii --- src/Compiler/FSComp.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index c8e98b2d8b1..f759495cc79 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1710,4 +1710,4 @@ featureAccessorFunctionShorthand,"underscore dot shorthand for accessor only fun 3570,tcStaticBindingInExtrinsicAugmentation,"Static bindings cannot be added to extrinsic augmentations. Consider using a 'static member' instead." 3571,pickleFsharpCoreBackwardsCompatible,"Newly added pickle state cannot be used in FSharp.Core, since it must be working in older compilers+tooling as well. The time window is at least 3 years after feature introduction. Violation: %s . Context: \n %s " 3577,tcOverrideUsesMultipleArgumentsInsteadOfTuple,"This override takes a tuple instead of multiple arguments. Try to add an additional layer of parentheses at the method definition (e.g. 'member _.Foo((x, y))'), or remove parentheses at the abstract method declaration (e.g. 'abstract member Foo: 'a * 'b -> 'c')." -featureUnmanagedConstraintCsharpInterop,"Interop betwee C#'s and F#'s unmanaged generic constraint" \ No newline at end of file +featureUnmanagedConstraintCsharpInterop,"Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq)" \ No newline at end of file From 90cff50c27116702ff8f25f92bacfcd909f6bc57 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 25 Jul 2023 13:15:25 +0200 Subject: [PATCH 15/15] localized resources rebuilt --- src/Compiler/xlf/FSComp.txt.cs.xlf | 4 ++-- src/Compiler/xlf/FSComp.txt.de.xlf | 4 ++-- src/Compiler/xlf/FSComp.txt.es.xlf | 4 ++-- src/Compiler/xlf/FSComp.txt.fr.xlf | 4 ++-- src/Compiler/xlf/FSComp.txt.it.xlf | 4 ++-- src/Compiler/xlf/FSComp.txt.ja.xlf | 4 ++-- src/Compiler/xlf/FSComp.txt.ko.xlf | 4 ++-- src/Compiler/xlf/FSComp.txt.pl.xlf | 4 ++-- src/Compiler/xlf/FSComp.txt.pt-BR.xlf | 4 ++-- src/Compiler/xlf/FSComp.txt.ru.xlf | 4 ++-- src/Compiler/xlf/FSComp.txt.tr.xlf | 4 ++-- src/Compiler/xlf/FSComp.txt.zh-Hans.xlf | 4 ++-- src/Compiler/xlf/FSComp.txt.zh-Hant.xlf | 4 ++-- 13 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index 7c2fe454dfd..1774835234c 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -453,8 +453,8 @@ - Interop betwee C#'s and F#'s unmanaged generic constraint - Interop betwee C#'s and F#'s unmanaged generic constraint + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index 846c30e3ce7..94bf9bb03f7 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -453,8 +453,8 @@ - Interop betwee C#'s and F#'s unmanaged generic constraint - Interop betwee C#'s and F#'s unmanaged generic constraint + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index 55bb1f4c3c3..dacdeb354f1 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -453,8 +453,8 @@ - Interop betwee C#'s and F#'s unmanaged generic constraint - Interop betwee C#'s and F#'s unmanaged generic constraint + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index fd919f59596..2f8338efcd3 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -453,8 +453,8 @@ - Interop betwee C#'s and F#'s unmanaged generic constraint - Interop betwee C#'s and F#'s unmanaged generic constraint + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index 0f91163729d..a27c941ce6c 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -453,8 +453,8 @@ - Interop betwee C#'s and F#'s unmanaged generic constraint - Interop betwee C#'s and F#'s unmanaged generic constraint + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index edc61f725b8..ec10f4449b5 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -453,8 +453,8 @@ - Interop betwee C#'s and F#'s unmanaged generic constraint - Interop betwee C#'s and F#'s unmanaged generic constraint + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index b68a75b349a..2a60ab567cb 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -453,8 +453,8 @@ - Interop betwee C#'s and F#'s unmanaged generic constraint - Interop betwee C#'s and F#'s unmanaged generic constraint + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index 01eb905d79b..23edf6023fa 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -453,8 +453,8 @@ - Interop betwee C#'s and F#'s unmanaged generic constraint - Interop betwee C#'s and F#'s unmanaged generic constraint + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 3b8289662a3..bd334da6c2b 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -453,8 +453,8 @@ - Interop betwee C#'s and F#'s unmanaged generic constraint - Interop betwee C#'s and F#'s unmanaged generic constraint + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index 3afa4fb909f..c22f46f5ac8 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -453,8 +453,8 @@ - Interop betwee C#'s and F#'s unmanaged generic constraint - Interop betwee C#'s and F#'s unmanaged generic constraint + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index ebb04e90e9a..d1a94c2bf75 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -453,8 +453,8 @@ - Interop betwee C#'s and F#'s unmanaged generic constraint - Interop betwee C#'s and F#'s unmanaged generic constraint + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index 4c8e8fde67d..5b0e5ebb6eb 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -453,8 +453,8 @@ - Interop betwee C#'s and F#'s unmanaged generic constraint - Interop betwee C#'s and F#'s unmanaged generic constraint + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index c7eb266c825..8cebbef5299 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -453,8 +453,8 @@ - Interop betwee C#'s and F#'s unmanaged generic constraint - Interop betwee C#'s and F#'s unmanaged generic constraint + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq) + Interop between C#'s and F#'s unmanaged generic constraint (emit additional modreq)