@@ -2352,26 +2352,126 @@ 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 void 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::warn_priority_out_of_range)
2367
+ << PriorityLoc << A << ReservedPriorityLower << ReservedPriorityUpper;
2368
+ }
2369
+ }
2370
+
2371
+ static bool FunctionParamsAreMainLike (ASTContext &Context,
2372
+ const FunctionDecl *FD) {
2373
+ assert (FD->hasPrototype () && " expected the function to have a prototype" );
2374
+ const auto *FPT = FD->getType ()->castAs <FunctionProtoType>();
2375
+ QualType CharPP =
2376
+ Context.getPointerType (Context.getPointerType (Context.CharTy ));
2377
+ QualType Expected[] = {Context.IntTy , CharPP, CharPP, CharPP};
2378
+ for (unsigned I = 0 ;
2379
+ I < sizeof (Expected) / sizeof (QualType) && I < FPT->getNumParams ();
2380
+ ++I) {
2381
+ QualType AT = FPT->getParamType (I);
2382
+
2383
+ if (!Context.hasSameUnqualifiedType (AT, Expected[I])) {
2384
+ if (Expected[I] == CharPP) {
2385
+ // As an extension, the following forms are okay:
2386
+ // char const **
2387
+ // char const * const *
2388
+ // char * const *
2389
+
2390
+ QualifierCollector Qs;
2391
+ const PointerType *PT;
2392
+ if ((PT = Qs.strip (AT)->getAs <PointerType>()) &&
2393
+ (PT = Qs.strip (PT->getPointeeType ())->getAs <PointerType>()) &&
2394
+ Context.hasSameType (QualType (Qs.strip (PT->getPointeeType ()), 0 ),
2395
+ Context.CharTy )) {
2396
+ Qs.removeConst ();
2397
+ if (!Qs.empty ())
2398
+ return false ;
2399
+ continue ; // Accepted as an extension.
2400
+ }
2401
+ }
2402
+ return false ;
2403
+ }
2404
+ }
2405
+ return true ;
2406
+ }
2407
+
2408
+ template <typename CtorDtorAttr>
2409
+ static void handleCtorDtorAttr (Sema &S, Decl *D, const ParsedAttr &AL) {
2410
+ uint32_t Priority = CtorDtorAttr::DefaultPriority;
2357
2411
if (S.getLangOpts ().HLSL && AL.getNumArgs ()) {
2358
2412
S.Diag (AL.getLoc (), diag::err_hlsl_init_priority_unsupported);
2359
2413
return ;
2360
2414
}
2361
- if (AL.getNumArgs () &&
2362
- !checkUInt32Argument (S, AL, AL.getArgAsExpr (0 ), priority))
2363
- return ;
2364
2415
2365
- D->addAttr (::new (S.Context ) ConstructorAttr (S.Context , AL, priority));
2366
- }
2416
+ // If we're given an argument for the priority, check that it's valid.
2417
+ if (AL.getNumArgs ()) {
2418
+ if (!checkUInt32Argument (S, AL, AL.getArgAsExpr (0 ), Priority))
2419
+ return ;
2420
+
2421
+ // Diagnose an invalid priority, but continue to process the attribute.
2422
+ diagnoseInvalidPriority (S, Priority, AL, AL.getArgAsExpr (0 )->getExprLoc ());
2423
+ }
2367
2424
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))
2425
+ // Ensure the function we're attaching to is something that is sensible to
2426
+ // automatically call before or after main(); it should accept no arguments.
2427
+ // In theory, a void return type is the only truly safe return type (consider
2428
+ // that calling conventions may place returned values in a hidden pointer
2429
+ // argument passed to the function that will not be present when called
2430
+ // automatically). However, there is a significant amount of existing code
2431
+ // which uses an int return type. So we will accept void, int, and
2432
+ // unsigned int return types. Any other return type, or a non-void parameter
2433
+ // list is treated as an error because it's a form of type system
2434
+ // incompatibility. The function also cannot be a member function. We allow
2435
+ // K&R C functions because that's a difficult edge case where it depends on
2436
+ // how the function is defined as to whether it does or does not expect
2437
+ // arguments.
2438
+ //
2439
+ // However! glibc on ELF will pass the same arguments to a constructor
2440
+ // function as are given to main(), so we will allow `int, char *[]` and
2441
+ // `int, char *[], char *[]` (or qualified versions thereof), but only if
2442
+ // the target is explicitly for glibc.
2443
+ const auto *FD = cast<FunctionDecl>(D);
2444
+ QualType RetTy = FD->getReturnType ();
2445
+ bool IsGlibC = S.Context .getTargetInfo ().getTriple ().isGNUEnvironment ();
2446
+ if (!(RetTy->isVoidType () ||
2447
+ RetTy->isSpecificBuiltinType (BuiltinType::UInt) ||
2448
+ RetTy->isSpecificBuiltinType (BuiltinType::Int)) ||
2449
+ FD->isVariadic () ||
2450
+ (FD->hasPrototype () &&
2451
+ ((!IsGlibC && FD->getNumParams () != 0 ) ||
2452
+ (IsGlibC && !FunctionParamsAreMainLike (S.Context , FD))))) {
2453
+ S.Diag (AL.getLoc (), diag::err_ctor_dtor_attr_on_non_void_func)
2454
+ << AL << FD->getSourceRange ();
2455
+ return ;
2456
+ }
2457
+ if (FD->getType ()->castAs <FunctionType>()->getCallConv () !=
2458
+ CallingConv::CC_C) {
2459
+ S.Diag (AL.getLoc (), diag::err_ctor_dtor_calling_conv)
2460
+ << AL << FD->getSourceRange ();
2461
+ return ;
2462
+ }
2463
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FD); MD && MD->isInstance ()) {
2464
+ S.Diag (AL.getLoc (), diag::err_ctor_dtor_member_func)
2465
+ << AL << FD->getSourceRange ();
2466
+ return ;
2467
+ }
2468
+ if (FD->isConsteval ()) {
2469
+ S.Diag (AL.getLoc (), diag::err_ctordtor_attr_consteval)
2470
+ << AL << FD->getSourceRange ();
2372
2471
return ;
2472
+ }
2373
2473
2374
- D->addAttr (:: new (S.Context ) DestructorAttr (S. Context , AL, priority ));
2474
+ D->addAttr (CtorDtorAttr::Create (S.Context , Priority, AL ));
2375
2475
}
2376
2476
2377
2477
template <typename AttrTy>
@@ -3888,16 +3988,9 @@ static void handleInitPriorityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
3888
3988
return ;
3889
3989
}
3890
3990
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 ();
3899
- return ;
3900
- }
3991
+ // Diagnose an invalid priority, but continue to process the attribute.
3992
+ diagnoseInvalidPriority (S, prioritynum, AL, E->getExprLoc ());
3993
+
3901
3994
D->addAttr (::new (S.Context ) InitPriorityAttr (S.Context , AL, prioritynum));
3902
3995
}
3903
3996
@@ -8959,13 +9052,13 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
8959
9052
handlePassObjectSizeAttr (S, D, AL);
8960
9053
break ;
8961
9054
case ParsedAttr::AT_Constructor:
8962
- handleConstructorAttr (S, D, AL);
9055
+ handleCtorDtorAttr<ConstructorAttr> (S, D, AL);
8963
9056
break ;
8964
9057
case ParsedAttr::AT_Deprecated:
8965
9058
handleDeprecatedAttr (S, D, AL);
8966
9059
break ;
8967
9060
case ParsedAttr::AT_Destructor:
8968
- handleDestructorAttr (S, D, AL);
9061
+ handleCtorDtorAttr<DestructorAttr> (S, D, AL);
8969
9062
break ;
8970
9063
case ParsedAttr::AT_EnableIf:
8971
9064
handleEnableIfAttr (S, D, AL);
0 commit comments