This is an archive of the discontinued LLVM Phabricator instance.

[clang] Add a note "deducing return type for 'foo'"
Needs ReviewPublic

Authored by Quuxplusone on Feb 14 2022, 2:00 PM.

Details

Summary

Emit the note when we fail to deduce a return type, or deduce conflicting return types.

Diff Detail

Event Timeline

Quuxplusone requested review of this revision.Feb 14 2022, 2:00 PM
Quuxplusone created this revision.

I am curious about the behavior if we removed the guard if (OrigResultType.getBeginLoc().isValid()).

clang/lib/Sema/SemaStmt.cpp
3804–3807

What if merge this one into above block where the diagnostic happens.

Rebase; adjust more expected output in tests.

clang/lib/Sema/SemaStmt.cpp
3805

I am curious about the behavior if we removed the guard if (OrigResultType.getBeginLoc().isValid()).

That caused the note to be emitted in some cases without a source location (similar to https://github.com/llvm/llvm-project/issues/29054 ). I admit I should check to make sure we have a test case for each of these four ifs, though; I suspect we don't.

$ cat x.cpp
auto f = []() { return x; };

$ clang++ x.cpp
x.cpp:1:24: error: use of undeclared identifier 'x'
auto f = []() { return x; };
                       ^
note: deducing return type for 'operator()'
1 error generated.
rsmith added inline comments.Feb 15 2022, 5:52 PM
clang/lib/Sema/SemaStmt.cpp
3804–3809

The approach of tacking a note onto the end of someone else's diagnostic is an antipattern and should be avoided in new code. For example, if DeduceAutoType produces an error (which is shown) then a warning (which is hidden), the note will be attached to the warning, and will not appear.

The right thing to do is to create a new kind of CodeSynthesisContext for this case and push that. Then we'll use the same logic as in template instantiation backtraces, and the note will get properly attached to the first emitted diagnostic within the context. This will also remove the need to track down all the different places that emit diagnostics (like we see below): you just push the context when you start doing the action you want to attach the note to and pop it when you're done. It'll also properly order the note with respect to other context notes.

I'll update with the CodeSynthesisContext approach in a little bit. But meanwhile there's a problem with both the current patch and (I think even more) with the new approach. @rsmith any help?:
When I try my latest patch with the libc++ tests, with bin/llvm-lit -sv --param enable_modules=True (which corresponds to the -fmodules compiler flag), I get lots of errors like the following. Do you have any idea what's going wrong?

I can't minimize the example beyond "all of libc++" because Modules prevents creduce from doing anything meaningful. And unfortunately whatever's going wrong seems to be specifically about Modules.

Assertion failed: (Replacement.isCanonical() && "replacement types must always be canonical"), function getSubstTemplateTypeParmType, file /Users/aodwyer/llvm-project/clang/lib/AST/ASTContext.cpp, line 4691.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.	Program arguments: /Users/aodwyer/llvm-project/build3/bin/clang-14 -cc1 -triple x86_64-apple-macosx10.15.0 -Wundef-prefix=TARGET_OS_ -Werror=undef-prefix -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -emit-obj -mrelax-all --mrelax-relocations -disable-free -clear-ast-before-backend -main-file-name rbegin.pass.cpp -mrelocation-model pic -pic-level 2 -mframe-pointer=all -ffp-contract=on -fno-rounding-math -funwind-tables=2 -target-sdk-version=10.15.6 -fcompatibility-qualified-id-block-type-checking -fvisibility-inlines-hidden-static-local-var -target-cpu penryn -tune-cpu generic -mllvm -treat-scalable-fixed-error-as-warning -debugger-tuning=lldb -target-linker-version 609.8 -v -fcoverage-compilation-dir=/Users/aodwyer/llvm-project/build3/projects/libcxx/test/std/ranges/range.access -nostdinc++ -resource-dir /Users/aodwyer/llvm-project/build3/lib/clang/15.0.0 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk -isystem /Users/aodwyer/llvm-project/build3/include/c++/v1 -I /Users/aodwyer/llvm-project/build3/projects/libcxx/include/c++build -I /Users/aodwyer/llvm-project/libcxx/test/support -D _LIBCPP_DISABLE_AVAILABILITY -D _LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -stdlib=libc++ -internal-isystem /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/local/include -internal-isystem /Users/aodwyer/llvm-project/build3/lib/clang/15.0.0/include -internal-externc-isystem /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/include -Werror -Wall -Wextra -Wshadow -Wundef -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-c++11-extensions -Wno-user-defined-literals -Wno-noexcept-type -Wno-atomic-alignment -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -Werror=thread-safety -Wuser-defined-warnings -std=c++20 -fdeprecated-macro -fdebug-compilation-dir=/Users/aodwyer/llvm-project/build3/projects/libcxx/test/std/ranges/range.access -ferror-limit 19 -stack-protector 1 -fblocks -fencode-extended-block-signature -fcoroutines-ts -fregister-global-dtors-with-atexit -fgnuc-version=4.2.1 -fmodules -fimplicit-module-maps -fmodules-cache-path=/var/folders/0l/9t0yv2890_g4wgmy53n_mg7w0000gy/C/clang/ModuleCache -fmodules-validate-system-headers -fcxx-exceptions -fexceptions -fmax-type-align=16 -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /var/folders/0l/9t0yv2890_g4wgmy53n_mg7w0000gy/T/lit-tmp-fkh9o_f4/rbegin-d58043.o -x c++ /Users/aodwyer/llvm-project/libcxx/test/std/ranges/range.access/rbegin.pass.cpp
1.	/Users/aodwyer/llvm-project/libcxx/test/support/test_iterators.h:524:30: current parser token '{'
Stack dump without symbol names (ensure you have llvm-symbolizer in your PATH or set the environment var `LLVM_SYMBOLIZER_PATH` to point to it):
0  clang-14                 0x000000010634385d llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 61
1  clang-14                 0x0000000106343ddb PrintStackTraceSignalHandler(void*) + 27
2  clang-14                 0x0000000106341cfa llvm::sys::RunSignalHandlers() + 138
3  clang-14                 0x00000001063457af SignalHandler(int) + 223
4  libsystem_platform.dylib 0x00007fff677255fd _sigtramp + 29
5  libsystem_platform.dylib 0x00007ffeed912f58 _sigtramp + 18446744071664753016
6  libsystem_c.dylib        0x00007fff675fb808 abort + 120
7  libsystem_c.dylib        0x00007fff675faac6 err + 0
8  clang-14                 0x000000010b769ce1 clang::ASTContext::getSubstTemplateTypeParmType(clang::TemplateTypeParmType const*, clang::QualType) const + 161
9  clang-14                 0x000000010b1feb40 (anonymous namespace)::TemplateInstantiator::TransformTemplateTypeParmType(clang::TypeLocBuilder&, clang::TemplateTypeParmTypeLoc) + 1456
10 clang-14                 0x000000010b1ba74f clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformType(clang::TypeLocBuilder&, clang::TypeLoc) + 4383
11 clang-14                 0x000000010b1b91e2 clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformType(clang::TypeSourceInfo*) + 354
12 clang-14                 0x000000010b1e9137 clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformTemplateArgument(clang::TemplateArgumentLoc const&, clang::TemplateArgumentLoc&, bool) + 1079
13 clang-14                 0x000000010b1e8c4f bool clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformTemplateArguments<clang::TemplateArgumentLocInventIterator<(anonymous namespace)::TemplateInstantiator, clang::TemplateArgument const*> >(clang::TemplateArgumentLocInventIterator<(anonymous namespace)::TemplateInstantiator, clang::TemplateArgument const*>, clang::TemplateArgumentLocInventIterator<(anonymous namespace)::TemplateInstantiator, clang::TemplateArgument const*>, clang::TemplateArgumentListInfo&, bool) + 2447
14 clang-14                 0x000000010b1e7981 bool clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformTemplateArguments<clang::TemplateArgumentLocContainerIterator<clang::TemplateSpecializationTypeLoc> >(clang::TemplateArgumentLocContainerIterator<clang::TemplateSpecializationTypeLoc>, clang::TemplateArgumentLocContainerIterator<clang::TemplateSpecializationTypeLoc>, clang::TemplateArgumentListInfo&, bool) + 289
15 clang-14                 0x000000010b1e6e3e clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformTemplateSpecializationType(clang::TypeLocBuilder&, clang::TemplateSpecializationTypeLoc, clang::TemplateName) + 286
16 clang-14                 0x000000010b1e6a7b clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformTSIInObjectScope(clang::TypeLoc, clang::QualType, clang::NamedDecl*, clang::CXXScopeSpec&) + 523
17 clang-14                 0x000000010b21c0c5 clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformTypeInObjectScope(clang::TypeLoc, clang::QualType, clang::NamedDecl*, clang::CXXScopeSpec&) + 165
18 clang-14                 0x000000010b1c88a5 clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformNestedNameSpecifierLoc(clang::NestedNameSpecifierLoc, clang::QualType, clang::NamedDecl*) + 1365
19 clang-14                 0x000000010b1f5f55 clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformDependentNameType(clang::TypeLocBuilder&, clang::DependentNameTypeLoc, bool) + 149
20 clang-14                 0x000000010b1fa140 clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformDependentNameType(clang::TypeLocBuilder&, clang::DependentNameTypeLoc) + 64
21 clang-14                 0x000000010b1b9d4a clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformType(clang::TypeLocBuilder&, clang::TypeLoc) + 1818
22 clang-14                 0x000000010b1b91e2 clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformType(clang::TypeSourceInfo*) + 354
23 clang-14                 0x000000010b1bad21 clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformType(clang::QualType) + 161
24 clang-14                 0x000000010b1fdf9f clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformSubstTemplateTypeParmType(clang::TypeLocBuilder&, clang::SubstTemplateTypeParmTypeLoc) + 143
25 clang-14                 0x000000010b1ba5d3 clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformType(clang::TypeLocBuilder&, clang::TypeLoc) + 4003
26 clang-14                 0x000000010b1b91e2 clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformType(clang::TypeSourceInfo*) + 354
27 clang-14                 0x000000010b1e9137 clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformTemplateArgument(clang::TemplateArgumentLoc const&, clang::TemplateArgumentLoc&, bool) + 1079
28 clang-14                 0x000000010b1c543c bool clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformTemplateArguments<clang::TemplateArgumentLoc const*>(clang::TemplateArgumentLoc const*, clang::TemplateArgumentLoc const*, clang::TemplateArgumentListInfo&, bool) + 2428
29 clang-14                 0x000000010b1bc136 clang::Sema::SubstTemplateArguments(llvm::ArrayRef<clang::TemplateArgumentLoc>, clang::MultiLevelTemplateArgumentList const&, clang::TemplateArgumentListInfo&) + 182
30 clang-14                 0x000000010a4d15f6 substituteParameterMappings(clang::Sema&, clang::NormalizedConstraint&, clang::ConceptDecl*, llvm::ArrayRef<clang::TemplateArgument>, clang::ASTTemplateArgumentListInfo const*) + 1574
31 clang-14                 0x000000010a4d1088 substituteParameterMappings(clang::Sema&, clang::NormalizedConstraint&, clang::ConceptDecl*, llvm::ArrayRef<clang::TemplateArgument>, clang::ASTTemplateArgumentListInfo const*) + 184
32 clang-14                 0x000000010a4d1088 substituteParameterMappings(clang::Sema&, clang::NormalizedConstraint&, clang::ConceptDecl*, llvm::ArrayRef<clang::TemplateArgument>, clang::ASTTemplateArgumentListInfo const*) + 184
33 clang-14                 0x000000010a4d1088 substituteParameterMappings(clang::Sema&, clang::NormalizedConstraint&, clang::ConceptDecl*, llvm::ArrayRef<clang::TemplateArgument>, clang::ASTTemplateArgumentListInfo const*) + 184
34 clang-14                 0x000000010a4d0cf7 clang::NormalizedConstraint::fromConstraintExpr(clang::Sema&, clang::NamedDecl*, clang::Expr const*) + 1079
35 clang-14                 0x000000010a4d0a05 clang::NormalizedConstraint::fromConstraintExpr(clang::Sema&, clang::NamedDecl*, clang::Expr const*) + 325
36 clang-14                 0x000000010a4d099e clang::NormalizedConstraint::fromConstraintExpr(clang::Sema&, clang::NamedDecl*, clang::Expr const*) + 222
37 clang-14                 0x000000010a4d099e clang::NormalizedConstraint::fromConstraintExpr(clang::Sema&, clang::NamedDecl*, clang::Expr const*) + 222
38 clang-14                 0x000000010a4d03e4 clang::NormalizedConstraint::fromConstraintExprs(clang::Sema&, clang::NamedDecl*, llvm::ArrayRef<clang::Expr const*>) + 164
39 clang-14                 0x000000010a4cffa7 clang::Sema::getNormalizedAssociatedConstraints(clang::NamedDecl*, llvm::ArrayRef<clang::Expr const*>) + 151
40 clang-14                 0x000000010a4d0bec clang::NormalizedConstraint::fromConstraintExpr(clang::Sema&, clang::NamedDecl*, clang::Expr const*) + 812
41 clang-14                 0x000000010a4d03e4 clang::NormalizedConstraint::fromConstraintExprs(clang::Sema&, clang::NamedDecl*, llvm::ArrayRef<clang::Expr const*>) + 164
42 clang-14                 0x000000010a4cffa7 clang::Sema::getNormalizedAssociatedConstraints(clang::NamedDecl*, llvm::ArrayRef<clang::Expr const*>) + 151
43 clang-14                 0x000000010a4d0bec clang::NormalizedConstraint::fromConstraintExpr(clang::Sema&, clang::NamedDecl*, clang::Expr const*) + 812
44 clang-14                 0x000000010a4d0a05 clang::NormalizedConstraint::fromConstraintExpr(clang::Sema&, clang::NamedDecl*, clang::Expr const*) + 325
45 clang-14                 0x000000010a4d099e clang::NormalizedConstraint::fromConstraintExpr(clang::Sema&, clang::NamedDecl*, clang::Expr const*) + 222
46 clang-14                 0x000000010a4d099e clang::NormalizedConstraint::fromConstraintExpr(clang::Sema&, clang::NamedDecl*, clang::Expr const*) + 222
47 clang-14                 0x000000010a4d03e4 clang::NormalizedConstraint::fromConstraintExprs(clang::Sema&, clang::NamedDecl*, llvm::ArrayRef<clang::Expr const*>) + 164
48 clang-14                 0x000000010a4cffa7 clang::Sema::getNormalizedAssociatedConstraints(clang::NamedDecl*, llvm::ArrayRef<clang::Expr const*>) + 151
49 clang-14                 0x000000010a4d0bec clang::NormalizedConstraint::fromConstraintExpr(clang::Sema&, clang::NamedDecl*, clang::Expr const*) + 812
50 clang-14                 0x000000010a4d03e4 clang::NormalizedConstraint::fromConstraintExprs(clang::Sema&, clang::NamedDecl*, llvm::ArrayRef<clang::Expr const*>) + 164
51 clang-14                 0x000000010a4cffa7 clang::Sema::getNormalizedAssociatedConstraints(clang::NamedDecl*, llvm::ArrayRef<clang::Expr const*>) + 151
52 clang-14                 0x000000010a4d1d96 bool subsumes<clang::Sema::IsAtLeastAsConstrained(clang::NamedDecl*, llvm::ArrayRef<clang::Expr const*>, clang::NamedDecl*, llvm::ArrayRef<clang::Expr const*>, bool&)::$_2>(clang::Sema&, clang::NamedDecl*, llvm::ArrayRef<clang::Expr const*>, clang::NamedDecl*, llvm::ArrayRef<clang::Expr const*>, bool&, clang::Sema::IsAtLeastAsConstrained(clang::NamedDecl*, llvm::ArrayRef<clang::Expr const*>, clang::NamedDecl*, llvm::ArrayRef<clang::Expr const*>, bool&)::$_2) + 150
53 clang-14                 0x000000010a4d1a2b clang::Sema::IsAtLeastAsConstrained(clang::NamedDecl*, llvm::ArrayRef<clang::Expr const*>, clang::NamedDecl*, llvm::ArrayRef<clang::Expr const*>, bool&) + 411
54 clang-14                 0x000000010b0cfe2c clang::Sema::isMoreSpecializedThanPrimary(clang::ClassTemplatePartialSpecializationDecl*, clang::sema::TemplateDeductionInfo&) + 428
55 clang-14                 0x000000010b0758dd void checkMoreSpecializedThanPrimary<clang::ClassTemplatePartialSpecializationDecl>(clang::Sema&, clang::ClassTemplatePartialSpecializationDecl*) + 141
56 clang-14                 0x000000010af84a20 void checkTemplatePartialSpecialization<clang::ClassTemplatePartialSpecializationDecl>(clang::Sema&, clang::ClassTemplatePartialSpecializationDecl*) + 32
57 clang-14                 0x000000010af849ed clang::Sema::CheckTemplatePartialSpecialization(clang::ClassTemplatePartialSpecializationDecl*) + 29
58 clang-14                 0x000000010af932db clang::Sema::ActOnClassTemplateSpecialization(clang::Scope*, unsigned int, clang::Sema::TagUseKind, clang::SourceLocation, clang::SourceLocation, clang::CXXScopeSpec&, clang::TemplateIdAnnotation&, clang::ParsedAttributesView const&, llvm::MutableArrayRef<clang::TemplateParameterList*>, clang::Sema::SkipBodyInfo*) + 5195
59 clang-14                 0x0000000109f81f63 clang::Parser::ParseClassSpecifier(clang::tok::TokenKind, clang::SourceLocation, clang::DeclSpec&, clang::Parser::ParsedTemplateInfo const&, clang::AccessSpecifier, bool, clang::Parser::DeclSpecContext, clang::ParsedAttributesWithRange&) + 7187
60 clang-14                 0x0000000109f60058 clang::Parser::ParseDeclarationSpecifiers(clang::DeclSpec&, clang::Parser::ParsedTemplateInfo const&, clang::AccessSpecifier, clang::Parser::DeclSpecContext, clang::Parser::LateParsedAttrList*) + 17192
61 clang-14                 0x000000010a0336e9 clang::Parser::ParseSingleDeclarationAfterTemplate(clang::DeclaratorContext, clang::Parser::ParsedTemplateInfo const&, clang::ParsingDeclRAIIObject&, clang::SourceLocation&, clang::ParsedAttributes&, clang::AccessSpecifier) + 841
62 clang-14                 0x000000010a032a22 clang::Parser::ParseTemplateDeclarationOrSpecialization(clang::DeclaratorContext, clang::SourceLocation&, clang::ParsedAttributes&, clang::AccessSpecifier) + 1426
63 clang-14                 0x000000010a032354 clang::Parser::ParseDeclarationStartingWithTemplate(clang::DeclaratorContext, clang::SourceLocation&, clang::ParsedAttributes&, clang::AccessSpecifier) + 228

I'll update with the CodeSynthesisContext approach in a little bit. But meanwhile there's a problem with both the current patch and (I think even more) with the new approach. @rsmith any help?:
When I try my latest patch with the libc++ tests, with bin/llvm-lit -sv --param enable_modules=True (which corresponds to the -fmodules compiler flag), I get lots of errors like the following. Do you have any idea what's going wrong?

I don't see any obvious way that this patch could be responsible for that failure, unless it's something like a pre-existing use of uninitialized memory or a use-after-free and this patch is just changing the happenstance behavior.

I can't minimize the example beyond "all of libc++" because Modules prevents creduce from doing anything meaningful. And unfortunately whatever's going wrong seems to be specifically about Modules.

It'd likely be useful to know what the Replacement type is, if you can observe this crash in a debugger. I think it's possible for a type that was canonical prior to serialization and deserialization to be non-canonical afterwards (perhaps because we picked a different declaration of the type to be the first one or merged types in a different order or something like that). It may be that we just need to map to a canonical type in some suitable place.

Apply @rsmith's suggested approach.

I don't see any obvious way that this patch could be responsible for that failure, unless it's something like a pre-existing use of uninitialized memory or a use-after-free and this patch is just changing the happenstance behavior.

Well, it might more likely be the fault of D119184 rather than this one per se. D119184 is doing things with Context.VoidTy that weren't there before; could any of that new code be the culprit?

Quuxplusone edited the summary of this revision. (Show Details)Feb 16 2022, 12:05 PM

Apply @rsmith's suggested approach.

Thanks, the structure of the change looks good to me.

Looking through the test changes, I'm a bit torn by the value proposition of this note. Some of the errors were already pointing at the return type itself, and others were pointing at the return statement. I can see value in making sure that we mention both when diagnosing return type deduction errors, but pointing out the return type twice and never mentioning the return statement seems a bit noisy. I don't think I have any great suggestions here, though; there's not really a great way we can determine which of the two a diagnostic is pointing at so we can point the note at the other one.

The cases that I found in the tests that seem particularly bad today:

template<typename T> concept Never = false;
void g();
// error: cannot form a reference to 'void'
auto &f() {
    // ...
    return g();
}
// error: deduced type 'void' does not satisfy 'Never'
Never auto h() {
    // ...
    return g();
}

... would both benefit from a note saying "return type deduced from returned expression of type 'void' here" or something like that, and aren't really helped much by this change. But I think noting *both* the return statement and the return type would be too much (then we guarantee we always have at least one noisy note). :(

What do you think? This seems on balance to be near the edge between a useful note and a noisy note, though in many of the specific cases it's pretty clearly on one side or the other.

I don't see any obvious way that this patch could be responsible for that failure, unless it's something like a pre-existing use of uninitialized memory or a use-after-free and this patch is just changing the happenstance behavior.

Well, it might more likely be the fault of D119184 rather than this one per se. D119184 is doing things with Context.VoidTy that weren't there before; could any of that new code be the culprit?

That seems more likely, but nothing in that patch is jumping out at me. Especially given that it only affects the behavior of return type deduction in a function with either no return statements or only return;, it's pretty weird that libc++ would even exercise the patch.

clang/test/SemaCXX/deduced-return-void.cpp
153–155

This seems like an unfortunate case: both the error and the note point at the return type and neither points at the return statement.

clang/test/SemaTemplate/concepts.cpp
176–178

This is an unfortunate case: the existing error message already points at the C auto constraint, so we now have an error and a note both pointing at the return type, and nothing pointing to the return statement itself.

Rebased after landing D119184. I'm no longer necessarily trying to land this note (it's not directly relevant to my interests anymore), but I'd like to show its CI as green before I abandon it. @rsmith would you like me to do something other than abandon this?

Herald added a project: Restricted Project. · View Herald TranscriptMar 4 2022, 11:57 AM