From cbc0183f7d18556159d6c8258b13086f02b3dbc2 Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Sat, 5 Oct 2024 20:35:22 +0800 Subject: [PATCH] move `isSafe/setUnsafe` to `safe.d` (#16871) --- compiler/src/dmd/dcast.d | 1 + compiler/src/dmd/dsymbolsem.d | 1 + compiler/src/dmd/escape.d | 1 + compiler/src/dmd/frontend.h | 2 - compiler/src/dmd/func.d | 66 ----------- compiler/src/dmd/funcsem.d | 111 +------------------ compiler/src/dmd/initsem.d | 1 + compiler/src/dmd/safe.d | 187 +++++++++++++++++++++++++++++++- compiler/src/dmd/statementsem.d | 1 + compiler/src/dmd/tocsym.d | 1 + 10 files changed, 193 insertions(+), 179 deletions(-) diff --git a/compiler/src/dmd/dcast.d b/compiler/src/dmd/dcast.d index ef84af9de5a..f54484f981e 100644 --- a/compiler/src/dmd/dcast.d +++ b/compiler/src/dmd/dcast.d @@ -46,6 +46,7 @@ import dmd.root.ctfloat; import dmd.common.outbuffer; import dmd.root.rmem; import dmd.root.utf; +import dmd.safe : setUnsafe; import dmd.tokens; import dmd.typesem; diff --git a/compiler/src/dmd/dsymbolsem.d b/compiler/src/dmd/dsymbolsem.d index 4eed91aca3a..4395ff81ab0 100644 --- a/compiler/src/dmd/dsymbolsem.d +++ b/compiler/src/dmd/dsymbolsem.d @@ -67,6 +67,7 @@ import dmd.root.filename; import dmd.common.outbuffer; import dmd.root.rmem; import dmd.rootobject; +import dmd.safe; import dmd.semantic2; import dmd.semantic3; import dmd.sideeffect; diff --git a/compiler/src/dmd/escape.d b/compiler/src/dmd/escape.d index fb3fb9b9645..e1b2ef791a7 100644 --- a/compiler/src/dmd/escape.d +++ b/compiler/src/dmd/escape.d @@ -34,6 +34,7 @@ import dmd.location; import dmd.mtype; import dmd.printast; import dmd.rootobject; +import dmd.safe; import dmd.tokens; import dmd.typesem : hasPointers, parameterStorageClass; import dmd.visitor; diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index d47e83a44d9..e5daa3ae1ed 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -3773,8 +3773,6 @@ class FuncDeclaration : public Declaration bool isCodeseg() const final override; bool isOverloadable() const final override; bool isAbstract() final override; - bool isSafe(); - bool isTrusted(); virtual bool isNested() const; AggregateDeclaration* isThis() override; bool needThis() final override; diff --git a/compiler/src/dmd/func.d b/compiler/src/dmd/func.d index 996b4377405..22a25a115a6 100644 --- a/compiler/src/dmd/func.d +++ b/compiler/src/dmd/func.d @@ -685,72 +685,6 @@ extern (C++) class FuncDeclaration : Declaration return bitFields; } - final bool isSafe() - { - if (safetyInprocess) - setUnsafe(); - return type.toTypeFunction().trust == TRUST.safe; - } - - extern (D) final bool isSafeBypassingInference() - { - return !(safetyInprocess) && isSafe(); - } - - final bool isTrusted() - { - if (safetyInprocess) - setUnsafe(); - return type.toTypeFunction().trust == TRUST.trusted; - } - - /************************************** - * The function is doing something unsafe, so mark it as unsafe. - * - * Params: - * gag = surpress error message (used in escape.d) - * loc = location of error - * fmt = printf-style format string - * arg0 = (optional) argument for first %s format specifier - * arg1 = (optional) argument for second %s format specifier - * arg2 = (optional) argument for third %s format specifier - * Returns: whether there's a safe error - */ - extern (D) final bool setUnsafe( - bool gag = false, Loc loc = Loc.init, const(char)* fmt = null, - RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) - { - if (safetyInprocess) - { - safetyInprocess = false; - type.toTypeFunction().trust = TRUST.system; - if (fmt || arg0) - safetyViolation = new AttributeViolation(loc, fmt, arg0, arg1, arg2); - - if (fes) - fes.func.setUnsafe(); - } - else if (isSafe()) - { - if (!gag && fmt) - .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); - - return true; - } - return false; - } - - /************************************** - * The function is calling `@system` function `f`, so mark it as unsafe. - * - * Params: - * f = function being called (needed for diagnostic of inferred functions) - * Returns: whether there's a safe error - */ - extern (D) final bool setUnsafeCall(FuncDeclaration f) - { - return setUnsafe(false, f.loc, null, f, null); - } /************************************** * The function is doing something that may throw an exception, register that in case nothrow is being inferred diff --git a/compiler/src/dmd/funcsem.d b/compiler/src/dmd/funcsem.d index bf59f45baeb..3502916c057 100644 --- a/compiler/src/dmd/funcsem.d +++ b/compiler/src/dmd/funcsem.d @@ -54,6 +54,7 @@ import dmd.rootobject; import dmd.root.filename; import dmd.root.string; import dmd.root.stringtable; +import dmd.safe; import dmd.semantic2; import dmd.semantic3; import dmd.statement; @@ -2807,116 +2808,6 @@ bool isRootTraitsCompilesScope(Scope* sc) @safe return (sc.traitsCompiles) && !sc.func.skipCodegen; } -/************************************** - * A statement / expression in this scope is not `@safe`, - * so mark the enclosing function as `@system` - * - * Params: - * sc = scope that the unsafe statement / expression is in - * gag = surpress error message (used in escape.d) - * loc = location of error - * fmt = printf-style format string - * arg0 = (optional) argument for first %s format specifier - * arg1 = (optional) argument for second %s format specifier - * arg2 = (optional) argument for third %s format specifier - * Returns: whether there's a safe error - */ -bool setUnsafe(Scope* sc, - bool gag = false, Loc loc = Loc.init, const(char)* fmt = null, - RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) -{ - if (sc.intypeof) - return false; // typeof(cast(int*)0) is safe - - if (sc.debug_) // debug {} scopes are permissive - return false; - - if (!sc.func) - { - if (sc.varDecl) - { - if (sc.varDecl.storage_class & STC.safe) - { - .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); - return true; - } - else if (!(sc.varDecl.storage_class & STC.trusted)) - { - sc.varDecl.storage_class |= STC.system; - sc.varDecl.systemInferred = true; - } - } - return false; - } - - - if (isRootTraitsCompilesScope(sc)) // __traits(compiles, x) - { - if (sc.func.isSafeBypassingInference()) - { - // Message wil be gagged, but still call error() to update global.errors and for - // -verrors=spec - .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); - return true; - } - return false; - } - - return sc.func.setUnsafe(gag, loc, fmt, arg0, arg1, arg2); -} - -/*************************************** - * Like `setUnsafe`, but for safety errors still behind preview switches - * - * Given a `FeatureState fs`, for example dip1000 / dip25 / systemVariables, - * the behavior changes based on the setting: - * - * - In case of `-revert=fs`, it does nothing. - * - In case of `-preview=fs`, it's the same as `setUnsafe` - * - By default, print a deprecation in `@safe` functions, or store an attribute violation in inferred functions. - * - * Params: - * sc = used to find affected function/variable, and for checking whether we are in a deprecated / speculative scope - * fs = feature state from the preview flag - * gag = surpress error message - * loc = location of error - * msg = printf-style format string - * arg0 = (optional) argument for first %s format specifier - * arg1 = (optional) argument for second %s format specifier - * arg2 = (optional) argument for third %s format specifier - * Returns: whether an actual safe error (not deprecation) occured - */ -bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char)* msg, - RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) -{ - //printf("setUnsafePreview() fs:%d %s\n", fs, msg); - with (FeatureState) final switch (fs) - { - case disabled: - return false; - - case enabled: - return sc.setUnsafe(gag, loc, msg, arg0, arg1, arg2); - - case default_: - if (!sc.func) - return false; - if (sc.func.isSafeBypassingInference()) - { - if (!gag && !sc.isDeprecated()) - { - deprecation(loc, msg, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); - } - } - else if (!sc.func.safetyViolation) - { - import dmd.func : AttributeViolation; - sc.func.safetyViolation = new AttributeViolation(loc, msg, arg0, arg1, arg2); - } - return false; - } -} - /+ + Checks the parameter and return types iff this is a `main` function. + diff --git a/compiler/src/dmd/initsem.d b/compiler/src/dmd/initsem.d index a72fd90ec98..505a3e13636 100644 --- a/compiler/src/dmd/initsem.d +++ b/compiler/src/dmd/initsem.d @@ -41,6 +41,7 @@ import dmd.location; import dmd.mtype; import dmd.opover; import dmd.optimize; +import dmd.safe : setUnsafe; import dmd.statement; import dmd.target; import dmd.tokens; diff --git a/compiler/src/dmd/safe.d b/compiler/src/dmd/safe.d index 34cab800868..bb4e3f49873 100644 --- a/compiler/src/dmd/safe.d +++ b/compiler/src/dmd/safe.d @@ -21,14 +21,19 @@ import dmd.dcast : implicitConvTo; import dmd.dclass; import dmd.declaration; import dmd.dscope; +import dmd.errors; import dmd.expression; +import dmd.func; +import dmd.funcsem : isRootTraitsCompilesScope; +import dmd.globals : FeatureState; import dmd.id; import dmd.identifier; +import dmd.location; import dmd.mtype; +import dmd.rootobject; import dmd.target; import dmd.tokens; import dmd.typesem : hasPointers, arrayOf, size; -import dmd.funcsem : setUnsafe, setUnsafePreview; /************************************************************* * Check for unsafe access in @safe code: @@ -309,3 +314,183 @@ bool checkUnsafeDotExp(Scope* sc, Expression e, Identifier id, int flag) } return false; } + +bool isSafe(FuncDeclaration fd) +{ + if (fd.safetyInprocess) + fd.setUnsafe(); + return fd.type.toTypeFunction().trust == TRUST.safe; +} + +extern (D) bool isSafeBypassingInference(FuncDeclaration fd) +{ + return !(fd.safetyInprocess) && fd.isSafe(); +} + +bool isTrusted(FuncDeclaration fd) +{ + if (fd.safetyInprocess) + fd.setUnsafe(); + return fd.type.toTypeFunction().trust == TRUST.trusted; +} + +/************************************** + * The function is doing something unsafe, so mark it as unsafe. + * + * Params: + * fd = func declaration to set unsafe + * gag = surpress error message (used in escape.d) + * loc = location of error + * fmt = printf-style format string + * arg0 = (optional) argument for first %s format specifier + * arg1 = (optional) argument for second %s format specifier + * arg2 = (optional) argument for third %s format specifier + * Returns: whether there's a safe error + */ +extern (D) bool setUnsafe( + FuncDeclaration fd, + bool gag = false, Loc loc = Loc.init, const(char)* fmt = null, + RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) +{ + if (fd.safetyInprocess) + { + fd.safetyInprocess = false; + fd.type.toTypeFunction().trust = TRUST.system; + if (fmt || arg0) + fd.safetyViolation = new AttributeViolation(loc, fmt, arg0, arg1, arg2); + + if (fd.fes) + fd.fes.func.setUnsafe(); + } + else if (fd.isSafe()) + { + if (!gag && fmt) + .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); + + return true; + } + return false; +} + +/************************************** + * The function is calling `@system` function `f`, so mark it as unsafe. + * + * Params: + * fd = caller + * f = function being called (needed for diagnostic of inferred functions) + * Returns: whether there's a safe error + */ +extern (D) bool setUnsafeCall(FuncDeclaration fd, FuncDeclaration f) +{ + return fd.setUnsafe(false, f.loc, null, f, null); +} + +/************************************** + * A statement / expression in this scope is not `@safe`, + * so mark the enclosing function as `@system` + * + * Params: + * sc = scope that the unsafe statement / expression is in + * gag = surpress error message (used in escape.d) + * loc = location of error + * fmt = printf-style format string + * arg0 = (optional) argument for first %s format specifier + * arg1 = (optional) argument for second %s format specifier + * arg2 = (optional) argument for third %s format specifier + * Returns: whether there's a safe error + */ +bool setUnsafe(Scope* sc, + bool gag = false, Loc loc = Loc.init, const(char)* fmt = null, + RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) +{ + if (sc.intypeof) + return false; // typeof(cast(int*)0) is safe + + if (sc.debug_) // debug {} scopes are permissive + return false; + + if (!sc.func) + { + if (sc.varDecl) + { + if (sc.varDecl.storage_class & STC.safe) + { + .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); + return true; + } + else if (!(sc.varDecl.storage_class & STC.trusted)) + { + sc.varDecl.storage_class |= STC.system; + sc.varDecl.systemInferred = true; + } + } + return false; + } + + + if (isRootTraitsCompilesScope(sc)) // __traits(compiles, x) + { + if (sc.func.isSafeBypassingInference()) + { + // Message wil be gagged, but still call error() to update global.errors and for + // -verrors=spec + .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); + return true; + } + return false; + } + + return sc.func.setUnsafe(gag, loc, fmt, arg0, arg1, arg2); +} + +/*************************************** + * Like `setUnsafe`, but for safety errors still behind preview switches + * + * Given a `FeatureState fs`, for example dip1000 / dip25 / systemVariables, + * the behavior changes based on the setting: + * + * - In case of `-revert=fs`, it does nothing. + * - In case of `-preview=fs`, it's the same as `setUnsafe` + * - By default, print a deprecation in `@safe` functions, or store an attribute violation in inferred functions. + * + * Params: + * sc = used to find affected function/variable, and for checking whether we are in a deprecated / speculative scope + * fs = feature state from the preview flag + * gag = surpress error message + * loc = location of error + * msg = printf-style format string + * arg0 = (optional) argument for first %s format specifier + * arg1 = (optional) argument for second %s format specifier + * arg2 = (optional) argument for third %s format specifier + * Returns: whether an actual safe error (not deprecation) occured + */ +bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char)* msg, + RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) +{ + //printf("setUnsafePreview() fs:%d %s\n", fs, msg); + with (FeatureState) final switch (fs) + { + case disabled: + return false; + + case enabled: + return sc.setUnsafe(gag, loc, msg, arg0, arg1, arg2); + + case default_: + if (!sc.func) + return false; + if (sc.func.isSafeBypassingInference()) + { + if (!gag && !sc.isDeprecated()) + { + deprecation(loc, msg, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); + } + } + else if (!sc.func.safetyViolation) + { + import dmd.func : AttributeViolation; + sc.func.safetyViolation = new AttributeViolation(loc, msg, arg0, arg1, arg2); + } + return false; + } +} diff --git a/compiler/src/dmd/statementsem.d b/compiler/src/dmd/statementsem.d index 9d988f08d12..a73bd5ebdf3 100644 --- a/compiler/src/dmd/statementsem.d +++ b/compiler/src/dmd/statementsem.d @@ -57,6 +57,7 @@ import dmd.opover; import dmd.parse; import dmd.common.outbuffer; import dmd.root.string; +import dmd.safe : isSafe, setUnsafe; import dmd.semantic2; import dmd.sideeffect; import dmd.statement; diff --git a/compiler/src/dmd/tocsym.d b/compiler/src/dmd/tocsym.d index e1aa8a44206..74bfe275f1a 100644 --- a/compiler/src/dmd/tocsym.d +++ b/compiler/src/dmd/tocsym.d @@ -42,6 +42,7 @@ import dmd.init; import dmd.location; import dmd.mangle; import dmd.mtype; +import dmd.safe : isSafe; import dmd.target; import dmd.toctype; import dmd.todt;