Skip to content

[Clang] Do not create dependent CallExpr having UnresolvedLookupExpr inside non-dependent context #124609

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

TilakChad
Copy link
Contributor

@TilakChad TilakChad commented Jan 27, 2025

The UnresolvedLookupExpr doesn't get looked up and resolved again while it is inside the non-dependent context. It propagates into the codegen phase, causing the assertion failure.

We attempt to determine if the current context is dependent before moving on with the substitution introduced in the 20a0567.

This fixes #122892.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Jan 27, 2025
@llvmbot
Copy link
Member

llvmbot commented Jan 27, 2025

@llvm/pr-subscribers-clang

Author: TilakChad (TilakChad)

Changes

The UnresolvedLookupExpr doesn't get looked up and resolved again while it is in the non-dependent context. It propagates into the codegen phase and causing the assertion failure.

We attempt to determine if the enclosing function is templated before moving on with the substitution introduced in the 20a0567.

This fixes #122892.


Full diff: https://github.com/llvm/llvm-project/pull/124609.diff

2 Files Affected:

  • (modified) clang/lib/Sema/SemaOverload.cpp (+9-1)
  • (modified) clang/test/SemaCXX/deduced-return-type-cxx14.cpp (+42)
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 6ae9c51c06b315..b6cb358eb71c8b 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -14228,9 +14228,17 @@ ExprResult Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn,
     const FunctionDecl *FDecl = Best->Function;
     if (FDecl && FDecl->isTemplateInstantiation() &&
         FDecl->getReturnType()->isUndeducedType()) {
+
+      // UnresolvedLookupExpr will not be resolved again inside non-dependent
+      // function (i.e non-templated function in this case).
+      const FunctionDecl *EnclosingFn = getCurFunctionDecl();
+      const bool Resolvable =
+          EnclosingFn && EnclosingFn->getTemplatedKind() ==
+                             FunctionDecl::TemplatedKind::TK_FunctionTemplate;
+
       if (const auto *TP =
               FDecl->getTemplateInstantiationPattern(/*ForDefinition=*/false);
-          TP && TP->willHaveBody()) {
+          TP && TP->willHaveBody() && Resolvable) {
         return CallExpr::Create(Context, Fn, Args, Context.DependentTy,
                                 VK_PRValue, RParenLoc, CurFPFeatureOverrides());
       }
diff --git a/clang/test/SemaCXX/deduced-return-type-cxx14.cpp b/clang/test/SemaCXX/deduced-return-type-cxx14.cpp
index c33e07088ba32f..aa62c4a57a6366 100644
--- a/clang/test/SemaCXX/deduced-return-type-cxx14.cpp
+++ b/clang/test/SemaCXX/deduced-return-type-cxx14.cpp
@@ -703,6 +703,48 @@ auto f(auto x) { // cxx14-error {{'auto' not allowed in function prototype}}
   return f(1) + 1;
 }
 
+namespace GH122892 {
+  struct NonTemplate {
+    void caller() {
+        c1(int{}); // since-cxx20-error {{cannot be used before it is defined}}
+        c2(int{}); // since-cxx14-error {{cannot be used before it is defined}}
+    }
+
+    static auto c1(auto x) { // since-cxx20-note {{declared here}} // cxx14-error {{'auto' not allowed in function prototype}}
+    }
+
+    template <typename T>
+    static auto c2(T x) { // since-cxx14-note {{declared here}}
+        return x;
+    }
+  };
+
+  struct FunctionTemplateSpecialized {
+    template <typename T>
+    void specialized(){}
+
+    template <>
+    void specialized<int>() {
+      c1(int{}); // since-cxx20-error {{cannot be used before it is defined}}
+      c2(int{}); // since-cxx14-error {{cannot be used before it is defined}}
+    }
+
+    static auto c1(auto x) { // since-cxx20-note {{declared here}} // cxx14-error {{'auto' not allowed in function prototype}}
+    }
+
+    template <typename T>
+    static auto c2(T x) { // since-cxx14-note {{declared here}}
+        return x;
+    }
+  };
+
+  struct MemberInit {
+    int x1 = c1(int{}); // since-cxx20-error {{cannot be used before it is defined}}
+
+    static auto c1(auto x) { return x; } // since-cxx20-note {{declared here}} // cxx14-error {{'auto' not allowed in function prototype}}
+  };
+
+}
 }
 
 #if __cplusplus >= 202002L

@cor3ntin cor3ntin requested a review from erichkeane January 27, 2025 20:40
@erichkeane
Copy link
Collaborator

This doesn't quite seem right to me... First, I would expect us to check the declaration context here rather than whether it is in a function template.

Second, I find myself thinking the UnresolvedLookupExpr in a non-dependent context is the actual problem, and we need to figure out how we got there, as that is the big problem.

@TilakChad
Copy link
Contributor Author

Yeah, it seems more appropriate to check for the dependence of current DeclContext.

As for the second part, it seems to happen only when a non-dependent function calls the later defined overloaded member function.

Should it compile (I guess not) or be diagnosed before reaching that point?

@TilakChad
Copy link
Contributor Author

TilakChad commented Mar 13, 2025

Hi @erichkeane
Is this resolution still not good enough? I should probably close the PR if thats the case.

@erichkeane
Copy link
Collaborator

Hi @erichkeane Is this resolution still not good enough? I should probably close the PR if thats the case.

I apparently didn't see your last change since your last comment. Looking now.

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have minor suggestions, and am not quite comfortable with this yet, so please improve the commit message and make the change I requested (re Resolvable), and I'll revisit this afterwards.

// As there'll be no attempt to resolve UnresolvedLookupExpr again inside
// non-dependent context, skip considering it as type-dependent.
const DeclContext *DC = CurContext;
const bool Resolvable = DC && DC->isDependentContext();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolvable isn't a good name here, it sorta implies the opposite of what we mean, but not to the point of NotResolvable being a good idea. I think just putting && CurContext->isDependentContext() below is actually more readable. Also, you don't really have to check the DeclContext, having a null one I think requires that you be outside of a TranslationUnitDecl, which isn't possible.

I see that we are just going through FinishOverloadedCallExpr in the event that we are going to instantiate this again, which I guess makes sense, since this is creating a call with a DependentTy, but this comment doesn't make it clear what i I think I'd be happy with a better commit message explaining the whole situation and why this works. I've debugged a while and think I have a good hold on it though, so just a better description I think would help

@TilakChad TilakChad force-pushed the clang-18-regression-unexpected-placeholder branch from 2204b25 to c865ba5 Compare March 13, 2025 19:14
@TilakChad
Copy link
Contributor Author

I've incorporated your feedback and updated the PR.

Let me know if I have to change anything.
Thanks for taking your time to review this PR.

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to give a conditional approval. My comfort level is reasonable right now, but I want time to think about it. So if I haven't responded by ~EOD Monday (or if no one else did other comments), feel free to merge.

@TilakChad TilakChad changed the title [Clang] Fixed UnresolvedLookupExpr propagating into the codegen phase [Clang] Do not create dependent CallExpr having UnresolvedLookupExpr inside non-dependent context Mar 18, 2025
@Sirraide
Copy link
Member

This is still missing a release note, but I can merge it for you once you add one.

@TilakChad
Copy link
Contributor Author

This is still missing a release note, but I can merge it for you once you add one.

@Sirraide
I've updated the release notes.
Please merge it on my behalf. And thanks.

@Sirraide
Copy link
Member

I’ll do that once CI is done

Copy link
Member

@Sirraide Sirraide left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some minor fixes

@Sirraide
Copy link
Member

Huh, I wonder why CI’s upset all of a sudden

@mizvekov mizvekov merged commit 523cf65 into llvm:main Mar 20, 2025
12 checks passed
@llvm-ci
Copy link
Collaborator

llvm-ci commented Mar 20, 2025

LLVM Buildbot has detected a new failure on builder openmp-offload-amdgpu-runtime running on omp-vega20-0 while building clang at step 7 "Add check check-offload".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/30/builds/18066

Here is the relevant piece of the build log for the reference
Step 7 (Add check check-offload) failure: test (failure)
******************** TEST 'libomptarget :: amdgcn-amd-amdhsa :: offloading/gpupgo/pgo1.c' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./bin/clang -fopenmp    -I /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test -I /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src  -nogpulib -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib  -fopenmp-targets=amdgcn-amd-amdhsa /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo1.c -o /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo1.c.tmp /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib/libomptarget.devicertl.a -fcreate-profile      -Xarch_device -fprofile-generate
# executed command: /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./bin/clang -fopenmp -I /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test -I /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -nogpulib -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib -fopenmp-targets=amdgcn-amd-amdhsa /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo1.c -o /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo1.c.tmp /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib/libomptarget.devicertl.a -fcreate-profile -Xarch_device -fprofile-generate
# note: command had no output on stdout or stderr
# RUN: at line 3
env LLVM_PROFILE_FILE=pgo1.c.llvm.profraw      /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo1.c.tmp 2>&1
# executed command: env LLVM_PROFILE_FILE=pgo1.c.llvm.profraw /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo1.c.tmp
# note: command had no output on stdout or stderr
# RUN: at line 5
llvm-profdata show --all-functions --counts      amdgcn-amd-amdhsa.pgo1.c.llvm.profraw |      /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./bin/FileCheck /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo1.c --check-prefix="LLVM-PGO"
# executed command: llvm-profdata show --all-functions --counts amdgcn-amd-amdhsa.pgo1.c.llvm.profraw
# note: command had no output on stdout or stderr
# executed command: /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./bin/FileCheck /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo1.c --check-prefix=LLVM-PGO
# note: command had no output on stdout or stderr
# RUN: at line 9
/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./bin/clang -fopenmp    -I /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test -I /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src  -nogpulib -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib  -fopenmp-targets=amdgcn-amd-amdhsa /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo1.c -o /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo1.c.tmp /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib/libomptarget.devicertl.a -fcreate-profile      -Xarch_device -fprofile-instr-generate
# executed command: /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./bin/clang -fopenmp -I /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test -I /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -nogpulib -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib -fopenmp-targets=amdgcn-amd-amdhsa /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo1.c -o /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo1.c.tmp /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib/libomptarget.devicertl.a -fcreate-profile -Xarch_device -fprofile-instr-generate
# note: command had no output on stdout or stderr
# RUN: at line 11
env LLVM_PROFILE_FILE=pgo1.c.clang.profraw      /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo1.c.tmp 2>&1
# executed command: env LLVM_PROFILE_FILE=pgo1.c.clang.profraw /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo1.c.tmp
# note: command had no output on stdout or stderr
# RUN: at line 13
llvm-profdata show --all-functions --counts      amdgcn-amd-amdhsa.pgo1.c.clang.profraw |      /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./bin/FileCheck /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo1.c --check-prefix="CLANG-PGO"
# executed command: llvm-profdata show --all-functions --counts amdgcn-amd-amdhsa.pgo1.c.clang.profraw
# note: command had no output on stdout or stderr
# executed command: /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./bin/FileCheck /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo1.c --check-prefix=CLANG-PGO
# .---command stderr------------
# | /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo1.c:63:15: error: CLANG-PGO: expected string not found in input
# | // CLANG-PGO: Block counts: [11, 20]
# |               ^
# | <stdin>:5:19: note: scanning from here
# |  Function count: 0
# |                   ^
# | <stdin>:6:2: note: possible intended match here
# |  Block counts: [13, 20]
# |  ^
# | 
# | Input file: <stdin>
# | Check file: /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo1.c
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
...

Copy link
Collaborator

@shafik shafik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a few concerns w/ this PR.

Number one we do not cover the original test case which crashes, as far as I can tell none of the tests crash before this fix. This is large oversight.

It looks like currently gcc is the only one that rejects this code as we will now do as well: https://godbolt.org/z/Pqnb4eq6K

I don't see anyone justifying this change in behavior w/ wording from the standard. I would have expected to see such wording references in the summary and also added to a comment in the code itself to document the justification for this change in behavior for future readers of the code.

CC @erichkeane @mizvekov @AaronBallman

@mizvekov
Copy link
Contributor

mizvekov commented Mar 20, 2025

Clang does crash with one of the included test cases, if you try to actually use the function: https://godbolt.org/z/e6e6Ehjoj

My reading is that using a static member function with placeholder type before it has been deduced is ill-formed, per https://eel.is/c++draft/dcl.spec.auto#general-13

@shafik
Copy link
Collaborator

shafik commented Mar 21, 2025

Clang does crash with one of the included test cases, if you try to actually use the function: https://godbolt.org/z/e6e6Ehjoj

That is not a regression test, at minimum when fixing a bug we should include a test that directly reproduces the bug to ensure that we don't reintroduce the bug again in the future in some subtle different way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Clang 18 regression on C++20 mode: Unexpected placeholder builtin type! crash
8 participants