Fix 20148 - void initializated bool can be both true and false

This commit is contained in:
Dennis Korpel 2023-06-28 12:12:25 +02:00 committed by The Dlang Bot
parent 8272384b3e
commit 0b677d90ee
6 changed files with 86 additions and 26 deletions

View file

@ -223,7 +223,7 @@ extern (C++) class StructDeclaration : AggregateDeclaration
bool hasCopyCtor; // copy constructor bool hasCopyCtor; // copy constructor
bool hasPointerField; // members with indirections bool hasPointerField; // members with indirections
bool hasVoidInitPointers; // void-initialized unsafe fields bool hasVoidInitPointers; // void-initialized unsafe fields
bool hasSystemFields; // @system members bool hasUnsafeBitpatterns; // @system members, pointers, bool
bool hasFieldWithInvariant; // invariants bool hasFieldWithInvariant; // invariants
bool computedTypeProperties;// the above 3 fields are computed bool computedTypeProperties;// the above 3 fields are computed
// Even if struct is defined as non-root symbol, some built-in operations // Even if struct is defined as non-root symbol, some built-in operations
@ -396,13 +396,16 @@ extern (C++) class StructDeclaration : AggregateDeclaration
foreach (vd; fields) foreach (vd; fields)
{ {
if (vd.storage_class & STC.ref_ || vd.hasPointers()) if (vd.storage_class & STC.ref_ || vd.hasPointers())
{
hasPointerField = true; hasPointerField = true;
hasUnsafeBitpatterns = true;
}
if (vd._init && vd._init.isVoidInitializer() && vd.type.hasPointers()) if (vd._init && vd._init.isVoidInitializer() && vd.type.hasPointers())
hasVoidInitPointers = true; hasVoidInitPointers = true;
if (vd.storage_class & STC.system || vd.type.hasSystemFields()) if (vd.storage_class & STC.system || vd.type.hasUnsafeBitpatterns())
hasSystemFields = true; hasUnsafeBitpatterns = true;
if (!vd._init && vd.type.hasVoidInitPointers()) if (!vd._init && vd.type.hasVoidInitPointers())
hasVoidInitPointers = true; hasVoidInitPointers = true;

View file

@ -1072,8 +1072,11 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
// Calculate type size + safety checks // Calculate type size + safety checks
if (sc && sc.func) if (sc && sc.func)
{ {
if (dsym._init && dsym._init.isVoidInitializer()) if (dsym._init && dsym._init.isVoidInitializer() && !(dsym.storage_class & STC.temp))
{ {
// Don't do these checks for STC.temp vars because the generated `opAssign`
// for a struct with postblit and destructor void initializes a temporary
// __swap variable, which can be trusted
if (dsym.type.hasPointers()) // also computes type size if (dsym.type.hasPointers()) // also computes type size
sc.setUnsafe(false, dsym.loc, sc.setUnsafe(false, dsym.loc,
@ -1081,9 +1084,12 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
else if (dsym.type.hasInvariant()) else if (dsym.type.hasInvariant())
sc.setUnsafe(false, dsym.loc, sc.setUnsafe(false, dsym.loc,
"`void` initializers for structs with invariants are not allowed in safe functions"); "`void` initializers for structs with invariants are not allowed in safe functions");
else if (dsym.type.hasSystemFields()) else if (dsym.type.toBasetype().ty == Tbool)
sc.setUnsafePreview(global.params.systemVariables, false, dsym.loc, sc.setUnsafePreview(global.params.systemVariables, false, dsym.loc,
"`void` initializers for `@system` variables not allowed in safe functions"); "a `bool` must be 0 or 1, so void intializing it is not allowed in safe functions");
else if (dsym.type.hasUnsafeBitpatterns())
sc.setUnsafePreview(global.params.systemVariables, false, dsym.loc,
"`void` initializers for types with unsafe bit patterns are not allowed in safe functions");
} }
else if (!dsym._init && else if (!dsym._init &&
!(dsym.storage_class & (STC.static_ | STC.extern_ | STC.gshared | STC.manifest | STC.field | STC.parameter)) && !(dsym.storage_class & (STC.static_ | STC.extern_ | STC.gshared | STC.manifest | STC.field | STC.parameter)) &&

View file

@ -1863,7 +1863,7 @@ public:
virtual bool isZeroInit(const Loc& loc); virtual bool isZeroInit(const Loc& loc);
virtual int32_t hasWild() const; virtual int32_t hasWild() const;
virtual bool hasVoidInitPointers(); virtual bool hasVoidInitPointers();
virtual bool hasSystemFields(); virtual bool hasUnsafeBitpatterns();
virtual bool hasInvariant(); virtual bool hasInvariant();
virtual Type* nextOf(); virtual Type* nextOf();
Type* baseElemOf(); Type* baseElemOf();
@ -4242,6 +4242,7 @@ public:
bool isunsigned() override; bool isunsigned() override;
MATCH implicitConvTo(Type* to) override; MATCH implicitConvTo(Type* to) override;
bool isZeroInit(const Loc& loc) override; bool isZeroInit(const Loc& loc) override;
bool hasUnsafeBitpatterns() override;
TypeBasic* isTypeBasic() override; TypeBasic* isTypeBasic() override;
void accept(Visitor* v) override; void accept(Visitor* v) override;
}; };
@ -4329,7 +4330,7 @@ public:
MATCH constConv(Type* to) override; MATCH constConv(Type* to) override;
bool isZeroInit(const Loc& loc) override; bool isZeroInit(const Loc& loc) override;
bool hasVoidInitPointers() override; bool hasVoidInitPointers() override;
bool hasSystemFields() override; bool hasUnsafeBitpatterns() override;
bool hasInvariant() override; bool hasInvariant() override;
Type* nextOf() override; Type* nextOf() override;
void accept(Visitor* v) override; void accept(Visitor* v) override;
@ -4568,7 +4569,7 @@ public:
MATCH constConv(Type* to) override; MATCH constConv(Type* to) override;
MATCH implicitConvTo(Type* to) override; MATCH implicitConvTo(Type* to) override;
Expression* defaultInitLiteral(const Loc& loc) override; Expression* defaultInitLiteral(const Loc& loc) override;
bool hasSystemFields() override; bool hasUnsafeBitpatterns() override;
bool hasVoidInitPointers() override; bool hasVoidInitPointers() override;
bool hasInvariant() override; bool hasInvariant() override;
bool needsDestruction() override; bool needsDestruction() override;
@ -4607,7 +4608,7 @@ public:
bool needsCopyOrPostblit() override; bool needsCopyOrPostblit() override;
bool needsNested() override; bool needsNested() override;
bool hasVoidInitPointers() override; bool hasVoidInitPointers() override;
bool hasSystemFields() override; bool hasUnsafeBitpatterns() override;
bool hasInvariant() override; bool hasInvariant() override;
MATCH implicitConvTo(Type* to) override; MATCH implicitConvTo(Type* to) override;
MATCH constConv(Type* to) override; MATCH constConv(Type* to) override;
@ -7311,8 +7312,8 @@ public:
bool hasPointerField(bool v); bool hasPointerField(bool v);
bool hasVoidInitPointers() const; bool hasVoidInitPointers() const;
bool hasVoidInitPointers(bool v); bool hasVoidInitPointers(bool v);
bool hasSystemFields() const; bool hasUnsafeBitpatterns() const;
bool hasSystemFields(bool v); bool hasUnsafeBitpatterns(bool v);
bool hasFieldWithInvariant() const; bool hasFieldWithInvariant() const;
bool hasFieldWithInvariant(bool v); bool hasFieldWithInvariant(bool v);
bool computedTypeProperties() const; bool computedTypeProperties() const;

View file

@ -1412,7 +1412,7 @@ extern (C++) abstract class Type : ASTNode
* Returns: * Returns:
* true if so * true if so
*/ */
bool hasSystemFields() bool hasUnsafeBitpatterns()
{ {
return false; return false;
} }
@ -2340,6 +2340,11 @@ extern (C++) final class TypeBasic : Type
} }
} }
override bool hasUnsafeBitpatterns()
{
return ty == Tbool;
}
// For eliminating dynamic_cast // For eliminating dynamic_cast
override TypeBasic isTypeBasic() override TypeBasic isTypeBasic()
{ {
@ -2657,9 +2662,9 @@ extern (C++) final class TypeSArray : TypeArray
return ae; return ae;
} }
override bool hasSystemFields() override bool hasUnsafeBitpatterns()
{ {
return next.hasSystemFields(); return next.hasUnsafeBitpatterns();
} }
override bool hasVoidInitPointers() override bool hasVoidInitPointers()
@ -3976,11 +3981,11 @@ extern (C++) final class TypeStruct : Type
return sym.hasVoidInitPointers; return sym.hasVoidInitPointers;
} }
override bool hasSystemFields() override bool hasUnsafeBitpatterns()
{ {
sym.size(Loc.initial); // give error for forward references sym.size(Loc.initial); // give error for forward references
sym.determineTypeProperties(); sym.determineTypeProperties();
return sym.hasSystemFields; return sym.hasUnsafeBitpatterns;
} }
override bool hasInvariant() override bool hasInvariant()
@ -4239,9 +4244,9 @@ extern (C++) final class TypeEnum : Type
return memType().hasVoidInitPointers(); return memType().hasVoidInitPointers();
} }
override bool hasSystemFields() override bool hasUnsafeBitpatterns()
{ {
return memType().hasSystemFields(); return memType().hasUnsafeBitpatterns();
} }
override bool hasInvariant() override bool hasInvariant()

View file

@ -276,7 +276,7 @@ public:
virtual bool isZeroInit(const Loc &loc = Loc()); // if initializer is 0 virtual bool isZeroInit(const Loc &loc = Loc()); // if initializer is 0
virtual int hasWild() const; virtual int hasWild() const;
virtual bool hasVoidInitPointers(); virtual bool hasVoidInitPointers();
virtual bool hasSystemFields(); virtual bool hasUnsafeBitpatterns();
virtual bool hasInvariant(); virtual bool hasInvariant();
virtual Type *nextOf(); virtual Type *nextOf();
Type *baseElemOf(); Type *baseElemOf();
@ -421,7 +421,7 @@ public:
MATCH constConv(Type *to) override; MATCH constConv(Type *to) override;
MATCH implicitConvTo(Type *to) override; MATCH implicitConvTo(Type *to) override;
Expression *defaultInitLiteral(const Loc &loc) override; Expression *defaultInitLiteral(const Loc &loc) override;
bool hasSystemFields() override; bool hasUnsafeBitpatterns() override;
bool hasVoidInitPointers() override; bool hasVoidInitPointers() override;
bool hasInvariant() override; bool hasInvariant() override;
bool needsDestruction() override; bool needsDestruction() override;
@ -739,7 +739,7 @@ public:
bool needsCopyOrPostblit() override; bool needsCopyOrPostblit() override;
bool needsNested() override; bool needsNested() override;
bool hasVoidInitPointers() override; bool hasVoidInitPointers() override;
bool hasSystemFields() override; bool hasUnsafeBitpatterns() override;
bool hasInvariant() override; bool hasInvariant() override;
MATCH implicitConvTo(Type *to) override; MATCH implicitConvTo(Type *to) override;
MATCH constConv(Type *to) override; MATCH constConv(Type *to) override;
@ -775,7 +775,7 @@ public:
MATCH constConv(Type *to) override; MATCH constConv(Type *to) override;
bool isZeroInit(const Loc &loc) override; bool isZeroInit(const Loc &loc) override;
bool hasVoidInitPointers() override; bool hasVoidInitPointers() override;
bool hasSystemFields() override; bool hasUnsafeBitpatterns() override;
bool hasInvariant() override; bool hasInvariant() override;
Type *nextOf() override; Type *nextOf() override;

View file

@ -2,9 +2,13 @@
REQUIRED_ARGS: -preview=systemVariables REQUIRED_ARGS: -preview=systemVariables
TEST_OUTPUT: TEST_OUTPUT:
--- ---
fail_compilation/systemvariables_void_init.d(29): Error: `void` initializers for `@system` variables not allowed in safe functions fail_compilation/systemvariables_void_init.d(48): Error: `void` initializers for types with unsafe bit patterns are not allowed in safe functions
fail_compilation/systemvariables_void_init.d(30): Error: `void` initializers for `@system` variables not allowed in safe functions fail_compilation/systemvariables_void_init.d(49): Error: `void` initializers for types with unsafe bit patterns are not allowed in safe functions
fail_compilation/systemvariables_void_init.d(31): Error: `void` initializers for `@system` variables not allowed in safe functions fail_compilation/systemvariables_void_init.d(50): Error: `void` initializers for types with unsafe bit patterns are not allowed in safe functions
fail_compilation/systemvariables_void_init.d(51): Error: a `bool` must be 0 or 1, so void intializing it is not allowed in safe functions
fail_compilation/systemvariables_void_init.d(52): Error: a `bool` must be 0 or 1, so void intializing it is not allowed in safe functions
fail_compilation/systemvariables_void_init.d(53): Error: `void` initializers for types with unsafe bit patterns are not allowed in safe functions
fail_compilation/systemvariables_void_init.d(54): Error: `void` initializers for types with unsafe bit patterns are not allowed in safe functions
--- ---
*/ */
@ -24,9 +28,50 @@ enum E : C
x = C.init, x = C.init,
} }
enum B : bool
{
x,
}
struct SB
{
bool x;
}
struct SSB
{
SB sb;
}
void main() @safe void main() @safe
{ {
S s = void; S s = void;
C c = void; C c = void;
E e = void; E e = void;
const bool b = void;
B bb = void;
SB sb = void;
SSB ssb = void;
}
// The following test is reduced from Phobos. The compiler generates this `opAssign`:
// (CopyPreventer __swap2 = void;) , __swap2 = this , (this = p , __swap2.~this());
// The compiler would give an error about void initialization a struct with a bool,
// but it can be trusted in this case because it's a compiler generated temporary.
auto staticArray(T)(T a) @safe
{
T c;
c = a;
}
void assignmentTest() @safe
{
static struct CopyPreventer
{
bool on;
this(this) @safe {}
~this() { }
}
staticArray(CopyPreventer());
} }