1 | //===------- Interp.cpp - Interpreter for the constexpr VM ------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "Interp.h" |
10 | #include "Function.h" |
11 | #include "InterpFrame.h" |
12 | #include "InterpShared.h" |
13 | #include "InterpStack.h" |
14 | #include "Opcode.h" |
15 | #include "PrimType.h" |
16 | #include "Program.h" |
17 | #include "State.h" |
18 | #include "clang/AST/ASTContext.h" |
19 | #include "clang/AST/ASTDiagnostic.h" |
20 | #include "clang/AST/CXXInheritance.h" |
21 | #include "clang/AST/DeclObjC.h" |
22 | #include "clang/AST/Expr.h" |
23 | #include "clang/AST/ExprCXX.h" |
24 | #include "llvm/ADT/APSInt.h" |
25 | #include "llvm/ADT/StringExtras.h" |
26 | #include <limits> |
27 | #include <vector> |
28 | |
29 | using namespace clang; |
30 | |
31 | using namespace clang; |
32 | using namespace clang::interp; |
33 | |
34 | static bool RetValue(InterpState &S, CodePtr &Pt, APValue &Result) { |
35 | llvm::report_fatal_error(reason: "Interpreter cannot return values" ); |
36 | } |
37 | |
38 | //===----------------------------------------------------------------------===// |
39 | // Jmp, Jt, Jf |
40 | //===----------------------------------------------------------------------===// |
41 | |
42 | static bool Jmp(InterpState &S, CodePtr &PC, int32_t Offset) { |
43 | PC += Offset; |
44 | return true; |
45 | } |
46 | |
47 | static bool Jt(InterpState &S, CodePtr &PC, int32_t Offset) { |
48 | if (S.Stk.pop<bool>()) { |
49 | PC += Offset; |
50 | } |
51 | return true; |
52 | } |
53 | |
54 | static bool Jf(InterpState &S, CodePtr &PC, int32_t Offset) { |
55 | if (!S.Stk.pop<bool>()) { |
56 | PC += Offset; |
57 | } |
58 | return true; |
59 | } |
60 | |
61 | static void diagnoseMissingInitializer(InterpState &S, CodePtr OpPC, |
62 | const ValueDecl *VD) { |
63 | const SourceInfo &E = S.Current->getSource(PC: OpPC); |
64 | S.FFDiag(SI: E, DiagId: diag::note_constexpr_var_init_unknown, ExtraNotes: 1) << VD; |
65 | S.Note(Loc: VD->getLocation(), DiagId: diag::note_declared_at) << VD->getSourceRange(); |
66 | } |
67 | |
68 | static void diagnoseNonConstVariable(InterpState &S, CodePtr OpPC, |
69 | const ValueDecl *VD); |
70 | static bool diagnoseUnknownDecl(InterpState &S, CodePtr OpPC, |
71 | const ValueDecl *D) { |
72 | const SourceInfo &E = S.Current->getSource(PC: OpPC); |
73 | |
74 | if (isa<ParmVarDecl>(Val: D)) { |
75 | if (S.getLangOpts().CPlusPlus11) { |
76 | S.FFDiag(SI: E, DiagId: diag::note_constexpr_function_param_value_unknown) << D; |
77 | S.Note(Loc: D->getLocation(), DiagId: diag::note_declared_at) << D->getSourceRange(); |
78 | } else { |
79 | S.FFDiag(SI: E); |
80 | } |
81 | return false; |
82 | } |
83 | |
84 | if (!D->getType().isConstQualified()) |
85 | diagnoseNonConstVariable(S, OpPC, VD: D); |
86 | else if (const auto *VD = dyn_cast<VarDecl>(Val: D); |
87 | VD && !VD->getAnyInitializer()) |
88 | diagnoseMissingInitializer(S, OpPC, VD); |
89 | |
90 | return false; |
91 | } |
92 | |
93 | static void diagnoseNonConstVariable(InterpState &S, CodePtr OpPC, |
94 | const ValueDecl *VD) { |
95 | if (!S.getLangOpts().CPlusPlus) |
96 | return; |
97 | |
98 | const SourceInfo &Loc = S.Current->getSource(PC: OpPC); |
99 | if (const auto *VarD = dyn_cast<VarDecl>(Val: VD); |
100 | VarD && VarD->getType().isConstQualified() && |
101 | !VarD->getAnyInitializer()) { |
102 | diagnoseMissingInitializer(S, OpPC, VD); |
103 | return; |
104 | } |
105 | |
106 | // Rather random, but this is to match the diagnostic output of the current |
107 | // interpreter. |
108 | if (isa<ObjCIvarDecl>(Val: VD)) |
109 | return; |
110 | |
111 | if (VD->getType()->isIntegralOrEnumerationType()) { |
112 | S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_ltor_non_const_int, ExtraNotes: 1) << VD; |
113 | S.Note(Loc: VD->getLocation(), DiagId: diag::note_declared_at); |
114 | return; |
115 | } |
116 | |
117 | S.FFDiag(SI: Loc, |
118 | DiagId: S.getLangOpts().CPlusPlus11 ? diag::note_constexpr_ltor_non_constexpr |
119 | : diag::note_constexpr_ltor_non_integral, |
120 | ExtraNotes: 1) |
121 | << VD << VD->getType(); |
122 | S.Note(Loc: VD->getLocation(), DiagId: diag::note_declared_at); |
123 | } |
124 | |
125 | static bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr, |
126 | AccessKinds AK) { |
127 | if (Ptr.isActive()) |
128 | return true; |
129 | |
130 | // Get the inactive field descriptor. |
131 | const FieldDecl *InactiveField = Ptr.getField(); |
132 | |
133 | // Walk up the pointer chain to find the union which is not active. |
134 | Pointer U = Ptr.getBase(); |
135 | while (!U.isActive()) { |
136 | U = U.getBase(); |
137 | } |
138 | |
139 | // Find the active field of the union. |
140 | const Record *R = U.getRecord(); |
141 | assert(R && R->isUnion() && "Not a union" ); |
142 | const FieldDecl *ActiveField = nullptr; |
143 | for (unsigned I = 0, N = R->getNumFields(); I < N; ++I) { |
144 | const Pointer &Field = U.atField(Off: R->getField(I)->Offset); |
145 | if (Field.isActive()) { |
146 | ActiveField = Field.getField(); |
147 | break; |
148 | } |
149 | } |
150 | |
151 | const SourceInfo &Loc = S.Current->getSource(PC: OpPC); |
152 | S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_access_inactive_union_member) |
153 | << AK << InactiveField << !ActiveField << ActiveField; |
154 | return false; |
155 | } |
156 | |
157 | static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Pointer &Ptr, |
158 | AccessKinds AK) { |
159 | if (auto ID = Ptr.getDeclID()) { |
160 | if (!Ptr.isStaticTemporary()) |
161 | return true; |
162 | |
163 | if (Ptr.getDeclDesc()->getType().isConstQualified()) |
164 | return true; |
165 | |
166 | if (S.P.getCurrentDecl() == ID) |
167 | return true; |
168 | |
169 | const SourceInfo &E = S.Current->getSource(PC: OpPC); |
170 | S.FFDiag(SI: E, DiagId: diag::note_constexpr_access_static_temporary, ExtraNotes: 1) << AK; |
171 | S.Note(Loc: Ptr.getDeclLoc(), DiagId: diag::note_constexpr_temporary_here); |
172 | return false; |
173 | } |
174 | return true; |
175 | } |
176 | |
177 | static bool CheckGlobal(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { |
178 | if (auto ID = Ptr.getDeclID()) { |
179 | if (!Ptr.isStatic()) |
180 | return true; |
181 | |
182 | if (S.P.getCurrentDecl() == ID) |
183 | return true; |
184 | |
185 | S.FFDiag(Loc: S.Current->getLocation(PC: OpPC), DiagId: diag::note_constexpr_modify_global); |
186 | return false; |
187 | } |
188 | return true; |
189 | } |
190 | |
191 | namespace clang { |
192 | namespace interp { |
193 | static void popArg(InterpState &S, const Expr *Arg) { |
194 | PrimType Ty = S.getContext().classify(E: Arg).value_or(u: PT_Ptr); |
195 | TYPE_SWITCH(Ty, S.Stk.discard<T>()); |
196 | } |
197 | |
198 | void cleanupAfterFunctionCall(InterpState &S, CodePtr OpPC) { |
199 | assert(S.Current); |
200 | const Function *CurFunc = S.Current->getFunction(); |
201 | assert(CurFunc); |
202 | |
203 | if (CurFunc->isUnevaluatedBuiltin()) |
204 | return; |
205 | |
206 | // Some builtin functions require us to only look at the call site, since |
207 | // the classified parameter types do not match. |
208 | if (CurFunc->isBuiltin()) { |
209 | const auto *CE = |
210 | cast<CallExpr>(Val: S.Current->Caller->getExpr(PC: S.Current->getRetPC())); |
211 | for (int32_t I = CE->getNumArgs() - 1; I >= 0; --I) { |
212 | const Expr *A = CE->getArg(Arg: I); |
213 | popArg(S, Arg: A); |
214 | } |
215 | return; |
216 | } |
217 | |
218 | if (S.Current->Caller && CurFunc->isVariadic()) { |
219 | // CallExpr we're look for is at the return PC of the current function, i.e. |
220 | // in the caller. |
221 | // This code path should be executed very rarely. |
222 | unsigned NumVarArgs; |
223 | const Expr *const *Args = nullptr; |
224 | unsigned NumArgs = 0; |
225 | const Expr *CallSite = S.Current->Caller->getExpr(PC: S.Current->getRetPC()); |
226 | if (const auto *CE = dyn_cast<CallExpr>(Val: CallSite)) { |
227 | Args = CE->getArgs(); |
228 | NumArgs = CE->getNumArgs(); |
229 | } else if (const auto *CE = dyn_cast<CXXConstructExpr>(Val: CallSite)) { |
230 | Args = CE->getArgs(); |
231 | NumArgs = CE->getNumArgs(); |
232 | } else |
233 | assert(false && "Can't get arguments from that expression type" ); |
234 | |
235 | assert(NumArgs >= CurFunc->getNumWrittenParams()); |
236 | NumVarArgs = NumArgs - CurFunc->getNumWrittenParams(); |
237 | for (unsigned I = 0; I != NumVarArgs; ++I) { |
238 | const Expr *A = Args[NumArgs - 1 - I]; |
239 | popArg(S, Arg: A); |
240 | } |
241 | } |
242 | |
243 | // And in any case, remove the fixed parameters (the non-variadic ones) |
244 | // at the end. |
245 | S.Current->popArgs(); |
246 | } |
247 | |
248 | bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { |
249 | if (!Ptr.isExtern()) |
250 | return true; |
251 | |
252 | if (Ptr.isInitialized() || |
253 | (Ptr.getDeclDesc()->asVarDecl() == S.EvaluatingDecl)) |
254 | return true; |
255 | |
256 | if (!S.checkingPotentialConstantExpression() && S.getLangOpts().CPlusPlus) { |
257 | const auto *VD = Ptr.getDeclDesc()->asValueDecl(); |
258 | diagnoseNonConstVariable(S, OpPC, VD); |
259 | } |
260 | return false; |
261 | } |
262 | |
263 | bool CheckArray(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { |
264 | if (!Ptr.isUnknownSizeArray()) |
265 | return true; |
266 | const SourceInfo &E = S.Current->getSource(PC: OpPC); |
267 | S.FFDiag(SI: E, DiagId: diag::note_constexpr_unsized_array_indexed); |
268 | return false; |
269 | } |
270 | |
271 | bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr, |
272 | AccessKinds AK) { |
273 | if (Ptr.isZero()) { |
274 | const auto &Src = S.Current->getSource(PC: OpPC); |
275 | |
276 | if (Ptr.isField()) |
277 | S.FFDiag(SI: Src, DiagId: diag::note_constexpr_null_subobject) << CSK_Field; |
278 | else |
279 | S.FFDiag(SI: Src, DiagId: diag::note_constexpr_access_null) << AK; |
280 | |
281 | return false; |
282 | } |
283 | |
284 | if (!Ptr.isLive()) { |
285 | const auto &Src = S.Current->getSource(PC: OpPC); |
286 | bool IsTemp = Ptr.isTemporary(); |
287 | |
288 | S.FFDiag(SI: Src, DiagId: diag::note_constexpr_lifetime_ended, ExtraNotes: 1) << AK << !IsTemp; |
289 | |
290 | if (IsTemp) |
291 | S.Note(Loc: Ptr.getDeclLoc(), DiagId: diag::note_constexpr_temporary_here); |
292 | else |
293 | S.Note(Loc: Ptr.getDeclLoc(), DiagId: diag::note_declared_at); |
294 | |
295 | return false; |
296 | } |
297 | |
298 | return true; |
299 | } |
300 | |
301 | bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc) { |
302 | assert(Desc); |
303 | |
304 | auto IsConstType = [&S](const VarDecl *VD) -> bool { |
305 | if (VD->isConstexpr()) |
306 | return true; |
307 | |
308 | QualType T = VD->getType(); |
309 | if (S.getLangOpts().CPlusPlus && !S.getLangOpts().CPlusPlus11) |
310 | return (T->isSignedIntegerOrEnumerationType() || |
311 | T->isUnsignedIntegerOrEnumerationType()) && |
312 | T.isConstQualified(); |
313 | |
314 | if (T.isConstQualified()) |
315 | return true; |
316 | |
317 | if (const auto *RT = T->getAs<ReferenceType>()) |
318 | return RT->getPointeeType().isConstQualified(); |
319 | |
320 | if (const auto *PT = T->getAs<PointerType>()) |
321 | return PT->getPointeeType().isConstQualified(); |
322 | |
323 | return false; |
324 | }; |
325 | |
326 | if (const auto *D = Desc->asVarDecl(); |
327 | D && D->hasGlobalStorage() && D != S.EvaluatingDecl && !IsConstType(D)) { |
328 | diagnoseNonConstVariable(S, OpPC, VD: D); |
329 | return S.inConstantContext(); |
330 | } |
331 | |
332 | return true; |
333 | } |
334 | |
335 | static bool CheckConstant(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { |
336 | if (Ptr.isIntegralPointer()) |
337 | return true; |
338 | return CheckConstant(S, OpPC, Desc: Ptr.getDeclDesc()); |
339 | } |
340 | |
341 | bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr, |
342 | CheckSubobjectKind CSK) { |
343 | if (!Ptr.isZero()) |
344 | return true; |
345 | const SourceInfo &Loc = S.Current->getSource(PC: OpPC); |
346 | S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_null_subobject) |
347 | << CSK << S.Current->getRange(PC: OpPC); |
348 | |
349 | return false; |
350 | } |
351 | |
352 | bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr, |
353 | AccessKinds AK) { |
354 | if (!Ptr.isOnePastEnd()) |
355 | return true; |
356 | const SourceInfo &Loc = S.Current->getSource(PC: OpPC); |
357 | S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_access_past_end) |
358 | << AK << S.Current->getRange(PC: OpPC); |
359 | return false; |
360 | } |
361 | |
362 | bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr, |
363 | CheckSubobjectKind CSK) { |
364 | if (!Ptr.isElementPastEnd()) |
365 | return true; |
366 | const SourceInfo &Loc = S.Current->getSource(PC: OpPC); |
367 | S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_past_end_subobject) |
368 | << CSK << S.Current->getRange(PC: OpPC); |
369 | return false; |
370 | } |
371 | |
372 | bool CheckSubobject(InterpState &S, CodePtr OpPC, const Pointer &Ptr, |
373 | CheckSubobjectKind CSK) { |
374 | if (!Ptr.isOnePastEnd()) |
375 | return true; |
376 | |
377 | const SourceInfo &Loc = S.Current->getSource(PC: OpPC); |
378 | S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_past_end_subobject) |
379 | << CSK << S.Current->getRange(PC: OpPC); |
380 | return false; |
381 | } |
382 | |
383 | bool CheckDowncast(InterpState &S, CodePtr OpPC, const Pointer &Ptr, |
384 | uint32_t Offset) { |
385 | uint32_t MinOffset = Ptr.getDeclDesc()->getMetadataSize(); |
386 | uint32_t PtrOffset = Ptr.getByteOffset(); |
387 | |
388 | // We subtract Offset from PtrOffset. The result must be at least |
389 | // MinOffset. |
390 | if (Offset < PtrOffset && (PtrOffset - Offset) >= MinOffset) |
391 | return true; |
392 | |
393 | const auto *E = cast<CastExpr>(Val: S.Current->getExpr(PC: OpPC)); |
394 | QualType TargetQT = E->getType()->getPointeeType(); |
395 | QualType MostDerivedQT = Ptr.getDeclPtr().getType(); |
396 | |
397 | S.CCEDiag(E, DiagId: diag::note_constexpr_invalid_downcast) |
398 | << MostDerivedQT << TargetQT; |
399 | |
400 | return false; |
401 | } |
402 | |
403 | bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { |
404 | assert(Ptr.isLive() && "Pointer is not live" ); |
405 | if (!Ptr.isConst() || Ptr.isMutable()) |
406 | return true; |
407 | |
408 | // The This pointer is writable in constructors and destructors, |
409 | // even if isConst() returns true. |
410 | // TODO(perf): We could be hitting this code path quite a lot in complex |
411 | // constructors. Is there a better way to do this? |
412 | if (S.Current->getFunction()) { |
413 | for (const InterpFrame *Frame = S.Current; Frame; Frame = Frame->Caller) { |
414 | if (const Function *Func = Frame->getFunction(); |
415 | Func && (Func->isConstructor() || Func->isDestructor()) && |
416 | Ptr.block() == Frame->getThis().block()) { |
417 | return true; |
418 | } |
419 | } |
420 | } |
421 | |
422 | if (!Ptr.isBlockPointer()) |
423 | return false; |
424 | |
425 | const QualType Ty = Ptr.getType(); |
426 | const SourceInfo &Loc = S.Current->getSource(PC: OpPC); |
427 | S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_modify_const_type) << Ty; |
428 | return false; |
429 | } |
430 | |
431 | bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { |
432 | assert(Ptr.isLive() && "Pointer is not live" ); |
433 | if (!Ptr.isMutable()) |
434 | return true; |
435 | |
436 | // In C++14 onwards, it is permitted to read a mutable member whose |
437 | // lifetime began within the evaluation. |
438 | if (S.getLangOpts().CPlusPlus14 && |
439 | Ptr.block()->getEvalID() == S.Ctx.getEvalID()) |
440 | return true; |
441 | |
442 | const SourceInfo &Loc = S.Current->getSource(PC: OpPC); |
443 | const FieldDecl *Field = Ptr.getField(); |
444 | S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_access_mutable, ExtraNotes: 1) << AK_Read << Field; |
445 | S.Note(Loc: Field->getLocation(), DiagId: diag::note_declared_at); |
446 | return false; |
447 | } |
448 | |
449 | bool CheckVolatile(InterpState &S, CodePtr OpPC, const Pointer &Ptr, |
450 | AccessKinds AK) { |
451 | assert(Ptr.isLive()); |
452 | |
453 | // FIXME: This check here might be kinda expensive. Maybe it would be better |
454 | // to have another field in InlineDescriptor for this? |
455 | if (!Ptr.isBlockPointer()) |
456 | return true; |
457 | |
458 | QualType PtrType = Ptr.getType(); |
459 | if (!PtrType.isVolatileQualified()) |
460 | return true; |
461 | |
462 | const SourceInfo &Loc = S.Current->getSource(PC: OpPC); |
463 | if (S.getLangOpts().CPlusPlus) |
464 | S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_access_volatile_type) << AK << PtrType; |
465 | else |
466 | S.FFDiag(SI: Loc); |
467 | return false; |
468 | } |
469 | |
470 | bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr, |
471 | AccessKinds AK) { |
472 | assert(Ptr.isLive()); |
473 | |
474 | if (Ptr.isInitialized()) |
475 | return true; |
476 | |
477 | if (const auto *VD = Ptr.getDeclDesc()->asVarDecl(); |
478 | VD && VD->hasGlobalStorage()) { |
479 | const SourceInfo &Loc = S.Current->getSource(PC: OpPC); |
480 | if (VD->getAnyInitializer()) { |
481 | S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_var_init_non_constant, ExtraNotes: 1) << VD; |
482 | S.Note(Loc: VD->getLocation(), DiagId: diag::note_declared_at); |
483 | } else { |
484 | diagnoseMissingInitializer(S, OpPC, VD); |
485 | } |
486 | return false; |
487 | } |
488 | |
489 | if (!S.checkingPotentialConstantExpression()) { |
490 | S.FFDiag(SI: S.Current->getSource(PC: OpPC), DiagId: diag::note_constexpr_access_uninit) |
491 | << AK << /*uninitialized=*/true << S.Current->getRange(PC: OpPC); |
492 | } |
493 | return false; |
494 | } |
495 | |
496 | bool CheckGlobalInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { |
497 | if (Ptr.isInitialized()) |
498 | return true; |
499 | |
500 | assert(S.getLangOpts().CPlusPlus); |
501 | const auto *VD = cast<VarDecl>(Val: Ptr.getDeclDesc()->asValueDecl()); |
502 | if ((!VD->hasConstantInitialization() && |
503 | VD->mightBeUsableInConstantExpressions(C: S.getCtx())) || |
504 | (S.getLangOpts().OpenCL && !S.getLangOpts().CPlusPlus11 && |
505 | !VD->hasICEInitializer(Context: S.getCtx()))) { |
506 | const SourceInfo &Loc = S.Current->getSource(PC: OpPC); |
507 | S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_var_init_non_constant, ExtraNotes: 1) << VD; |
508 | S.Note(Loc: VD->getLocation(), DiagId: diag::note_declared_at); |
509 | } |
510 | return false; |
511 | } |
512 | |
513 | bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr, |
514 | AccessKinds AK) { |
515 | if (!CheckLive(S, OpPC, Ptr, AK)) |
516 | return false; |
517 | if (!CheckConstant(S, OpPC, Ptr)) |
518 | return false; |
519 | |
520 | if (!CheckDummy(S, OpPC, Ptr, AK)) |
521 | return false; |
522 | if (!CheckExtern(S, OpPC, Ptr)) |
523 | return false; |
524 | if (!CheckRange(S, OpPC, Ptr, AK)) |
525 | return false; |
526 | if (!CheckActive(S, OpPC, Ptr, AK)) |
527 | return false; |
528 | if (!CheckInitialized(S, OpPC, Ptr, AK)) |
529 | return false; |
530 | if (!CheckTemporary(S, OpPC, Ptr, AK)) |
531 | return false; |
532 | if (!CheckMutable(S, OpPC, Ptr)) |
533 | return false; |
534 | if (!CheckVolatile(S, OpPC, Ptr, AK)) |
535 | return false; |
536 | return true; |
537 | } |
538 | |
539 | bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { |
540 | if (!CheckLive(S, OpPC, Ptr, AK: AK_Assign)) |
541 | return false; |
542 | if (!CheckDummy(S, OpPC, Ptr, AK: AK_Assign)) |
543 | return false; |
544 | if (!CheckExtern(S, OpPC, Ptr)) |
545 | return false; |
546 | if (!CheckRange(S, OpPC, Ptr, AK: AK_Assign)) |
547 | return false; |
548 | if (!CheckGlobal(S, OpPC, Ptr)) |
549 | return false; |
550 | if (!CheckConst(S, OpPC, Ptr)) |
551 | return false; |
552 | return true; |
553 | } |
554 | |
555 | bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { |
556 | if (!CheckLive(S, OpPC, Ptr, AK: AK_MemberCall)) |
557 | return false; |
558 | if (!Ptr.isDummy()) { |
559 | if (!CheckExtern(S, OpPC, Ptr)) |
560 | return false; |
561 | if (!CheckRange(S, OpPC, Ptr, AK: AK_MemberCall)) |
562 | return false; |
563 | } |
564 | return true; |
565 | } |
566 | |
567 | bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { |
568 | if (!CheckLive(S, OpPC, Ptr, AK: AK_Assign)) |
569 | return false; |
570 | if (!CheckRange(S, OpPC, Ptr, AK: AK_Assign)) |
571 | return false; |
572 | return true; |
573 | } |
574 | |
575 | bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) { |
576 | |
577 | if (F->isVirtual() && !S.getLangOpts().CPlusPlus20) { |
578 | const SourceLocation &Loc = S.Current->getLocation(PC: OpPC); |
579 | S.CCEDiag(Loc, DiagId: diag::note_constexpr_virtual_call); |
580 | return false; |
581 | } |
582 | |
583 | if (F->isConstexpr() && F->hasBody() && |
584 | (F->getDecl()->isConstexpr() || F->getDecl()->hasAttr<MSConstexprAttr>())) |
585 | return true; |
586 | |
587 | // Implicitly constexpr. |
588 | if (F->isLambdaStaticInvoker()) |
589 | return true; |
590 | |
591 | const SourceLocation &Loc = S.Current->getLocation(PC: OpPC); |
592 | if (S.getLangOpts().CPlusPlus11) { |
593 | const FunctionDecl *DiagDecl = F->getDecl(); |
594 | |
595 | // Invalid decls have been diagnosed before. |
596 | if (DiagDecl->isInvalidDecl()) |
597 | return false; |
598 | |
599 | // If this function is not constexpr because it is an inherited |
600 | // non-constexpr constructor, diagnose that directly. |
601 | const auto *CD = dyn_cast<CXXConstructorDecl>(Val: DiagDecl); |
602 | if (CD && CD->isInheritingConstructor()) { |
603 | const auto *Inherited = CD->getInheritedConstructor().getConstructor(); |
604 | if (!Inherited->isConstexpr()) |
605 | DiagDecl = CD = Inherited; |
606 | } |
607 | |
608 | // FIXME: If DiagDecl is an implicitly-declared special member function |
609 | // or an inheriting constructor, we should be much more explicit about why |
610 | // it's not constexpr. |
611 | if (CD && CD->isInheritingConstructor()) { |
612 | S.FFDiag(Loc, DiagId: diag::note_constexpr_invalid_inhctor, ExtraNotes: 1) |
613 | << CD->getInheritedConstructor().getConstructor()->getParent(); |
614 | S.Note(Loc: DiagDecl->getLocation(), DiagId: diag::note_declared_at); |
615 | } else { |
616 | // Don't emit anything if the function isn't defined and we're checking |
617 | // for a constant expression. It might be defined at the point we're |
618 | // actually calling it. |
619 | bool IsExtern = DiagDecl->getStorageClass() == SC_Extern; |
620 | if (!DiagDecl->isDefined() && !IsExtern && DiagDecl->isConstexpr() && |
621 | S.checkingPotentialConstantExpression()) |
622 | return false; |
623 | |
624 | // If the declaration is defined, declared 'constexpr' _and_ has a body, |
625 | // the below diagnostic doesn't add anything useful. |
626 | if (DiagDecl->isDefined() && DiagDecl->isConstexpr() && |
627 | DiagDecl->hasBody()) |
628 | return false; |
629 | |
630 | S.FFDiag(Loc, DiagId: diag::note_constexpr_invalid_function, ExtraNotes: 1) |
631 | << DiagDecl->isConstexpr() << (bool)CD << DiagDecl; |
632 | S.Note(Loc: DiagDecl->getLocation(), DiagId: diag::note_declared_at); |
633 | } |
634 | } else { |
635 | S.FFDiag(Loc, DiagId: diag::note_invalid_subexpr_in_const_expr); |
636 | } |
637 | |
638 | return false; |
639 | } |
640 | |
641 | bool CheckCallDepth(InterpState &S, CodePtr OpPC) { |
642 | if ((S.Current->getDepth() + 1) > S.getLangOpts().ConstexprCallDepth) { |
643 | S.FFDiag(SI: S.Current->getSource(PC: OpPC), |
644 | DiagId: diag::note_constexpr_depth_limit_exceeded) |
645 | << S.getLangOpts().ConstexprCallDepth; |
646 | return false; |
647 | } |
648 | |
649 | return true; |
650 | } |
651 | |
652 | bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This) { |
653 | if (!This.isZero()) |
654 | return true; |
655 | |
656 | const SourceInfo &Loc = S.Current->getSource(PC: OpPC); |
657 | |
658 | bool IsImplicit = false; |
659 | if (const auto *E = dyn_cast_if_present<CXXThisExpr>(Val: Loc.asExpr())) |
660 | IsImplicit = E->isImplicit(); |
661 | |
662 | if (S.getLangOpts().CPlusPlus11) |
663 | S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_this) << IsImplicit; |
664 | else |
665 | S.FFDiag(SI: Loc); |
666 | |
667 | return false; |
668 | } |
669 | |
670 | bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD) { |
671 | if (!MD->isPureVirtual()) |
672 | return true; |
673 | const SourceInfo &E = S.Current->getSource(PC: OpPC); |
674 | S.FFDiag(SI: E, DiagId: diag::note_constexpr_pure_virtual_call, ExtraNotes: 1) << MD; |
675 | S.Note(Loc: MD->getLocation(), DiagId: diag::note_declared_at); |
676 | return false; |
677 | } |
678 | |
679 | bool CheckFloatResult(InterpState &S, CodePtr OpPC, const Floating &Result, |
680 | APFloat::opStatus Status) { |
681 | const SourceInfo &E = S.Current->getSource(PC: OpPC); |
682 | |
683 | // [expr.pre]p4: |
684 | // If during the evaluation of an expression, the result is not |
685 | // mathematically defined [...], the behavior is undefined. |
686 | // FIXME: C++ rules require us to not conform to IEEE 754 here. |
687 | if (Result.isNan()) { |
688 | S.CCEDiag(SI: E, DiagId: diag::note_constexpr_float_arithmetic) |
689 | << /*NaN=*/true << S.Current->getRange(PC: OpPC); |
690 | return S.noteUndefinedBehavior(); |
691 | } |
692 | |
693 | // In a constant context, assume that any dynamic rounding mode or FP |
694 | // exception state matches the default floating-point environment. |
695 | if (S.inConstantContext()) |
696 | return true; |
697 | |
698 | FPOptions FPO = E.asExpr()->getFPFeaturesInEffect(LO: S.Ctx.getLangOpts()); |
699 | |
700 | if ((Status & APFloat::opInexact) && |
701 | FPO.getRoundingMode() == llvm::RoundingMode::Dynamic) { |
702 | // Inexact result means that it depends on rounding mode. If the requested |
703 | // mode is dynamic, the evaluation cannot be made in compile time. |
704 | S.FFDiag(SI: E, DiagId: diag::note_constexpr_dynamic_rounding); |
705 | return false; |
706 | } |
707 | |
708 | if ((Status != APFloat::opOK) && |
709 | (FPO.getRoundingMode() == llvm::RoundingMode::Dynamic || |
710 | FPO.getExceptionMode() != LangOptions::FPE_Ignore || |
711 | FPO.getAllowFEnvAccess())) { |
712 | S.FFDiag(SI: E, DiagId: diag::note_constexpr_float_arithmetic_strict); |
713 | return false; |
714 | } |
715 | |
716 | if ((Status & APFloat::opStatus::opInvalidOp) && |
717 | FPO.getExceptionMode() != LangOptions::FPE_Ignore) { |
718 | // There is no usefully definable result. |
719 | S.FFDiag(SI: E); |
720 | return false; |
721 | } |
722 | |
723 | return true; |
724 | } |
725 | |
726 | bool CheckDynamicMemoryAllocation(InterpState &S, CodePtr OpPC) { |
727 | if (S.getLangOpts().CPlusPlus20) |
728 | return true; |
729 | |
730 | const SourceInfo &E = S.Current->getSource(PC: OpPC); |
731 | S.CCEDiag(SI: E, DiagId: diag::note_constexpr_new); |
732 | return true; |
733 | } |
734 | |
735 | bool CheckNewDeleteForms(InterpState &S, CodePtr OpPC, bool NewWasArray, |
736 | bool DeleteIsArray, const Descriptor *D, |
737 | const Expr *NewExpr) { |
738 | if (NewWasArray == DeleteIsArray) |
739 | return true; |
740 | |
741 | QualType TypeToDiagnose; |
742 | // We need to shuffle things around a bit here to get a better diagnostic, |
743 | // because the expression we allocated the block for was of type int*, |
744 | // but we want to get the array size right. |
745 | if (D->isArray()) { |
746 | QualType ElemQT = D->getType()->getPointeeType(); |
747 | TypeToDiagnose = S.getCtx().getConstantArrayType( |
748 | EltTy: ElemQT, ArySize: APInt(64, static_cast<uint64_t>(D->getNumElems()), false), |
749 | SizeExpr: nullptr, ASM: ArraySizeModifier::Normal, IndexTypeQuals: 0); |
750 | } else |
751 | TypeToDiagnose = D->getType()->getPointeeType(); |
752 | |
753 | const SourceInfo &E = S.Current->getSource(PC: OpPC); |
754 | S.FFDiag(SI: E, DiagId: diag::note_constexpr_new_delete_mismatch) |
755 | << DeleteIsArray << 0 << TypeToDiagnose; |
756 | S.Note(Loc: NewExpr->getExprLoc(), DiagId: diag::note_constexpr_dynamic_alloc_here) |
757 | << NewExpr->getSourceRange(); |
758 | return false; |
759 | } |
760 | |
761 | bool CheckDeleteSource(InterpState &S, CodePtr OpPC, const Expr *Source, |
762 | const Pointer &Ptr) { |
763 | if (Source && isa<CXXNewExpr>(Val: Source)) |
764 | return true; |
765 | |
766 | // Whatever this is, we didn't heap allocate it. |
767 | const SourceInfo &Loc = S.Current->getSource(PC: OpPC); |
768 | S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_delete_not_heap_alloc) |
769 | << Ptr.toDiagnosticString(Ctx: S.getCtx()); |
770 | |
771 | if (Ptr.isTemporary()) |
772 | S.Note(Loc: Ptr.getDeclLoc(), DiagId: diag::note_constexpr_temporary_here); |
773 | else |
774 | S.Note(Loc: Ptr.getDeclLoc(), DiagId: diag::note_declared_at); |
775 | return false; |
776 | } |
777 | |
778 | /// We aleady know the given DeclRefExpr is invalid for some reason, |
779 | /// now figure out why and print appropriate diagnostics. |
780 | bool CheckDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR) { |
781 | const ValueDecl *D = DR->getDecl(); |
782 | return diagnoseUnknownDecl(S, OpPC, D); |
783 | } |
784 | |
785 | bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr, |
786 | AccessKinds AK) { |
787 | if (!Ptr.isDummy()) |
788 | return true; |
789 | |
790 | const Descriptor *Desc = Ptr.getDeclDesc(); |
791 | const ValueDecl *D = Desc->asValueDecl(); |
792 | if (!D) |
793 | return false; |
794 | |
795 | if (AK == AK_Read || AK == AK_Increment || AK == AK_Decrement) |
796 | return diagnoseUnknownDecl(S, OpPC, D); |
797 | |
798 | assert(AK == AK_Assign); |
799 | if (S.getLangOpts().CPlusPlus11) { |
800 | const SourceInfo &E = S.Current->getSource(PC: OpPC); |
801 | S.FFDiag(SI: E, DiagId: diag::note_constexpr_modify_global); |
802 | } |
803 | return false; |
804 | } |
805 | |
806 | bool CheckNonNullArgs(InterpState &S, CodePtr OpPC, const Function *F, |
807 | const CallExpr *CE, unsigned ArgSize) { |
808 | auto Args = llvm::ArrayRef(CE->getArgs(), CE->getNumArgs()); |
809 | auto NonNullArgs = collectNonNullArgs(F: F->getDecl(), Args); |
810 | unsigned Offset = 0; |
811 | unsigned Index = 0; |
812 | for (const Expr *Arg : Args) { |
813 | if (NonNullArgs[Index] && Arg->getType()->isPointerType()) { |
814 | const Pointer &ArgPtr = S.Stk.peek<Pointer>(Offset: ArgSize - Offset); |
815 | if (ArgPtr.isZero()) { |
816 | const SourceLocation &Loc = S.Current->getLocation(PC: OpPC); |
817 | S.CCEDiag(Loc, DiagId: diag::note_non_null_attribute_failed); |
818 | return false; |
819 | } |
820 | } |
821 | |
822 | Offset += align(Size: primSize(Type: S.Ctx.classify(E: Arg).value_or(u: PT_Ptr))); |
823 | ++Index; |
824 | } |
825 | return true; |
826 | } |
827 | |
828 | // FIXME: This is similar to code we already have in Compiler.cpp. |
829 | // I think it makes sense to instead add the field and base destruction stuff |
830 | // to the destructor Function itself. Then destroying a record would really |
831 | // _just_ be calling its destructor. That would also help with the diagnostic |
832 | // difference when the destructor or a field/base fails. |
833 | static bool runRecordDestructor(InterpState &S, CodePtr OpPC, |
834 | const Pointer &BasePtr, |
835 | const Descriptor *Desc) { |
836 | assert(Desc->isRecord()); |
837 | const Record *R = Desc->ElemRecord; |
838 | assert(R); |
839 | |
840 | // Fields. |
841 | for (const Record::Field &Field : llvm::reverse(C: R->fields())) { |
842 | const Descriptor *D = Field.Desc; |
843 | if (D->isRecord()) { |
844 | if (!runRecordDestructor(S, OpPC, BasePtr: BasePtr.atField(Off: Field.Offset), Desc: D)) |
845 | return false; |
846 | } else if (D->isCompositeArray()) { |
847 | const Descriptor *ElemDesc = Desc->ElemDesc; |
848 | assert(ElemDesc->isRecord()); |
849 | for (unsigned I = 0; I != Desc->getNumElems(); ++I) { |
850 | if (!runRecordDestructor(S, OpPC, BasePtr: BasePtr.atIndex(Idx: I).narrow(), |
851 | Desc: ElemDesc)) |
852 | return false; |
853 | } |
854 | } |
855 | } |
856 | |
857 | // Destructor of this record. |
858 | if (const CXXDestructorDecl *Dtor = R->getDestructor(); |
859 | Dtor && !Dtor->isTrivial()) { |
860 | const Function *DtorFunc = S.getContext().getOrCreateFunction(FD: Dtor); |
861 | if (!DtorFunc) |
862 | return false; |
863 | |
864 | S.Stk.push<Pointer>(Args: BasePtr); |
865 | if (!Call(S, OpPC, Func: DtorFunc, VarArgSize: 0)) |
866 | return false; |
867 | } |
868 | |
869 | // Bases. |
870 | for (const Record::Base &Base : llvm::reverse(C: R->bases())) { |
871 | if (!runRecordDestructor(S, OpPC, BasePtr: BasePtr.atField(Off: Base.Offset), Desc: Base.Desc)) |
872 | return false; |
873 | } |
874 | |
875 | return true; |
876 | } |
877 | |
878 | bool RunDestructors(InterpState &S, CodePtr OpPC, const Block *B) { |
879 | assert(B); |
880 | const Descriptor *Desc = B->getDescriptor(); |
881 | |
882 | if (Desc->isPrimitive() || Desc->isPrimitiveArray()) |
883 | return true; |
884 | |
885 | assert(Desc->isRecord() || Desc->isCompositeArray()); |
886 | |
887 | if (Desc->isCompositeArray()) { |
888 | const Descriptor *ElemDesc = Desc->ElemDesc; |
889 | assert(ElemDesc->isRecord()); |
890 | |
891 | Pointer RP(const_cast<Block *>(B)); |
892 | for (unsigned I = 0; I != Desc->getNumElems(); ++I) { |
893 | if (!runRecordDestructor(S, OpPC, BasePtr: RP.atIndex(Idx: I).narrow(), Desc: ElemDesc)) |
894 | return false; |
895 | } |
896 | return true; |
897 | } |
898 | |
899 | assert(Desc->isRecord()); |
900 | return runRecordDestructor(S, OpPC, BasePtr: Pointer(const_cast<Block *>(B)), Desc); |
901 | } |
902 | |
903 | void diagnoseEnumValue(InterpState &S, CodePtr OpPC, const EnumDecl *ED, |
904 | const APSInt &Value) { |
905 | llvm::APInt Min; |
906 | llvm::APInt Max; |
907 | |
908 | if (S.EvaluatingDecl && !S.EvaluatingDecl->isConstexpr()) |
909 | return; |
910 | |
911 | ED->getValueRange(Max, Min); |
912 | --Max; |
913 | |
914 | if (ED->getNumNegativeBits() && |
915 | (Max.slt(RHS: Value.getSExtValue()) || Min.sgt(RHS: Value.getSExtValue()))) { |
916 | const SourceLocation &Loc = S.Current->getLocation(PC: OpPC); |
917 | S.report(Loc, DiagId: diag::warn_constexpr_unscoped_enum_out_of_range) |
918 | << llvm::toString(I: Value, Radix: 10) << Min.getSExtValue() << Max.getSExtValue() |
919 | << ED; |
920 | } else if (!ED->getNumNegativeBits() && Max.ult(RHS: Value.getZExtValue())) { |
921 | const SourceLocation &Loc = S.Current->getLocation(PC: OpPC); |
922 | S.report(Loc, DiagId: diag::warn_constexpr_unscoped_enum_out_of_range) |
923 | << llvm::toString(I: Value, Radix: 10) << Min.getZExtValue() << Max.getZExtValue() |
924 | << ED; |
925 | } |
926 | } |
927 | |
928 | bool Interpret(InterpState &S, APValue &Result) { |
929 | // The current stack frame when we started Interpret(). |
930 | // This is being used by the ops to determine wheter |
931 | // to return from this function and thus terminate |
932 | // interpretation. |
933 | const InterpFrame *StartFrame = S.Current; |
934 | assert(!S.Current->isRoot()); |
935 | CodePtr PC = S.Current->getPC(); |
936 | |
937 | // Empty program. |
938 | if (!PC) |
939 | return true; |
940 | |
941 | for (;;) { |
942 | auto Op = PC.read<Opcode>(); |
943 | CodePtr OpPC = PC; |
944 | |
945 | switch (Op) { |
946 | #define GET_INTERP |
947 | #include "Opcodes.inc" |
948 | #undef GET_INTERP |
949 | } |
950 | } |
951 | } |
952 | |
953 | } // namespace interp |
954 | } // namespace clang |
955 | |