d: Merge upstream dmd 3982604c5, druntime bc58b1e9, phobos 12329adb6.
[official-gcc.git] / gcc / d / dmd / safe.d
blob7eb3e235e41f0aa861d1efa561184f871eeba7c2
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-2021 by The D Language Foundation, All Rights Reserved
7 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
8 * License: $(LINK2 http://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;
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.intypeof || !sc.func || !sc.func.isSafeBypassingInference())
55 return false;
56 auto ad = v.toParent2().isAggregateDeclaration();
57 if (!ad)
58 return false;
60 // needed to set v.overlapped and v.overlapUnsafe
61 if (ad.sizeok != Sizeok.done)
62 ad.determineSize(ad.loc);
64 const hasPointers = v.type.hasPointers();
65 if (hasPointers)
67 if (v.overlapped && sc.func.setUnsafe())
69 if (printmsg)
70 e.error("field `%s.%s` cannot access pointers in `@safe` code that overlap other fields",
71 ad.toChars(), v.toChars());
72 return true;
76 if (v.type.hasInvariant())
78 if (v.overlapped && sc.func.setUnsafe())
80 if (printmsg)
81 e.error("field `%s.%s` cannot access structs with invariants in `@safe` code that overlap other fields",
82 ad.toChars(), v.toChars());
83 return true;
87 if (readonly || !e.type.isMutable())
88 return false;
90 if (hasPointers && v.type.toBasetype().ty != Tstruct)
92 if ((!ad.type.alignment.isDefault() && ad.type.alignment.get() < target.ptrsize ||
93 (v.offset & (target.ptrsize - 1))) &&
94 sc.func.setUnsafe())
96 if (printmsg)
97 e.error("field `%s.%s` cannot modify misaligned pointers in `@safe` code",
98 ad.toChars(), v.toChars());
99 return true;
103 if (v.overlapUnsafe && sc.func.setUnsafe())
105 if (printmsg)
106 e.error("field `%s.%s` cannot modify fields in `@safe` code that overlap fields with other storage classes",
107 ad.toChars(), v.toChars());
108 return true;
111 return false;
115 /**********************************************
116 * Determine if it is @safe to cast e from tfrom to tto.
117 * Params:
118 * e = expression to be cast
119 * tfrom = type of e
120 * tto = type to cast e to
121 * Returns:
122 * true if @safe
124 bool isSafeCast(Expression e, Type tfrom, Type tto)
126 // Implicit conversions are always safe
127 if (tfrom.implicitConvTo(tto))
128 return true;
130 if (!tto.hasPointers())
131 return true;
133 auto tfromb = tfrom.toBasetype();
134 auto ttob = tto.toBasetype();
136 if (ttob.ty == Tclass && tfromb.ty == Tclass)
138 ClassDeclaration cdfrom = tfromb.isClassHandle();
139 ClassDeclaration cdto = ttob.isClassHandle();
141 int offset;
142 if (!cdfrom.isBaseOf(cdto, &offset) &&
143 !((cdfrom.isInterfaceDeclaration() || cdto.isInterfaceDeclaration())
144 && cdfrom.classKind == ClassKind.d && cdto.classKind == ClassKind.d))
145 return false;
147 if (cdfrom.isCPPinterface() || cdto.isCPPinterface())
148 return false;
150 if (!MODimplicitConv(tfromb.mod, ttob.mod))
151 return false;
152 return true;
155 if (ttob.ty == Tarray && tfromb.ty == Tsarray) // https://issues.dlang.org/show_bug.cgi?id=12502
156 tfromb = tfromb.nextOf().arrayOf();
158 if (ttob.ty == Tarray && tfromb.ty == Tarray ||
159 ttob.ty == Tpointer && tfromb.ty == Tpointer)
161 Type ttobn = ttob.nextOf().toBasetype();
162 Type tfromn = tfromb.nextOf().toBasetype();
164 /* From void[] to anything mutable is unsafe because:
165 * int*[] api;
166 * void[] av = api;
167 * int[] ai = cast(int[]) av;
168 * ai[0] = 7;
169 * *api[0] crash!
171 if (tfromn.ty == Tvoid && ttobn.isMutable())
173 if (ttob.ty == Tarray && e.op == EXP.arrayLiteral)
174 return true;
175 return false;
178 // If the struct is opaque we don't know about the struct members then the cast becomes unsafe
179 if (ttobn.ty == Tstruct && !(cast(TypeStruct)ttobn).sym.members ||
180 tfromn.ty == Tstruct && !(cast(TypeStruct)tfromn).sym.members)
181 return false;
183 const frompointers = tfromn.hasPointers();
184 const topointers = ttobn.hasPointers();
186 if (frompointers && !topointers && ttobn.isMutable())
187 return false;
189 if (!frompointers && topointers)
190 return false;
192 if (!topointers &&
193 ttobn.ty != Tfunction && tfromn.ty != Tfunction &&
194 (ttob.ty == Tarray || ttobn.size() <= tfromn.size()) &&
195 MODimplicitConv(tfromn.mod, ttobn.mod))
197 return true;
200 return false;
203 /*************************************************
204 * Check for unsafe use of `.ptr` or `.funcptr`
205 * Params:
206 * sc = context
207 * e = expression for error messages
208 * id = `ptr` or `funcptr`
209 * flag = DotExpFlag
210 * Returns:
211 * true if error
213 bool checkUnsafeDotExp(Scope* sc, Expression e, Identifier id, int flag)
215 if (!(flag & DotExpFlag.noDeref) && // this use is attempting a dereference
216 sc.func && // inside a function
217 !sc.intypeof && // allow unsafe code in typeof expressions
218 !(sc.flags & SCOPE.debug_) && // allow unsafe code in debug statements
219 sc.func.setUnsafe()) // infer this function to be unsafe
221 if (id == Id.ptr)
222 e.error("`%s.ptr` cannot be used in `@safe` code, use `&%s[0]` instead", e.toChars(), e.toChars());
223 else
224 e.error("`%s.%s` cannot be used in `@safe` code", e.toChars(), id.toChars());
225 return true;
227 return false;