d: Merge dmd, druntime d8e3976a58, phobos 7a6e95688
[official-gcc.git] / gcc / d / dmd / safe.d
blobaf81bff0dd1d22c2b271c72840d466d7ad6f27ae
1 /**
2 * Checks whether member access or array casting is allowed in `@safe` code.
4 * Specification: $(LINK2 https://dlang.org/spec/function.html#function-safety, Function Safety)
6 * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved
7 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
8 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/safe.d, _safe.d)
10 * Documentation: https://dlang.org/phobos/dmd_safe.html
11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/safe.d
14 module dmd.safe;
16 import core.stdc.stdio;
18 import dmd.aggregate;
19 import dmd.astenums;
20 import dmd.dclass;
21 import dmd.declaration;
22 import dmd.dscope;
23 import dmd.expression;
24 import dmd.id;
25 import dmd.identifier;
26 import dmd.mtype;
27 import dmd.target;
28 import dmd.tokens;
29 import dmd.func : setUnsafe, setUnsafePreview;
31 /*************************************************************
32 * Check for unsafe access in @safe code:
33 * 1. read overlapped pointers
34 * 2. write misaligned pointers
35 * 3. write overlapped storage classes
36 * Print error if unsafe.
37 * Params:
38 * sc = scope
39 * e = expression to check
40 * readonly = if access is read-only
41 * printmsg = print error message if true
42 * Returns:
43 * true if error
46 bool checkUnsafeAccess(Scope* sc, Expression e, bool readonly, bool printmsg)
48 //printf("checkUnsafeAccess(e: '%s', readonly: %d, printmsg: %d)\n", e.toChars(), readonly, printmsg);
49 if (e.op != EXP.dotVariable)
50 return false;
51 DotVarExp dve = cast(DotVarExp)e;
52 if (VarDeclaration v = dve.var.isVarDeclaration())
54 if (!sc.func)
55 return false;
56 auto ad = v.isMember2();
57 if (!ad)
58 return false;
60 import dmd.globals : global;
61 if (v.isSystem())
63 if (sc.setUnsafePreview(global.params.systemVariables, !printmsg, e.loc,
64 "cannot access `@system` field `%s.%s` in `@safe` code", ad, v))
65 return true;
68 // This branch shouldn't be here, but unfortunately calling `ad.determineSize`
69 // breaks code with circular reference errors. Specifically, test23589.d fails
70 if (ad.sizeok != Sizeok.done && !sc.func.isSafeBypassingInference())
71 return false;
73 // needed to set v.overlapped and v.overlapUnsafe
74 if (ad.sizeok != Sizeok.done)
75 ad.determineSize(ad.loc);
77 const hasPointers = v.type.hasPointers();
78 if (hasPointers)
80 if (v.overlapped)
82 if (sc.func.isSafeBypassingInference() && sc.setUnsafe(!printmsg, e.loc,
83 "field `%s.%s` cannot access pointers in `@safe` code that overlap other fields", ad, v))
85 return true;
87 else
89 import dmd.globals : FeatureState;
90 // @@@DEPRECATED_2.116@@@
91 // https://issues.dlang.org/show_bug.cgi?id=20655
92 // Inferring `@system` because of union access breaks code,
93 // so make it a deprecation safety violation as of 2.106
94 // To turn into an error, remove `isSafeBypassingInference` check in the
95 // above if statement and remove the else branch
96 sc.setUnsafePreview(FeatureState.default_, !printmsg, e.loc,
97 "field `%s.%s` cannot access pointers in `@safe` code that overlap other fields", ad, v);
102 if (v.type.hasInvariant())
104 if (v.overlapped)
106 if (sc.setUnsafe(!printmsg, e.loc,
107 "field `%s.%s` cannot access structs with invariants in `@safe` code that overlap other fields",
108 ad, v))
109 return true;
113 if (readonly || !e.type.isMutable())
114 return false;
116 if (hasPointers && v.type.toBasetype().ty != Tstruct)
118 if ((!ad.type.alignment.isDefault() && ad.type.alignment.get() < target.ptrsize ||
119 (v.offset & (target.ptrsize - 1))))
121 if (sc.setUnsafe(!printmsg, e.loc,
122 "field `%s.%s` cannot modify misaligned pointers in `@safe` code", ad, v))
123 return true;
127 if (v.overlapUnsafe)
129 if (sc.setUnsafe(!printmsg, e.loc,
130 "field `%s.%s` cannot modify fields in `@safe` code that overlap fields with other storage classes",
131 ad, v))
133 return true;
137 return false;
141 /**********************************************
142 * Determine if it is @safe to cast e from tfrom to tto.
143 * Params:
144 * e = expression to be cast
145 * tfrom = type of e
146 * tto = type to cast e to
147 * Returns:
148 * true if @safe
150 bool isSafeCast(Expression e, Type tfrom, Type tto)
152 // Implicit conversions are always safe
153 if (tfrom.implicitConvTo(tto))
154 return true;
156 if (!tto.hasPointers())
157 return true;
159 auto tfromb = tfrom.toBasetype();
160 auto ttob = tto.toBasetype();
162 if (ttob.ty == Tclass && tfromb.ty == Tclass)
164 ClassDeclaration cdfrom = tfromb.isClassHandle();
165 ClassDeclaration cdto = ttob.isClassHandle();
167 int offset;
168 if (!cdfrom.isBaseOf(cdto, &offset) &&
169 !((cdfrom.isInterfaceDeclaration() || cdto.isInterfaceDeclaration())
170 && cdfrom.classKind == ClassKind.d && cdto.classKind == ClassKind.d))
171 return false;
173 if (cdfrom.isCPPinterface() || cdto.isCPPinterface())
174 return false;
176 if (!MODimplicitConv(tfromb.mod, ttob.mod))
177 return false;
178 return true;
181 if (ttob.ty == Tarray && tfromb.ty == Tsarray) // https://issues.dlang.org/show_bug.cgi?id=12502
182 tfromb = tfromb.nextOf().arrayOf();
184 if (ttob.ty == Tarray && tfromb.ty == Tarray ||
185 ttob.ty == Tpointer && tfromb.ty == Tpointer)
187 Type ttobn = ttob.nextOf().toBasetype();
188 Type tfromn = tfromb.nextOf().toBasetype();
190 /* From void[] to anything mutable is unsafe because:
191 * int*[] api;
192 * void[] av = api;
193 * int[] ai = cast(int[]) av;
194 * ai[0] = 7;
195 * *api[0] crash!
197 if (tfromn.ty == Tvoid && ttobn.isMutable())
199 if (ttob.ty == Tarray && e.op == EXP.arrayLiteral)
200 return true;
201 return false;
204 // If the struct is opaque we don't know about the struct members then the cast becomes unsafe
205 if (ttobn.ty == Tstruct && !(cast(TypeStruct)ttobn).sym.members ||
206 tfromn.ty == Tstruct && !(cast(TypeStruct)tfromn).sym.members)
207 return false;
209 const frompointers = tfromn.hasPointers();
210 const topointers = ttobn.hasPointers();
212 if (frompointers && !topointers && ttobn.isMutable())
213 return false;
215 if (!frompointers && topointers)
216 return false;
218 if (!topointers &&
219 ttobn.ty != Tfunction && tfromn.ty != Tfunction &&
220 (ttob.ty == Tarray || ttobn.size() <= tfromn.size()) &&
221 MODimplicitConv(tfromn.mod, ttobn.mod))
223 return true;
226 return false;
229 /*************************************************
230 * Check for unsafe use of `.ptr` or `.funcptr`
231 * Params:
232 * sc = context
233 * e = expression for error messages
234 * id = `ptr` or `funcptr`
235 * flag = DotExpFlag
236 * Returns:
237 * true if error
239 bool checkUnsafeDotExp(Scope* sc, Expression e, Identifier id, int flag)
241 if (!(flag & DotExpFlag.noDeref)) // this use is attempting a dereference
243 if (id == Id.ptr)
244 return sc.setUnsafe(false, e.loc, "`%s.ptr` cannot be used in `@safe` code, use `&%s[0]` instead", e, e);
245 else
246 return sc.setUnsafe(false, e.loc, "`%s.%s` cannot be used in `@safe` code", e, id);
248 return false;