@@ -2352,26 +2352,78 @@ static void handleUnusedAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
2352
2352
D->addAttr (::new (S.Context ) UnusedAttr (S.Context , AL));
2353
2353
}
2354
2354
2355
- static void handleConstructorAttr (Sema &S, Decl *D, const ParsedAttr &AL) {
2356
- uint32_t priority = ConstructorAttr::DefaultPriority;
2355
+ static bool diagnoseInvalidPriority (Sema &S, uint32_t Priority,
2356
+ const ParsedAttr &A,
2357
+ SourceLocation PriorityLoc) {
2358
+ constexpr uint32_t ReservedPriorityLower = 101 , ReservedPriorityUpper = 65535 ;
2359
+
2360
+ // Only perform the priority check if the attribute is outside of a system
2361
+ // header. Values <= 100 are reserved for the implementation, and libc++
2362
+ // benefits from being able to specify values in that range. Values > 65535
2363
+ // are reserved for historical reasons.
2364
+ if ((Priority < ReservedPriorityLower || Priority > ReservedPriorityUpper) &&
2365
+ !S.getSourceManager ().isInSystemHeader (A.getLoc ())) {
2366
+ S.Diag (A.getLoc (), diag::err_attribute_argument_out_of_range)
2367
+ << PriorityLoc << A << ReservedPriorityLower << ReservedPriorityUpper;
2368
+ A.setInvalid ();
2369
+ return true ;
2370
+ }
2371
+ return false ;
2372
+ }
2373
+
2374
+ template <typename CtorDtorAttr>
2375
+ static void handleCtorDtorAttr (Sema &S, Decl *D, const ParsedAttr &AL) {
2376
+ uint32_t Priority = CtorDtorAttr::DefaultPriority;
2357
2377
if (S.getLangOpts ().HLSL && AL.getNumArgs ()) {
2358
2378
S.Diag (AL.getLoc (), diag::err_hlsl_init_priority_unsupported);
2359
2379
return ;
2360
2380
}
2361
- if (AL.getNumArgs () &&
2362
- !checkUInt32Argument (S, AL, AL.getArgAsExpr (0 ), priority))
2363
- return ;
2364
2381
2365
- D->addAttr (::new (S.Context ) ConstructorAttr (S.Context , AL, priority));
2366
- }
2382
+ // If we're given an argument for the priority, check that it's valid.
2383
+ if (AL.getNumArgs ()) {
2384
+ if (!checkUInt32Argument (S, AL, AL.getArgAsExpr (0 ), Priority))
2385
+ return ;
2367
2386
2368
- static void handleDestructorAttr (Sema &S, Decl *D, const ParsedAttr &AL) {
2369
- uint32_t priority = DestructorAttr::DefaultPriority;
2370
- if (AL.getNumArgs () &&
2371
- !checkUInt32Argument (S, AL, AL.getArgAsExpr (0 ), priority))
2387
+ // Ensure the priority is in a reasonable range.
2388
+ if (diagnoseInvalidPriority (S, Priority, AL,
2389
+ AL.getArgAsExpr (0 )->getExprLoc ()))
2390
+ return ;
2391
+ }
2392
+
2393
+ // Ensure the function we're attaching to is something that is sensible to
2394
+ // automatically call before or after main(); it should accept no arguments.
2395
+ // In theory, a void return type is the only truly safe return type (consider
2396
+ // that calling conventions may place returned values in a hidden pointer
2397
+ // argument passed to the function that will not be present when called
2398
+ // automatically). However, there is a significant amount of existing code
2399
+ // which uses an int return type. So we will accept void, int, and
2400
+ // unsigned int return types. Any other return type, or a non-void parameter
2401
+ // list is treated as an error because it's a form of type system
2402
+ // incompatibility. The function also cannot be a member function. We allow
2403
+ // K&R C functions because that's a difficult edge case where it depends on
2404
+ // how the function is defined as to whether it does or does not expect
2405
+ // arguments.
2406
+ const auto *FD = cast<FunctionDecl>(D);
2407
+ QualType RetTy = FD->getReturnType ();
2408
+ if (!(RetTy->isVoidType () ||
2409
+ RetTy->isSpecificBuiltinType (BuiltinType::UInt) ||
2410
+ RetTy->isSpecificBuiltinType (BuiltinType::Int)) ||
2411
+ (FD->hasPrototype () && FD->getNumParams () != 0 )) {
2412
+ S.Diag (AL.getLoc (), diag::err_ctor_dtor_attr_on_non_void_func)
2413
+ << AL << FD->getSourceRange ();
2372
2414
return ;
2415
+ } else if (const auto *MD = dyn_cast<CXXMethodDecl>(FD);
2416
+ MD && MD->isInstance ()) {
2417
+ S.Diag (AL.getLoc (), diag::err_ctor_dtor_member_func)
2418
+ << AL << FD->getSourceRange ();
2419
+ return ;
2420
+ } else if (FD->isConsteval ()) {
2421
+ S.Diag (AL.getLoc (), diag::err_ctordtor_attr_consteval)
2422
+ << AL << FD->getSourceRange ();
2423
+ return ;
2424
+ }
2373
2425
2374
- D->addAttr (:: new (S.Context ) DestructorAttr (S. Context , AL, priority ));
2426
+ D->addAttr (CtorDtorAttr::Create (S.Context , Priority, AL ));
2375
2427
}
2376
2428
2377
2429
template <typename AttrTy>
@@ -3888,16 +3940,9 @@ static void handleInitPriorityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
3888
3940
return ;
3889
3941
}
3890
3942
3891
- // Only perform the priority check if the attribute is outside of a system
3892
- // header. Values <= 100 are reserved for the implementation, and libc++
3893
- // benefits from being able to specify values in that range.
3894
- if ((prioritynum < 101 || prioritynum > 65535 ) &&
3895
- !S.getSourceManager ().isInSystemHeader (AL.getLoc ())) {
3896
- S.Diag (AL.getLoc (), diag::err_attribute_argument_out_of_range)
3897
- << E->getSourceRange () << AL << 101 << 65535 ;
3898
- AL.setInvalid ();
3943
+ if (diagnoseInvalidPriority (S, prioritynum, AL, E->getExprLoc ()))
3899
3944
return ;
3900
- }
3945
+
3901
3946
D->addAttr (::new (S.Context ) InitPriorityAttr (S.Context , AL, prioritynum));
3902
3947
}
3903
3948
@@ -8930,13 +8975,13 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
8930
8975
handlePassObjectSizeAttr (S, D, AL);
8931
8976
break ;
8932
8977
case ParsedAttr::AT_Constructor:
8933
- handleConstructorAttr (S, D, AL);
8978
+ handleCtorDtorAttr<ConstructorAttr> (S, D, AL);
8934
8979
break ;
8935
8980
case ParsedAttr::AT_Deprecated:
8936
8981
handleDeprecatedAttr (S, D, AL);
8937
8982
break ;
8938
8983
case ParsedAttr::AT_Destructor:
8939
- handleDestructorAttr (S, D, AL);
8984
+ handleCtorDtorAttr<DestructorAttr> (S, D, AL);
8940
8985
break ;
8941
8986
case ParsedAttr::AT_EnableIf:
8942
8987
handleEnableIfAttr (S, D, AL);
0 commit comments